{
  "formatVersion": 1,
  "name": "tickets-node-app",
  "files": {
    "README.md": "# Atmosphere Tickets Node App Example\n\nMinimal closed-beta event-app starter for Atmosphere Tickets.\n\nIt shows:\n\n- using ATM's `@atmosphere-money/app-node` beta package for ticket calls\n- reading ticket availability\n- creating a paid ticket hold that returns an ATM checkout URL\n- releasing a hold before checkout completes\n- claiming a limited free ticket without checkout\n- listing a buyer's issued tickets\n- verifying an opaque scan token before check-in\n- receiving signed ATM/Tickets events by HTTP webhook\n- receiving the same event shape through optional XRPC receiver callbacks\n- checking in an opaque ticket token\n\nTickets does not need a separate SDK package for beta. Use ATM's App Node SDK\nbecause holds, checkout, payment status, webhooks, and XRPC receiver helpers all\nshare ATM app auth. Keep app service-auth, buyer assertions, webhook secrets,\nscan tokens, and ticket fulfillment mutations on your server.\n\n## Run without ATM credentials\n\nRequires Node.js 22 or later and npm. Download the public starter from\nhttps://atmosphere.tickets/docs/tickets-node-app.zip; no repository access is\nneeded. The same sources are readable in\nhttps://atmosphere.tickets/docs/tickets-node-app.json.\n\n```sh\nnpm ci\nnpm run typecheck\nnpm run smoke\n```\n\nThe smoke uses mocked HTTP and signed fixtures. It needs no ATM account,\ncredentials, checkout or scanner. A successful smoke prints\n`Tickets example smoke check passed.`\n\nTo start the local server:\n\n```sh\ncp .env.example .env\nnpm run dev\ncurl http://localhost:8788/health\n```\n\nThe server listens on `http://localhost:8788`; `dev` loads `.env` using Node's\nnative environment-file support. Only `/health` works without ATM setup.\nThe ticket routes require real test configuration and an auth adapter.\n\n## CI checks\n\nUse these before copying changes into a real app:\n\n```sh\nnpm run typecheck\nnpm run smoke\n```\n\n`typecheck` keeps the TypeScript helper code honest. `smoke` exercises\nthe ATM/Tickets webhook signature format without needing live checkout state.\n\nRoutes:\n\n- `GET /health` returns a starter-kit health check.\n- `GET /tickets/availability` reads availability for `EVENT_URI`.\n- `POST /tickets/hold` creates a paid hold and returns `url`.\n- `POST /tickets/release-hold` releases a held ticket allocation.\n- `POST /tickets/free-claim` claims a limited free ticket.\n- `GET /tickets/buyer` lists app-scoped buyer tickets.\n- `POST /tickets/verify` verifies an opaque token without checking it in.\n- `POST /tickets/check-in` checks in an opaque token.\n- `POST /webhooks/atm` verifies signed ATM/Tickets events.\n- `POST /xrpc/money.atmosphere.event.receive` shows the optional XRPC receiver shape.\n\n## Sandbox walkthrough\n\nBefore making real test calls, replace the static `APP_SERVICE_AUTH` fixture\nwith a provider that mints a fresh token for each exact method and the target\nbroker audience. Replace the local XRPC token-comparison verifier with actual\nATM JWT signature/issuer/audience/method/replay verification, or use HTTP\nwebhooks. Add caller authentication, per-order persistent idempotency keys,\nprivate hold/status-token storage and durable callback processing in your app.\nThese fixture routes are not a production backend.\n\n1. Register the app in ATM test mode and enable Tickets.\n2. Create or choose a test event and types with event/type caps, and check-in list.\n3. Copy the test webhook signing secret into `ATM_WEBHOOK_SECRET`.\n4. Expose `http://localhost:8788/webhooks/atm` through a tunnel and save it as the test webhook URL.\n5. Send a dashboard test event and confirm this server verifies it (add durable deduplication in your adapter).\n6. Call `GET /tickets/availability`.\n7. Call `POST /tickets/hold`; open the returned `url`.\n8. Call `POST /tickets/release-hold` for a cancelled or abandoned hold.\n9. Call `POST /tickets/free-claim` for a free limited type.\n10. Call `GET /tickets/buyer` to show issued tickets to the buyer.\n11. Call `POST /tickets/verify`, then `POST /tickets/check-in` with an opaque scan token.\n12. Redrive a failed test event and confirm your app deduplicates by delivery id.\n\nWebhook signatures use `Atm-Signature: t=<unix>,v1=<hex>` and\n`Atm-Delivery-Id`. The signed payload is\n`${timestamp}.${deliveryId}.${rawBody}`.\n\n## Package notes\n\nThe example depends on the published ATM App Node SDK beta:\n\n```json\n\"@atmosphere-money/app-node\": \"0.0.0-beta.5\"\n```\n\nWhen testing unpublished SDK changes locally, you can temporarily replace that\nwith a separately built checkout of that package. Do not confuse ATM's\nunreleased SDK source with a published release.\n\n## Current contract and fixture limits\n\nThe published beta.5 SDK uses `items: [{ ticketTypeId, quantity }]` for both\nholds and free claims. Fresh free claims also require `customerEmail` and a\nbuyer assertion scoped to `tickets.atmosphere.claimFreeTicket`; paid assertions\nuse `money.atmosphere.payment.assertPayer`. Mint a separate fresh app token with\nthe exact method scope for each request. The static env token is a local fixture,\nnot a production token provider.\n\nBeta.5 still sends the older `buyerDid` list selector, so this example uses direct\nXRPC with `buyer` for buyer-ticket reads. Use current raw contracts for newer\norganizer authority fields, eventCode, payerBinding, releaseFreeTicket and\nlifecycle event types missing from this published SDK. Keep hold `statusToken`\nserver-side in a real adapter and reconcile final issued-ticket state; the\nbrowser checkout token is not the app status capability.\n\nBeta.5 types/sends paid type catalog refs as `productRef`/`priceRef`, while ATM\nrequires `product` (omit `defaultPrice` when supplying Product). Create and\nupdate paid types with authenticated raw XRPC until the coordinated SDK release.\nWithout `PAID_BUYER_ASSERTION_JWT`, the paid fixture omits both buyer fields for\nguest checkout; signed-in paid fixtures require the correct payer assertion.\n\nThe smoke uses mocked HTTP and signed current-shaped callback fixtures. No\nlive payments, real service-auth, email or deployed scanner state are exercised.\nThe starter logs verified callbacks but has no durable delivery-processing store;\nadd atomic deduplication/recovery before fulfillment. Persist a new operation key\nper intentional order and reuse it only for retries of that same order. Its\nfixture keys and ticket ids are not a production order system.\n",
    ".env.example": "# Example only: use the broker origin from your target ATM test app setup.\nATM_BASE_URL=https://checkout.atmosphere.money\nAPP_SERVICE_AUTH=replace-with-short-lived-app-service-auth\nATM_WEBHOOK_SECRET=replace-with-test-webhook-secret\nATM_EVENT_SERVICE_AUTH=optional-local-xrpc-receiver-token\nAPP_DID=did:plc:ticketapp\nAPP_BASE_URL=http://localhost:8788\nEVENT_URI=at://did:plc:organizer/community.lexicon.calendar.event/demo\nTICKET_TYPE_ID=ttyp_demo\nFREE_TICKET_TYPE_ID=ttyp_free_demo\nBUYER_DID=did:plc:buyer\n# Paid assertion lxm: money.atmosphere.payment.assertPayer\nPAID_BUYER_ASSERTION_JWT=replace-with-short-lived-assert-payer-jwt\n# Free assertion lxm: tickets.atmosphere.claimFreeTicket\nFREE_BUYER_ASSERTION_JWT=replace-with-short-lived-free-claim-jwt\nCUSTOMER_EMAIL=buyer@example.com\nHOLD_ID=thold_demo\nCHECK_IN_LIST_ID=list_demo\nTICKET_TOKEN=opaque_scan_token_from_qr_or_wallet_pass\n",
    "package.json": "{\n  \"name\": \"atmosphere-tickets-node-app-example\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"node --env-file=.env --import tsx server.ts\",\n    \"typecheck\": \"tsc --noEmit\",\n    \"smoke\": \"node --import tsx smoke.ts\"\n  },\n  \"dependencies\": {\n    \"@atmosphere-money/app-node\": \"0.0.0-beta.5\",\n    \"tsx\": \"^4.20.3\"\n  },\n  \"devDependencies\": {\n    \"@types/node\": \"^22.10.2\",\n    \"typescript\": \"^5.7.2\"\n  },\n  \"overrides\": {\n    \"esbuild\": \"0.28.1\"\n  }\n}\n",
    "package-lock.json": "{\n  \"name\": \"atmosphere-tickets-node-app-example\",\n  \"lockfileVersion\": 3,\n  \"requires\": true,\n  \"packages\": {\n    \"\": {\n      \"name\": \"atmosphere-tickets-node-app-example\",\n      \"dependencies\": {\n        \"@atmosphere-money/app-node\": \"0.0.0-beta.5\",\n        \"tsx\": \"^4.20.3\"\n      },\n      \"devDependencies\": {\n        \"@types/node\": \"^22.10.2\",\n        \"typescript\": \"^5.7.2\"\n      }\n    },\n    \"node_modules/@atmosphere-money/app-node\": {\n      \"version\": \"0.0.0-beta.5\",\n      \"resolved\": \"https://registry.npmjs.org/@atmosphere-money/app-node/-/app-node-0.0.0-beta.5.tgz\",\n      \"integrity\": \"sha512-K3bm1or9qyccMCuN8b86+AIEdv9bB7Rd2BQqA8mvlAkimS376Mw69gemQirvbZo4JdJufOyl3Nti8jBLYUgQnQ==\",\n      \"license\": \"MIT\",\n      \"engines\": {\n        \"node\": \">=22.0.0\"\n      }\n    },\n    \"node_modules/@esbuild/aix-ppc64\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz\",\n      \"integrity\": \"sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==\",\n      \"cpu\": [\n        \"ppc64\"\n      ],\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"aix\"\n      ],\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@esbuild/android-arm\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz\",\n      \"integrity\": \"sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==\",\n      \"cpu\": [\n        \"arm\"\n      ],\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"android\"\n      ],\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@esbuild/android-arm64\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz\",\n      \"integrity\": \"sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==\",\n      \"cpu\": [\n        \"arm64\"\n      ],\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"android\"\n      ],\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@esbuild/android-x64\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz\",\n      \"integrity\": \"sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==\",\n      \"cpu\": [\n        \"x64\"\n      ],\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"android\"\n      ],\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@esbuild/darwin-arm64\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz\",\n      \"integrity\": \"sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==\",\n      \"cpu\": [\n        \"arm64\"\n      ],\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"darwin\"\n      ],\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@esbuild/darwin-x64\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz\",\n      \"integrity\": \"sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==\",\n      \"cpu\": [\n        \"x64\"\n      ],\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"darwin\"\n      ],\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@esbuild/freebsd-arm64\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz\",\n      \"integrity\": \"sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==\",\n      \"cpu\": [\n        \"arm64\"\n      ],\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"freebsd\"\n      ],\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@esbuild/freebsd-x64\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz\",\n      \"integrity\": \"sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==\",\n      \"cpu\": [\n        \"x64\"\n      ],\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"freebsd\"\n      ],\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@esbuild/linux-arm\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz\",\n      \"integrity\": \"sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==\",\n      \"cpu\": [\n        \"arm\"\n      ],\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"linux\"\n      ],\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@esbuild/linux-arm64\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz\",\n      \"integrity\": \"sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==\",\n      \"cpu\": [\n        \"arm64\"\n      ],\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"linux\"\n      ],\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@esbuild/linux-ia32\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz\",\n      \"integrity\": \"sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==\",\n      \"cpu\": [\n        \"ia32\"\n      ],\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"linux\"\n      ],\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@esbuild/linux-loong64\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz\",\n      \"integrity\": \"sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==\",\n      \"cpu\": [\n        \"loong64\"\n      ],\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"linux\"\n      ],\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@esbuild/linux-mips64el\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz\",\n      \"integrity\": \"sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==\",\n      \"cpu\": [\n        \"mips64el\"\n      ],\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"linux\"\n      ],\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@esbuild/linux-ppc64\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz\",\n      \"integrity\": \"sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==\",\n      \"cpu\": [\n        \"ppc64\"\n      ],\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"linux\"\n      ],\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@esbuild/linux-riscv64\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz\",\n      \"integrity\": \"sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==\",\n      \"cpu\": [\n        \"riscv64\"\n      ],\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"linux\"\n      ],\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@esbuild/linux-s390x\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz\",\n      \"integrity\": \"sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==\",\n      \"cpu\": [\n        \"s390x\"\n      ],\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"linux\"\n      ],\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@esbuild/linux-x64\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz\",\n      \"integrity\": \"sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==\",\n      \"cpu\": [\n        \"x64\"\n      ],\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"linux\"\n      ],\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@esbuild/netbsd-arm64\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz\",\n      \"integrity\": \"sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==\",\n      \"cpu\": [\n        \"arm64\"\n      ],\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"netbsd\"\n      ],\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@esbuild/netbsd-x64\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz\",\n      \"integrity\": \"sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==\",\n      \"cpu\": [\n        \"x64\"\n      ],\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"netbsd\"\n      ],\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@esbuild/openbsd-arm64\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz\",\n      \"integrity\": \"sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==\",\n      \"cpu\": [\n        \"arm64\"\n      ],\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"openbsd\"\n      ],\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@esbuild/openbsd-x64\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz\",\n      \"integrity\": \"sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==\",\n      \"cpu\": [\n        \"x64\"\n      ],\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"openbsd\"\n      ],\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@esbuild/openharmony-arm64\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz\",\n      \"integrity\": \"sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==\",\n      \"cpu\": [\n        \"arm64\"\n      ],\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"openharmony\"\n      ],\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@esbuild/sunos-x64\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz\",\n      \"integrity\": \"sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==\",\n      \"cpu\": [\n        \"x64\"\n      ],\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"sunos\"\n      ],\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@esbuild/win32-arm64\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz\",\n      \"integrity\": \"sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==\",\n      \"cpu\": [\n        \"arm64\"\n      ],\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"win32\"\n      ],\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@esbuild/win32-ia32\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz\",\n      \"integrity\": \"sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==\",\n      \"cpu\": [\n        \"ia32\"\n      ],\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"win32\"\n      ],\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@esbuild/win32-x64\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz\",\n      \"integrity\": \"sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==\",\n      \"cpu\": [\n        \"x64\"\n      ],\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"win32\"\n      ],\n      \"engines\": {\n        \"node\": \">=18\"\n      }\n    },\n    \"node_modules/@types/node\": {\n      \"version\": \"22.19.19\",\n      \"dev\": true,\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"undici-types\": \"~6.21.0\"\n      }\n    },\n    \"node_modules/esbuild\": {\n      \"version\": \"0.28.1\",\n      \"resolved\": \"https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz\",\n      \"integrity\": \"sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==\",\n      \"hasInstallScript\": true,\n      \"license\": \"MIT\",\n      \"bin\": {\n        \"esbuild\": \"bin/esbuild\"\n      },\n      \"engines\": {\n        \"node\": \">=18\"\n      },\n      \"optionalDependencies\": {\n        \"@esbuild/aix-ppc64\": \"0.28.1\",\n        \"@esbuild/android-arm\": \"0.28.1\",\n        \"@esbuild/android-arm64\": \"0.28.1\",\n        \"@esbuild/android-x64\": \"0.28.1\",\n        \"@esbuild/darwin-arm64\": \"0.28.1\",\n        \"@esbuild/darwin-x64\": \"0.28.1\",\n        \"@esbuild/freebsd-arm64\": \"0.28.1\",\n        \"@esbuild/freebsd-x64\": \"0.28.1\",\n        \"@esbuild/linux-arm\": \"0.28.1\",\n        \"@esbuild/linux-arm64\": \"0.28.1\",\n        \"@esbuild/linux-ia32\": \"0.28.1\",\n        \"@esbuild/linux-loong64\": \"0.28.1\",\n        \"@esbuild/linux-mips64el\": \"0.28.1\",\n        \"@esbuild/linux-ppc64\": \"0.28.1\",\n        \"@esbuild/linux-riscv64\": \"0.28.1\",\n        \"@esbuild/linux-s390x\": \"0.28.1\",\n        \"@esbuild/linux-x64\": \"0.28.1\",\n        \"@esbuild/netbsd-arm64\": \"0.28.1\",\n        \"@esbuild/netbsd-x64\": \"0.28.1\",\n        \"@esbuild/openbsd-arm64\": \"0.28.1\",\n        \"@esbuild/openbsd-x64\": \"0.28.1\",\n        \"@esbuild/openharmony-arm64\": \"0.28.1\",\n        \"@esbuild/sunos-x64\": \"0.28.1\",\n        \"@esbuild/win32-arm64\": \"0.28.1\",\n        \"@esbuild/win32-ia32\": \"0.28.1\",\n        \"@esbuild/win32-x64\": \"0.28.1\"\n      }\n    },\n    \"node_modules/fsevents\": {\n      \"version\": \"2.3.3\",\n      \"resolved\": \"https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz\",\n      \"integrity\": \"sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==\",\n      \"hasInstallScript\": true,\n      \"license\": \"MIT\",\n      \"optional\": true,\n      \"os\": [\n        \"darwin\"\n      ],\n      \"engines\": {\n        \"node\": \"^8.16.0 || ^10.6.0 || >=11.0.0\"\n      }\n    },\n    \"node_modules/tsx\": {\n      \"version\": \"4.22.4\",\n      \"license\": \"MIT\",\n      \"dependencies\": {\n        \"esbuild\": \"~0.28.0\"\n      },\n      \"bin\": {\n        \"tsx\": \"dist/cli.mjs\"\n      },\n      \"engines\": {\n        \"node\": \">=18.0.0\"\n      },\n      \"optionalDependencies\": {\n        \"fsevents\": \"~2.3.3\"\n      }\n    },\n    \"node_modules/typescript\": {\n      \"version\": \"5.9.3\",\n      \"dev\": true,\n      \"license\": \"Apache-2.0\",\n      \"bin\": {\n        \"tsc\": \"bin/tsc\",\n        \"tsserver\": \"bin/tsserver\"\n      },\n      \"engines\": {\n        \"node\": \">=14.17\"\n      }\n    },\n    \"node_modules/undici-types\": {\n      \"version\": \"6.21.0\",\n      \"dev\": true,\n      \"license\": \"MIT\"\n    }\n  }\n}\n",
    "server.ts": "import { createServer } from \"node:http\";\nimport {\n  ATM_BROKER_DID,\n  ATM_EVENT_RECEIVE_NSID,\n  constructAtmWebhookEvent,\n  constructAtmXrpcReceiverEvent,\n  createAtmAppClient,\n  type AtmVerifiedServiceAuthClaims,\n} from \"@atmosphere-money/app-node\";\n\nconst env = {\n  atmBaseUrl: process.env.ATM_BASE_URL ?? \"https://checkout.atmosphere.money\",\n  appServiceAuth: process.env.APP_SERVICE_AUTH ?? \"\",\n  eventServiceAuth: process.env.ATM_EVENT_SERVICE_AUTH ?? \"\",\n  webhookSecret: process.env.ATM_WEBHOOK_SECRET ?? \"\",\n  appDid: process.env.APP_DID ?? \"did:plc:ticketapp\",\n  appBaseUrl: process.env.APP_BASE_URL ?? \"http://localhost:8788\",\n  eventUri:\n    process.env.EVENT_URI ??\n    \"at://did:plc:organizer/community.lexicon.calendar.event/demo\",\n  ticketTypeId: process.env.TICKET_TYPE_ID ?? \"ttyp_demo\",\n  freeTicketTypeId: process.env.FREE_TICKET_TYPE_ID ?? \"ttyp_free_demo\",\n  buyerDid: process.env.BUYER_DID ?? \"did:plc:buyer\",\n  paidBuyerAssertionJwt: process.env.PAID_BUYER_ASSERTION_JWT ?? \"\",\n  freeBuyerAssertionJwt: process.env.FREE_BUYER_ASSERTION_JWT ?? \"\",\n  customerEmail: process.env.CUSTOMER_EMAIL ?? \"buyer@example.com\",\n  holdId: process.env.HOLD_ID ?? \"thold_demo\",\n  checkInListId: process.env.CHECK_IN_LIST_ID ?? \"list_demo\",\n  ticketToken:\n    process.env.TICKET_TOKEN ?? \"opaque_scan_token_from_qr_or_wallet_pass\",\n};\n\nconst atm = createAtmAppClient({\n  brokerUrl: env.atmBaseUrl,\n  getServiceAuthToken: ({ lxm }) => {\n    if (!env.appServiceAuth) {\n      throw new Error(\n        `APP_SERVICE_AUTH is required for ${lxm}; mint a fresh token per request in production.`\n      );\n    }\n    return env.appServiceAuth;\n  },\n});\n\nfunction json(status: number, body: unknown) {\n  return new Response(JSON.stringify(body, null, 2), {\n    status,\n    headers: { \"content-type\": \"application/json\" },\n  });\n}\n\nasync function readBody(request: Request) {\n  return await request.text();\n}\n\nasync function getAvailability() {\n  return await atm.getTicketAvailability({\n    environment: \"test\",\n    eventUri: env.eventUri,\n  });\n}\n\nasync function createHold() {\n  return await atm.createTicketHold({\n    environment: \"test\",\n    eventUri: env.eventUri,\n    ...(env.paidBuyerAssertionJwt ? {\n      buyerDid: env.buyerDid,\n      buyerAssertionJwt: env.paidBuyerAssertionJwt,\n    } : {}),\n    items: [{ ticketTypeId: env.ticketTypeId, quantity: 1 }],\n    returnUrl: `${env.appBaseUrl}/tickets/return`,\n    cancelUrl: `${env.appBaseUrl}/events/demo`,\n    idempotencyKey: `hold:${env.eventUri}:${env.buyerDid}:${env.ticketTypeId}:1`,\n  });\n}\n\nasync function releaseHold() {\n  return await atm.releaseTicketHold({\n    environment: \"test\",\n    holdId: env.holdId,\n    reason: \"buyer_cancelled\",\n  });\n}\n\nasync function claimFreeTicket() {\n  return await atm.claimFreeTicket({\n    environment: \"test\",\n    eventUri: env.eventUri,\n    items: [{ ticketTypeId: env.freeTicketTypeId, quantity: 1 }],\n    buyerDid: env.buyerDid,\n    buyerAssertionJwt: env.freeBuyerAssertionJwt,\n    customerEmail: env.customerEmail,\n    idempotencyKey: `claim:${env.eventUri}:${env.buyerDid}:${env.freeTicketTypeId}`,\n  });\n}\n\nasync function listBuyerTickets() {\n  // Published beta.5 still sends buyerDid; ATM's current wire uses buyer.\n  // Use the canonical query until a coordinated SDK release updates selectors.\n  const query = new URLSearchParams({\n    environment: \"test\",\n    buyer: env.buyerDid,\n    limit: \"10\",\n  });\n  if (!env.appServiceAuth) throw new Error(\"APP_SERVICE_AUTH is required for tickets.atmosphere.listBuyerTickets\");\n  const response = await fetch(`${env.atmBaseUrl}/xrpc/tickets.atmosphere.listBuyerTickets?${query}`, {\n    headers: { authorization: `Bearer ${env.appServiceAuth}` },\n  });\n  if (!response.ok) throw new Error(`Buyer ticket read failed (${response.status})`);\n  return response.json();\n}\n\nasync function verifyTicket() {\n  return await atm.verifyTicket({\n    environment: \"test\",\n    ticketToken: env.ticketToken,\n  });\n}\n\nasync function checkInTicket() {\n  return await atm.checkInTicket({\n    environment: \"test\",\n    ticketToken: env.ticketToken,\n    checkInListId: env.checkInListId,\n  });\n}\n\nfunction verifyLocalEventServiceAuth(input: {\n  token: string;\n  expectedIss: string;\n  expectedAud: string;\n  expectedLxm: string;\n}): AtmVerifiedServiceAuthClaims {\n  if (!env.eventServiceAuth || input.token !== env.eventServiceAuth) {\n    throw new Error(\"Invalid local XRPC receiver token\");\n  }\n  return {\n    iss: input.expectedIss,\n    aud: input.expectedAud,\n    lxm: input.expectedLxm,\n    exp: Math.floor(Date.now() / 1000) + 60,\n    iat: Math.floor(Date.now() / 1000),\n    jti: \"local-ticket-demo-jti\",\n  };\n}\n\nasync function handle(request: Request) {\n  const url = new URL(request.url);\n  try {\n    if (request.method === \"GET\" && url.pathname === \"/health\") {\n      return json(200, { ok: true, service: \"tickets-node-app-example\" });\n    }\n    if (request.method === \"GET\" && url.pathname === \"/tickets/availability\") {\n      return json(200, await getAvailability());\n    }\n    if (request.method === \"POST\" && url.pathname === \"/tickets/hold\") {\n      return json(200, await createHold());\n    }\n    if (request.method === \"POST\" && url.pathname === \"/tickets/release-hold\") {\n      return json(200, await releaseHold());\n    }\n    if (request.method === \"POST\" && url.pathname === \"/tickets/free-claim\") {\n      return json(200, await claimFreeTicket());\n    }\n    if (request.method === \"GET\" && url.pathname === \"/tickets/buyer\") {\n      return json(200, await listBuyerTickets());\n    }\n    if (request.method === \"POST\" && url.pathname === \"/tickets/verify\") {\n      return json(200, await verifyTicket());\n    }\n    if (request.method === \"POST\" && url.pathname === \"/tickets/check-in\") {\n      return json(200, await checkInTicket());\n    }\n    if (request.method === \"POST\" && url.pathname === \"/webhooks/atm\") {\n      const rawBody = await readBody(request);\n      const event = constructAtmWebhookEvent({\n        rawBody,\n        secret: env.webhookSecret,\n        headers: {\n          signature: request.headers.get(\"atm-signature\"),\n          deliveryId: request.headers.get(\"atm-delivery-id\"),\n          event: request.headers.get(\"atm-event\"),\n          apiVersion: request.headers.get(\"atm-api-version\"),\n          environment: request.headers.get(\"atm-environment\"),\n        },\n      });\n      console.log(\"verified Tickets event\", event.type, event.id);\n      return json(200, { ok: true, deliveryId: event.id });\n    }\n    if (\n      request.method === \"POST\" &&\n      url.pathname === \"/xrpc/money.atmosphere.event.receive\"\n    ) {\n      const rawBody = await readBody(request);\n      const event = await constructAtmXrpcReceiverEvent({\n        rawBody,\n        appDid: env.appDid,\n        expectedIssuerDid: ATM_BROKER_DID,\n        expectedLxm: ATM_EVENT_RECEIVE_NSID,\n        headers: {\n          authorization: request.headers.get(\"authorization\"),\n          deliveryId: request.headers.get(\"atm-delivery-id\"),\n          event: request.headers.get(\"atm-event\"),\n          apiVersion: request.headers.get(\"atm-api-version\"),\n          environment: request.headers.get(\"atm-environment\"),\n        },\n        verifyServiceAuthJwt: verifyLocalEventServiceAuth,\n      });\n      console.log(\"received Tickets XRPC event\", event.type, event.id);\n      return json(200, { ok: true, deliveryId: event.id });\n    }\n    return json(404, { error: \"NotFound\" });\n  } catch (error) {\n    return json(500, {\n      error: \"ExampleError\",\n      message: error instanceof Error ? error.message : String(error),\n    });\n  }\n}\n\ncreateServer(async (request, response) => {\n  const body = await handle(\n    new Request(`http://localhost:8788${request.url}`, {\n      method: request.method,\n      headers: request.headers as HeadersInit,\n      body:\n        request.method === \"GET\" || request.method === \"HEAD\"\n          ? undefined\n          : request,\n      duplex: \"half\",\n    } as RequestInit)\n  );\n  const headers: Record<string, string> = {};\n  body.headers.forEach((value, key) => {\n    headers[key] = value;\n  });\n  response.writeHead(body.status, headers);\n  response.end(await body.text());\n}).listen(8788, \"127.0.0.1\", () => {\n  console.log(\"Tickets example server listening on http://localhost:8788\");\n});\n",
    "smoke.ts": "import {\n  createAtmAppClient,\n  constructTypedAtmWebhookEvent,\n  signAtmWebhookPayload,\n  verifyAtmWebhookSignature,\n} from \"@atmosphere-money/app-node\";\n\nconst secret = \"whsec_test_secret\";\nconst deliveryId = \"del_ticket_test\";\nconst timestamp = 1_770_000_000;\nconst rawBody = JSON.stringify({\n  id: deliveryId,\n  environment: \"test\",\n  type: \"tickets.issued\",\n  createdAt: new Date(timestamp * 1000).toISOString(),\n  apiVersion: \"2026-07\",\n  data: {\n    $type: \"money.atmosphere.event.defs#ticketsIssued\",\n    claimId: \"claim_demo\",\n    hold: { id: \"thold_demo\", eventId: \"tevt_demo\" },\n    tickets: [{ id: \"tix_demo\", eventId: \"tevt_demo\", ticketTypeId: \"ttyp_free_demo\", status: \"active\" }],\n  },\n});\n\nconst signature = signAtmWebhookPayload({\n  rawBody,\n  deliveryId,\n  secret,\n  timestamp,\n});\n\nif (\n  !verifyAtmWebhookSignature({\n    rawBody,\n    signature,\n    deliveryId,\n    secret,\n    now: timestamp,\n  })\n) {\n  throw new Error(\"Tickets signature smoke check failed\");\n}\n\nconst verifiedEvent = constructTypedAtmWebhookEvent({\n  rawBody,\n  secret,\n  expectedType: \"tickets.issued\",\n  now: timestamp,\n  headers: {\n    signature,\n    deliveryId,\n    event: \"tickets.issued\",\n    apiVersion: \"2026-07\",\n    environment: \"test\",\n  },\n});\n// beta.5's event types are older than the current required nested payload.\nif (verifiedEvent.data.tickets?.length !== 1 || (verifiedEvent.data.hold as { id?: string } | undefined)?.id !== \"thold_demo\") {\n  throw new Error(\"Tickets typed event smoke check failed\");\n}\n\nconst calls: Array<{ url: string; init?: RequestInit }> = [];\nconst originalFetch = globalThis.fetch;\nglobalThis.fetch = (async (url: string | URL | Request, init?: RequestInit) => {\n  calls.push({ url: String(url), init });\n  return new Response(JSON.stringify({ ok: true, url: String(url) }), {\n    status: 200,\n    headers: { \"content-type\": \"application/json\" },\n  });\n}) as typeof fetch;\n\ntry {\n  const atm = createAtmAppClient({\n    brokerUrl: \"https://checkout.atmosphere.money\",\n    getServiceAuthToken: ({ lxm, aud }) => `jwt:${lxm}:${aud}`,\n  });\n  await atm.getTicketAvailability({\n    environment: \"test\",\n    eventUri: \"at://did:plc:organizer/community.lexicon.calendar.event/demo\",\n  });\n  await atm.createTicketHold({\n    environment: \"test\",\n    eventUri: \"at://did:plc:organizer/community.lexicon.calendar.event/demo\",\n    buyerDid: \"did:plc:buyer\",\n    buyerAssertionJwt: \"buyer.assertion.jwt\",\n    items: [{ ticketTypeId: \"ttyp_demo\", quantity: 1 }],\n    returnUrl: \"https://events.example/tickets/return\",\n    cancelUrl: \"https://events.example/events/demo\",\n    idempotencyKey: \"hold:demo:buyer:ttyp_demo:1\",\n  });\n  await atm.claimFreeTicket({\n    environment: \"test\",\n    eventUri: \"at://did:plc:organizer/community.lexicon.calendar.event/demo\",\n    items: [{ ticketTypeId: \"ttyp_free_demo\", quantity: 1 }],\n    buyerDid: \"did:plc:buyer\",\n    buyerAssertionJwt: \"buyer.assertion.jwt\",\n    customerEmail: \"buyer@example.com\",\n    idempotencyKey: \"free:demo:buyer:ttyp_free_demo\",\n  });\n  await atm.verifyTicket({\n    environment: \"test\",\n    ticketToken: \"opaque_scan_token\",\n  });\n  await atm.checkInTicket({\n    environment: \"test\",\n    ticketToken: \"opaque_scan_token\",\n    checkInListId: \"list_demo\",\n  });\n} finally {\n  globalThis.fetch = originalFetch;\n}\n\nif (calls.length !== 5) {\n  throw new Error(`Expected 5 ATM calls, got ${calls.length}`);\n}\nif (!calls[1]?.url.endsWith(\"/xrpc/tickets.atmosphere.createTicketHold\")) {\n  throw new Error(\"Ticket hold XRPC call missing\");\n}\nconst holdBody = JSON.parse(String(calls[1].init?.body));\nif (holdBody.items[0]?.ticketTypeId !== \"ttyp_demo\" || \"tierId\" in holdBody.items[0]) {\n  throw new Error(\"Ticket hold body does not match current ticketTypeId contract\");\n}\nif (!calls[2]?.url.endsWith(\"/xrpc/tickets.atmosphere.claimFreeTicket\")) {\n  throw new Error(\"Free ticket claim XRPC call missing\");\n}\nconst freeBody = JSON.parse(String(calls[2].init?.body));\nif (freeBody.items[0]?.ticketTypeId !== \"ttyp_free_demo\" || freeBody.customerEmail !== \"buyer@example.com\" || \"ticketTypeId\" in freeBody) {\n  throw new Error(\"Free claim must send the current item array and delivery email\");\n}\n\nconsole.log(\"Tickets example smoke check passed.\");\n",
    "tsconfig.json": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2022\",\n    \"lib\": [\"ES2022\", \"DOM\"],\n    \"module\": \"NodeNext\",\n    \"moduleResolution\": \"NodeNext\",\n    \"strict\": true,\n    \"skipLibCheck\": true,\n    \"noEmit\": true,\n    \"types\": [\"node\"]\n  },\n  \"include\": [\"server.ts\", \"smoke.ts\"]\n}\n"
  },
  "sha256": {
    "README.md": "5043b035670e647cfbdb8ff43036b904c93aa9bf2b818266809c728034308551",
    ".env.example": "6813458fc104bc7a610d6e4d2e0b767856261b17a4674584b6a531615b13b71d",
    "package.json": "bcd9a5149a1f6e759849ebac81905cccc28edefb4d71697784ede8d62c9bd959",
    "package-lock.json": "a084a1e44aac6a000a49884a7963167844070c216c217c2a1d5b99f4fddad136",
    "server.ts": "3b5d697102c3c1a2ac89908ccd6184b52164f3b84759f7184638bcc196c2d9a7",
    "smoke.ts": "795de7fcebe2307c30bdc73702a1c5c7e8133d35f9a25f3e797889803f4327ff",
    "tsconfig.json": "2b8e52db0ff7588102897b5c1f0775815912f582f7cf3faf653d96fa119be6a4"
  }
}
