{"_id":"@al8b/http-bridge","name":"@al8b/http-bridge","dist-tags":{"latest":"0.1.15"},"versions":{"0.1.15":{"name":"@al8b/http-bridge","version":"0.1.15","sideEffects":false,"scripts":{"build":"tsup","clean":"bun --bun ../../../scripts/clean-package.mjs dist","test":"vitest run --passWithNoTests"},"main":"./dist/index.js","module":"./dist/index.mjs","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.mjs","require":"./dist/index.js"},"./package.json":"./package.json"},"dependencies":{"@al8b/runtime":"^0.1.15"},"devDependencies":{"typescript":"~5.9.3"},"keywords":["runtime","http","bridge","api","fetch"],"publishConfig":{"access":"public"},"_id":"@al8b/http-bridge@0.1.15","gitHead":"77176c9f112ae7f55faa84a09c190174133c6b07","description":"A production-ready `RuntimeBridge` that connects LootiScript games to your REST API via `host.request()`.","_nodeVersion":"20.20.2","_npmVersion":"10.8.2","dist":{"integrity":"sha512-QPDhdJnbyJQl9Ewxy/VmIkGkovvV9OR75TtloebsdkI6g1G5yTi/uq+VfYK5q3/h/WtqqJjyilnenPCCa93ptA==","shasum":"df5b122d29c9e955d8dfb829aa0d1471a0d359c3","tarball":"https://registry.npmjs.org/@al8b/http-bridge/-/http-bridge-0.1.15.tgz","fileCount":14,"unpackedSize":118566,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDs4vr4IlgAesDUZa4owXrJWBHP0rFPAnGVtiQ/vE6HegIhAPNxwd0ZhxD1tyQRbUTtP30Y0fG0HdCDLT469JrypxMY"}]},"_npmUser":{"name":"nonomxyz","email":"hi@nonom.xyz"},"directories":{},"maintainers":[{"name":"nonomxyz","email":"hi@nonom.xyz"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/http-bridge_0.1.15_1775979424700_0.9858635738182746"},"_hasShrinkwrap":false}},"time":{"created":"2026-04-12T07:37:04.546Z","0.1.15":"2026-04-12T07:37:04.831Z","modified":"2026-04-12T07:37:05.077Z"},"maintainers":[{"name":"nonomxyz","email":"hi@nonom.xyz"}],"description":"A production-ready `RuntimeBridge` that connects LootiScript games to your REST API via `host.request()`.","keywords":["runtime","http","bridge","api","fetch"],"readme":"# @al8b/http-bridge\n\nA production-ready `RuntimeBridge` that connects LootiScript games to your REST API via `host.request()`.\n\n## Why\n\nWhen building multiplayer or server-authoritative games, your LootiScript game needs to fetch player data, leaderboards, inventory, and more from your backend. Previously you had to implement the entire `RuntimeBridge` interface yourself. `@al8b/http-bridge` handles this for you — zero configuration for simple cases, full customization when you need it.\n\n## Installation\n\n```bash\nnpm install @al8b/http-bridge\n```\n\n## Quick Start\n\n```typescript\nimport { createHttpBridge } from \"@al8b/http-bridge\";\nimport { createRuntime } from \"@al8b/runtime\";\n\nconst bridge = createHttpBridge({\n  baseUrl: \"https://api.mygame.com\",\n});\n\ncreateRuntime({\n  bridge,\n  sources: { main: \"...\" },\n});\n```\n\nNow your LootiScript game can call your backend:\n\n```lua\n-- game.loot\nfunction showProfile()\n    host.request(\"user.getProfile\", { id: session.user().id }, function(response)\n        if response.ok then\n            print(\"Welcome \" + response.name);\n        else\n            print(\"Error: \" + response.error);\n        end\n    end);\nend\n\nfunction update()\n    -- Submit score to leaderboard (fire and forget)\n    host.emit(\"game.score\", { score: game_state.current_score });\nend\n```\n\n## Endpoint Mapping\n\nBy default, `host.request(\"user.getProfile\", ...)` calls `POST https://api.mygame.com/user.getProfile`. Use `endpoints` to map to cleaner URLs with parameter substitution:\n\n```typescript\nconst bridge = createHttpBridge({\n  baseUrl: \"https://api.mygame.com\",\n  endpoints: {\n    \"user.getProfile\":  \"/users/{id}\",\n    \"inventory.get\":    \"/inventory/{user_id}?slot={slot}\",\n    \"leaderboard.get\":  \"/leaderboard?limit={limit}\",\n  },\n});\n```\n\n- `{param}` tokens are replaced with values from the payload\n- Unmapped requests fall back to `/{requestName}` (e.g. `\"foo.bar\"` → `/foo.bar`)\n\n## Custom URL Builder\n\nFor full control over URL construction:\n\n```typescript\nconst bridge = createHttpBridge({\n  baseUrl: \"https://api.mygame.com\",\n  urlBuilder: (name, payload, baseUrl) => {\n    // Return an absolute URL to skip baseUrl prepending\n    return `https://api.mygame.com/v2/${name}`;\n    // Or return a path:\n    return `/v2/${name}`;\n  },\n});\n```\n\n## Custom Response Handler\n\nTransform every response before it reaches LootiScript:\n\n```typescript\nconst bridge = createHttpBridge({\n  baseUrl: \"https://api.mygame.com\",\n  responseHandler: async (res, name) => {\n    if (!res.ok) {\n      const err = await res.json();\n      throw new Error(err.message ?? `HTTP ${res.status}`);\n    }\n    return res.json(); // returned as { ok: true, ...data }\n  },\n});\n```\n\nIf `responseHandler` throws, the LootiScript callback receives `{ ok: false, error: \"...\" }`.\n\n## Custom Headers (Auth, etc.)\n\n```typescript\nconst bridge = createHttpBridge({\n  baseUrl: \"https://api.mygame.com\",\n  defaults: {\n    headers: {\n      \"Authorization\": \"Bearer \" + getAuthToken(),\n      \"X-Game-Id\":    \"my-game\",\n    },\n  },\n});\n```\n\n## Logging `host.emit`\n\n`host.emit()` calls are logged via `console.log` by default:\n\n```\n[http-bridge] game.score { score: 1250 }\n```\n\nSilence them:\n\n```typescript\nconst bridge = createHttpBridge({\n  baseUrl: \"https://api.mygame.com\",\n  logEmit: () => {},  // silence\n});\n```\n\n## Composition with Realtime Bridge\n\nCombine with `@al8b/runtime-realtime` for multiplayer + backend access:\n\n```typescript\nimport { createHttpBridge } from \"@al8b/http-bridge\";\nimport { createRealtimeBridge } from \"@al8b/runtime-realtime\";\n\nconst bridge = {\n  ...createRealtimeBridge(myWebSocket),\n  ...createHttpBridge({ baseUrl: \"https://api.mygame.com\" }),\n  // realtime: emit/subscribe for real-time events\n  // http: request for REST API calls\n};\n\ncreateRuntime({ bridge, sources: { main: \"...\" } });\n```\n\n## Error Handling\n\nAll responses follow the same shape:\n\n```typescript\n// Success\n{ ok: true, ...data }\n\n// HTTP error (4xx/5xx) or network error\n{ ok: false, error: \"User not found\" }\n```\n\nAlways check `response.ok` before using the data:\n\n```lua\nhost.request(\"user.getProfile\", { id: 1 }, function(response)\n    if response.ok then\n        -- safe to use response.name, response.email, etc.\n    else\n        print(\"API Error: \" + response.error);\n    end\nend);\n```\n\n## API Reference\n\n### `createHttpBridge(config)`\n\n**config.baseUrl** `string`\\\nBase URL for all API calls. Example: `\"https://api.mygame.com\"`.\n\n**config.endpoints** `Record<string, string>`\\\nOptional map of request name → endpoint path. Supports `{param}` substitution from payload.\n\n**config.defaults** `RequestInit`\\\nDefault fetch options applied to every request (headers, credentials, etc.).\n\n**config.urlBuilder** `(name, payload, baseUrl) => string`\\\nCustom URL builder. Return a full URL (with protocol) to bypass baseUrl, or a path to prepend baseUrl.\n\n**config.responseHandler** `(response, name) => Promise<unknown>`\\\nTransform every response. Throw to return an error shape to LootiScript.\n\n**config.logEmit** `(name, payload) => void`\\\nLogger for `host.emit()` calls. Defaults to `console.log`. Pass `() => {}` to silence.\n","readmeFilename":"README.md","_rev":"1-7d24b6d1b5f86f701677c2c66511e084"}