@palbase/backend 17.4.0 → 18.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bin/palbase-backend.cjs +1848 -0
- package/dist/bin/palbase-backend.cjs.map +1 -0
- package/dist/bin/palbase-backend.d.cts +1 -0
- package/dist/bin/palbase-backend.d.ts +1 -0
- package/dist/bin/palbase-backend.js +168 -0
- package/dist/bin/palbase-backend.js.map +1 -0
- package/dist/chunk-7D4SUZUM.js +38 -0
- package/dist/chunk-7D4SUZUM.js.map +1 -0
- package/dist/chunk-N32VDWKH.js +172 -0
- package/dist/chunk-N32VDWKH.js.map +1 -0
- package/dist/chunk-POYAFBLF.js +189 -0
- package/dist/chunk-POYAFBLF.js.map +1 -0
- package/dist/chunk-QMVK4X3V.js +200 -0
- package/dist/chunk-QMVK4X3V.js.map +1 -0
- package/dist/chunk-SSGAMC26.js +342 -0
- package/dist/chunk-SSGAMC26.js.map +1 -0
- package/dist/chunk-VYH4U7ZQ.js +1138 -0
- package/dist/chunk-VYH4U7ZQ.js.map +1 -0
- package/dist/{chunk-AAN642N5.js → chunk-W5ODXPY3.js} +2 -336
- package/dist/chunk-W5ODXPY3.js.map +1 -0
- package/dist/chunk-YL4C5NRY.js +90 -0
- package/dist/chunk-YL4C5NRY.js.map +1 -0
- package/dist/db/env.cjs.map +1 -1
- package/dist/db/env.d.cts +21 -1
- package/dist/db/env.d.ts +21 -1
- package/dist/db/index.cjs.map +1 -1
- package/dist/db/index.d.cts +2 -1
- package/dist/db/index.d.ts +2 -1
- package/dist/db/index.js +9 -6
- package/dist/{index-VLrU7rSW.d.ts → endpoint-B0LpZixz.d.cts} +124 -685
- package/dist/{index-BA_oFAz9.d.cts → endpoint-B0LpZixz.d.ts} +124 -685
- package/dist/engine/index.cjs +1797 -0
- package/dist/engine/index.cjs.map +1 -0
- package/dist/engine/index.d.cts +7 -0
- package/dist/engine/index.d.ts +7 -0
- package/dist/engine/index.js +43 -0
- package/dist/engine/index.js.map +1 -0
- package/dist/index-B46CGNvx.d.cts +839 -0
- package/dist/index-BGSCWlUa.d.cts +674 -0
- package/dist/index-DZDUMth5.d.ts +839 -0
- package/dist/index-g-EzitI-.d.ts +674 -0
- package/dist/index.cjs +1031 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +290 -532
- package/dist/index.d.ts +290 -532
- package/dist/index.js +999 -509
- package/dist/index.js.map +1 -1
- package/dist/openapi/index.cjs +6464 -0
- package/dist/openapi/index.cjs.map +1 -0
- package/dist/openapi/index.d.cts +170 -0
- package/dist/openapi/index.d.ts +170 -0
- package/dist/openapi/index.js +6248 -0
- package/dist/openapi/index.js.map +1 -0
- package/dist/registry-3BLYv4si.d.ts +338 -0
- package/dist/registry-Cw0YEYCg.d.cts +338 -0
- package/dist/test/index.js +2 -0
- package/dist/test/index.js.map +1 -1
- package/docs/database.md +16 -3
- package/docs/llms-full.txt +16 -3
- package/package.json +43 -13
- package/stager/package.json +4 -0
- package/stager/return_types.js +338 -0
- package/stager/stage.js +78 -0
- package/stager/throw_analysis.js +726 -0
- package/template/AGENTS.md +261 -0
- package/template/config/secrets.ts +24 -0
- package/template/controllers/health.controller.ts +30 -0
- package/template/db/schema.ts +35 -0
- package/template/package.json +18 -0
- package/template/tsconfig.json +30 -0
- package/LICENSE +0 -21
- package/dist/chunk-AAN642N5.js.map +0 -1
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import {
|
|
3
|
+
BootRefused,
|
|
4
|
+
createApp,
|
|
5
|
+
loadConfig
|
|
6
|
+
} from "../chunk-VYH4U7ZQ.js";
|
|
7
|
+
import "../chunk-POYAFBLF.js";
|
|
8
|
+
import "../chunk-W5ODXPY3.js";
|
|
9
|
+
import {
|
|
10
|
+
getRegisteredControllers
|
|
11
|
+
} from "../chunk-YL4C5NRY.js";
|
|
12
|
+
import "../chunk-QMVK4X3V.js";
|
|
13
|
+
import "../chunk-7D4SUZUM.js";
|
|
14
|
+
|
|
15
|
+
// src/bin/palbase-backend.ts
|
|
16
|
+
import { readdir, stat } from "fs/promises";
|
|
17
|
+
import { join, resolve } from "path";
|
|
18
|
+
import { pathToFileURL } from "url";
|
|
19
|
+
var USAGE = `palbase-backend \u2014 run this project's backend
|
|
20
|
+
|
|
21
|
+
palbase-backend serve start the backend
|
|
22
|
+
palbase-backend dev start it and reload on change
|
|
23
|
+
palbase-backend routes print the route table and exit
|
|
24
|
+
|
|
25
|
+
Configuration comes from the environment (a .env beside package.json is read):
|
|
26
|
+
|
|
27
|
+
DATABASE_URL required \u2014 the stack's Postgres
|
|
28
|
+
AUTH_JWKS_URL required \u2014 where your stack publishes its signing keys
|
|
29
|
+
MODULE_BASE_URL where Documents/Storage/Notifications/Flags/Realtime live
|
|
30
|
+
PALBASE_ANON_KEY publishable key, sent on module calls
|
|
31
|
+
PALBASE_SERVICE_ROLE_KEY secret key, for privileged module calls
|
|
32
|
+
PORT default 3000
|
|
33
|
+
`;
|
|
34
|
+
function die(message) {
|
|
35
|
+
console.error(message);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
async function loadDotEnv(root) {
|
|
39
|
+
const file = Bun.file(join(root, ".env"));
|
|
40
|
+
if (!await file.exists()) return;
|
|
41
|
+
for (const raw of (await file.text()).split("\n")) {
|
|
42
|
+
const line = raw.trim();
|
|
43
|
+
if (!line || line.startsWith("#")) continue;
|
|
44
|
+
const eq = line.indexOf("=");
|
|
45
|
+
if (eq < 1) continue;
|
|
46
|
+
const key = line.slice(0, eq).trim();
|
|
47
|
+
if (process.env[key] !== void 0) continue;
|
|
48
|
+
let value = line.slice(eq + 1).trim();
|
|
49
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
50
|
+
value = value.slice(1, -1);
|
|
51
|
+
}
|
|
52
|
+
process.env[key] = value;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
async function findControllers(root) {
|
|
56
|
+
const dir = join(root, "controllers");
|
|
57
|
+
try {
|
|
58
|
+
if (!(await stat(dir)).isDirectory()) return [];
|
|
59
|
+
} catch {
|
|
60
|
+
return [];
|
|
61
|
+
}
|
|
62
|
+
const out = [];
|
|
63
|
+
const walk = async (d) => {
|
|
64
|
+
for (const entry of await readdir(d, { withFileTypes: true })) {
|
|
65
|
+
const full = join(d, entry.name);
|
|
66
|
+
if (entry.isDirectory()) await walk(full);
|
|
67
|
+
else if (/\.controller\.(ts|js|mts|mjs)$/.test(entry.name)) out.push(full);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
await walk(dir);
|
|
71
|
+
return out.sort();
|
|
72
|
+
}
|
|
73
|
+
async function importSchema(root) {
|
|
74
|
+
for (const candidate of ["db/schema.ts", "db/schema.js"]) {
|
|
75
|
+
const path = join(root, candidate);
|
|
76
|
+
try {
|
|
77
|
+
await stat(path);
|
|
78
|
+
return (await import(pathToFileURL(path).href)).default;
|
|
79
|
+
} catch {
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return void 0;
|
|
83
|
+
}
|
|
84
|
+
async function main() {
|
|
85
|
+
const command = process.argv[2] ?? "serve";
|
|
86
|
+
if (command === "--help" || command === "-h" || command === "help") {
|
|
87
|
+
console.log(USAGE);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (!["serve", "dev", "routes"].includes(command)) {
|
|
91
|
+
die(`palbase-backend: unknown command "${command}".
|
|
92
|
+
|
|
93
|
+
${USAGE}`);
|
|
94
|
+
}
|
|
95
|
+
if (typeof Bun === "undefined") {
|
|
96
|
+
die(
|
|
97
|
+
"palbase-backend needs Bun: the engine serves `fetch` natively and opens its own\nPostgres pool through Bun.sql. Install it from https://bun.sh, then re-run."
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
if (command === "dev" && !process.env.PALBASE_WATCHING) {
|
|
101
|
+
const self = Bun.fileURLToPath(import.meta.url);
|
|
102
|
+
const child = Bun.spawn(["bun", "--watch", self, "serve"], {
|
|
103
|
+
stdio: ["inherit", "inherit", "inherit"],
|
|
104
|
+
env: { ...process.env, PALBASE_WATCHING: "1" }
|
|
105
|
+
});
|
|
106
|
+
process.exit(await child.exited);
|
|
107
|
+
}
|
|
108
|
+
const root = resolve(process.env.PALBASE_PROJECT_DIR ?? process.cwd());
|
|
109
|
+
await loadDotEnv(root);
|
|
110
|
+
const files = await findControllers(root);
|
|
111
|
+
if (files.length === 0) {
|
|
112
|
+
die(
|
|
113
|
+
`palbase-backend: no controllers found under ${join(root, "controllers")}.
|
|
114
|
+
A backend is its controllers \u2014 add one and run again:
|
|
115
|
+
|
|
116
|
+
// controllers/hello.controller.ts
|
|
117
|
+
import { Controller, Get } from "@palbase/backend";
|
|
118
|
+
@Controller("/hello")
|
|
119
|
+
class HelloController {
|
|
120
|
+
@Get("") hi(): Promise<{ ok: boolean }> { return Promise.resolve({ ok: true }); }
|
|
121
|
+
}
|
|
122
|
+
`
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
for (const file of files) await import(pathToFileURL(file).href);
|
|
126
|
+
const controllers = getRegisteredControllers();
|
|
127
|
+
if (controllers.length === 0) {
|
|
128
|
+
die(
|
|
129
|
+
`palbase-backend: ${files.length} controller file(s) loaded but none registered.
|
|
130
|
+
Every one of them is missing its @Controller decorator, or the files were
|
|
131
|
+
compiled with decorators stripped. Nothing would answer, so this is fatal.`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
let config;
|
|
135
|
+
try {
|
|
136
|
+
config = loadConfig(process.env);
|
|
137
|
+
} catch (e) {
|
|
138
|
+
if (e instanceof BootRefused) {
|
|
139
|
+
die(
|
|
140
|
+
`${e.message}
|
|
141
|
+
|
|
142
|
+
Put them in a .env beside package.json, or export them. A local stack
|
|
143
|
+
(docker compose up) publishes both.`
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
throw e;
|
|
147
|
+
}
|
|
148
|
+
const app = await createApp({ config, controllers, schema: await importSchema(root) });
|
|
149
|
+
if (command === "routes") {
|
|
150
|
+
for (const route of app.routes) console.log(route.id);
|
|
151
|
+
await app.shutdown();
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
Bun.serve({ port: config.port, idleTimeout: 60, fetch: app.handle });
|
|
155
|
+
console.log(`palbase-backend listening on http://localhost:${config.port}`);
|
|
156
|
+
console.log(` ${app.routes.length} endpoint(s) from ${files.length} controller file(s)`);
|
|
157
|
+
for (const route of app.routes) console.log(` ${route.id}`);
|
|
158
|
+
for (const signal of ["SIGTERM", "SIGINT"]) {
|
|
159
|
+
process.on(signal, () => {
|
|
160
|
+
void app.shutdown().finally(() => process.exit(0));
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
main().catch((e) => {
|
|
165
|
+
console.error(e instanceof Error ? e.message : e);
|
|
166
|
+
process.exit(1);
|
|
167
|
+
});
|
|
168
|
+
//# sourceMappingURL=palbase-backend.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/bin/palbase-backend.ts"],"sourcesContent":["#!/usr/bin/env bun\n/**\n * palbase-backend — run this project's backend.\n *\n * npm run dev → palbase-backend dev (reloads on change)\n * npm start → palbase-backend serve\n *\n * There is no scaffolding step and no server file to write. The project IS the\n * backend: every `controllers/*.controller.ts` is imported, which is what\n * registers it, and the engine is built around whatever registered.\n *\n * It is the same engine the deployed runtime builds — not a development\n * stand-in. A dev server that is a different program from the production one is\n * a source of \"works locally\" reports, and this one has no second implementation\n * to disagree with.\n */\nimport { readdir, stat } from \"node:fs/promises\";\nimport { join, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\n\nimport { createApp, loadConfig, BootRefused } from \"../engine/index.js\";\nimport { getRegisteredControllers } from \"../decorators/controller.js\";\n\nconst USAGE = `palbase-backend — run this project's backend\n\n palbase-backend serve start the backend\n palbase-backend dev start it and reload on change\n palbase-backend routes print the route table and exit\n\nConfiguration comes from the environment (a .env beside package.json is read):\n\n DATABASE_URL required — the stack's Postgres\n AUTH_JWKS_URL required — where your stack publishes its signing keys\n MODULE_BASE_URL where Documents/Storage/Notifications/Flags/Realtime live\n PALBASE_ANON_KEY publishable key, sent on module calls\n PALBASE_SERVICE_ROLE_KEY secret key, for privileged module calls\n PORT default 3000\n`;\n\nfunction die(message: string): never {\n console.error(message);\n process.exit(1);\n}\n\n/** Read a `.env` beside the project, without adding a dependency for it. */\nasync function loadDotEnv(root: string): Promise<void> {\n const file = Bun.file(join(root, \".env\"));\n if (!(await file.exists())) return;\n for (const raw of (await file.text()).split(\"\\n\")) {\n const line = raw.trim();\n if (!line || line.startsWith(\"#\")) continue;\n const eq = line.indexOf(\"=\");\n if (eq < 1) continue;\n const key = line.slice(0, eq).trim();\n // The environment wins: an exported value is the operator being explicit,\n // and a file quietly overriding it is how a \"why is it still pointing at\n // the old database\" hour begins.\n if (process.env[key] !== undefined) continue;\n let value = line.slice(eq + 1).trim();\n if (\n (value.startsWith('\"') && value.endsWith('\"')) ||\n (value.startsWith(\"'\") && value.endsWith(\"'\"))\n ) {\n value = value.slice(1, -1);\n }\n process.env[key] = value;\n }\n}\n\n/** Every `*.controller.ts` under `controllers/`, sorted, recursively. */\nasync function findControllers(root: string): Promise<string[]> {\n const dir = join(root, \"controllers\");\n try {\n if (!(await stat(dir)).isDirectory()) return [];\n } catch {\n return [];\n }\n const out: string[] = [];\n const walk = async (d: string): Promise<void> => {\n for (const entry of await readdir(d, { withFileTypes: true })) {\n const full = join(d, entry.name);\n if (entry.isDirectory()) await walk(full);\n else if (/\\.controller\\.(ts|js|mts|mjs)$/.test(entry.name)) out.push(full);\n }\n };\n await walk(dir);\n // Sorted so route precedence is the same on every machine.\n return out.sort();\n}\n\nasync function importSchema(root: string): Promise<unknown> {\n for (const candidate of [\"db/schema.ts\", \"db/schema.js\"]) {\n const path = join(root, candidate);\n try {\n await stat(path);\n return (await import(pathToFileURL(path).href)).default;\n } catch {\n // Not every project declares a schema; the typed `.tables` surface is\n // simply absent then.\n }\n }\n return undefined;\n}\n\nasync function main(): Promise<void> {\n const command = process.argv[2] ?? \"serve\";\n if (command === \"--help\" || command === \"-h\" || command === \"help\") {\n console.log(USAGE);\n return;\n }\n if (![\"serve\", \"dev\", \"routes\"].includes(command)) {\n die(`palbase-backend: unknown command \"${command}\".\\n\\n${USAGE}`);\n }\n if (typeof Bun === \"undefined\") {\n die(\n \"palbase-backend needs Bun: the engine serves `fetch` natively and opens its own\\n\" +\n \"Postgres pool through Bun.sql. Install it from https://bun.sh, then re-run.\",\n );\n }\n\n // `dev` is `serve` under Bun's watcher. Re-exec rather than reimplement:\n // one code path serves, and the reload is the runtime's job, not ours.\n if (command === \"dev\" && !process.env.PALBASE_WATCHING) {\n const self = Bun.fileURLToPath(import.meta.url);\n const child = Bun.spawn([\"bun\", \"--watch\", self, \"serve\"], {\n stdio: [\"inherit\", \"inherit\", \"inherit\"],\n env: { ...process.env, PALBASE_WATCHING: \"1\" },\n });\n process.exit(await child.exited);\n }\n\n const root = resolve(process.env.PALBASE_PROJECT_DIR ?? process.cwd());\n await loadDotEnv(root);\n\n const files = await findControllers(root);\n if (files.length === 0) {\n die(\n `palbase-backend: no controllers found under ${join(root, \"controllers\")}.\\n` +\n `A backend is its controllers — add one and run again:\\n\\n` +\n ` // controllers/hello.controller.ts\\n` +\n ` import { Controller, Get } from \"@palbase/backend\";\\n` +\n ` @Controller(\"/hello\")\\n` +\n ` class HelloController {\\n` +\n ` @Get(\"\") hi(): Promise<{ ok: boolean }> { return Promise.resolve({ ok: true }); }\\n` +\n ` }\\n`,\n );\n }\n\n // Importing IS the registration — the decorator records each class as it runs.\n for (const file of files) await import(pathToFileURL(file).href);\n const controllers = getRegisteredControllers();\n if (controllers.length === 0) {\n die(\n `palbase-backend: ${files.length} controller file(s) loaded but none registered.\\n` +\n `Every one of them is missing its @Controller decorator, or the files were\\n` +\n `compiled with decorators stripped. Nothing would answer, so this is fatal.`,\n );\n }\n\n let config;\n try {\n config = loadConfig(process.env as Record<string, string | undefined>);\n } catch (e) {\n if (e instanceof BootRefused) {\n die(\n `${e.message}\\n\\n` +\n `Put them in a .env beside package.json, or export them. A local stack\\n` +\n `(docker compose up) publishes both.`,\n );\n }\n throw e;\n }\n\n const app = await createApp({ config, controllers, schema: await importSchema(root) });\n\n if (command === \"routes\") {\n for (const route of app.routes) console.log(route.id);\n await app.shutdown();\n return;\n }\n\n Bun.serve({ port: config.port, idleTimeout: 60, fetch: app.handle });\n console.log(`palbase-backend listening on http://localhost:${config.port}`);\n console.log(` ${app.routes.length} endpoint(s) from ${files.length} controller file(s)`);\n for (const route of app.routes) console.log(` ${route.id}`);\n\n for (const signal of [\"SIGTERM\", \"SIGINT\"] as const) {\n process.on(signal, () => {\n void app.shutdown().finally(() => process.exit(0));\n });\n }\n}\n\n// Not a top-level await: this file is also emitted in a CommonJS flavour, where\n// one is a build error. The rejection handler is the point either way — an\n// unhandled one exits 0 on some runtimes, which would report a dead backend as\n// a successful start.\nmain().catch((e: unknown) => {\n console.error(e instanceof Error ? e.message : e);\n process.exit(1);\n});\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
var __create = Object.create;
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
8
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
9
|
+
}) : x)(function(x) {
|
|
10
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
11
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
12
|
+
});
|
|
13
|
+
var __commonJS = (cb, mod) => function __require2() {
|
|
14
|
+
return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports;
|
|
15
|
+
};
|
|
16
|
+
var __copyProps = (to, from, except, desc) => {
|
|
17
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
18
|
+
for (let key of __getOwnPropNames(from))
|
|
19
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
20
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
21
|
+
}
|
|
22
|
+
return to;
|
|
23
|
+
};
|
|
24
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
25
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
26
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
27
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
28
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
29
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
30
|
+
mod
|
|
31
|
+
));
|
|
32
|
+
|
|
33
|
+
export {
|
|
34
|
+
__require,
|
|
35
|
+
__commonJS,
|
|
36
|
+
__toESM
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=chunk-7D4SUZUM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import {
|
|
2
|
+
HttpError
|
|
3
|
+
} from "./chunk-QMVK4X3V.js";
|
|
4
|
+
|
|
5
|
+
// src/purchases/registry.ts
|
|
6
|
+
var ENTITLEMENTS = /* @__PURE__ */ Symbol.for("palbase.backend.purchases.entitlements");
|
|
7
|
+
var SPENDS = /* @__PURE__ */ Symbol.for("palbase.backend.purchases.spends");
|
|
8
|
+
function carrierOf(target) {
|
|
9
|
+
return typeof target === "function" ? target : target.constructor ?? target;
|
|
10
|
+
}
|
|
11
|
+
function own(carrier, slot) {
|
|
12
|
+
if (!Object.prototype.hasOwnProperty.call(carrier, slot)) {
|
|
13
|
+
carrier[slot] = {};
|
|
14
|
+
}
|
|
15
|
+
return carrier[slot];
|
|
16
|
+
}
|
|
17
|
+
function recordEntitlement(target, fnName, key) {
|
|
18
|
+
own(carrierOf(target), ENTITLEMENTS)[fnName] = key;
|
|
19
|
+
}
|
|
20
|
+
function recordSpend(target, fnName, meta) {
|
|
21
|
+
own(carrierOf(target), SPENDS)[fnName] = meta;
|
|
22
|
+
}
|
|
23
|
+
function entitlementFor(ctor, fnName) {
|
|
24
|
+
return carrierOf(ctor)[ENTITLEMENTS]?.[fnName];
|
|
25
|
+
}
|
|
26
|
+
function spendFor(ctor, fnName) {
|
|
27
|
+
return carrierOf(ctor)[SPENDS]?.[fnName];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// src/error-registry.ts
|
|
31
|
+
import {
|
|
32
|
+
OpenAPIRegistry,
|
|
33
|
+
OpenApiGeneratorV31,
|
|
34
|
+
extendZodWithOpenApi
|
|
35
|
+
} from "@asteasolutions/zod-to-openapi";
|
|
36
|
+
import { z } from "zod";
|
|
37
|
+
extendZodWithOpenApi(z);
|
|
38
|
+
var ERROR_REGISTRY = /* @__PURE__ */ Symbol.for("palbase.backend.errorRegistry");
|
|
39
|
+
var BUILTIN_DATA_SCHEMAS = {
|
|
40
|
+
bad_request: z.object({
|
|
41
|
+
fields: z.array(z.object({ field: z.string(), message: z.string() }))
|
|
42
|
+
}),
|
|
43
|
+
too_many_requests: z.object({ retryAfter: z.number().int() }),
|
|
44
|
+
// Purchases (@RequireEntitlement / @Spend). Their payloads are what make a
|
|
45
|
+
// paywall renderable on the client: WHICH entitlement was missing, and for a
|
|
46
|
+
// spend the real ceiling and reset time rather than a guessed retry delay.
|
|
47
|
+
entitlement_required: z.object({ entitlement: z.string().nullable() }),
|
|
48
|
+
quota_exceeded: z.object({
|
|
49
|
+
key: z.string(),
|
|
50
|
+
scope: z.string(),
|
|
51
|
+
window: z.string(),
|
|
52
|
+
used: z.number().int(),
|
|
53
|
+
reserved: z.number().int(),
|
|
54
|
+
max: z.number().int(),
|
|
55
|
+
remaining: z.number().int(),
|
|
56
|
+
resetAt: z.string()
|
|
57
|
+
}),
|
|
58
|
+
credit_insufficient: z.object({
|
|
59
|
+
key: z.string(),
|
|
60
|
+
balance: z.number().int(),
|
|
61
|
+
reserved: z.number().int(),
|
|
62
|
+
remaining: z.number().int()
|
|
63
|
+
})
|
|
64
|
+
};
|
|
65
|
+
function getErrorRegistry() {
|
|
66
|
+
const g = globalThis;
|
|
67
|
+
if (!g[ERROR_REGISTRY]) {
|
|
68
|
+
const m = /* @__PURE__ */ new Map();
|
|
69
|
+
for (const [code, status, className] of [
|
|
70
|
+
["bad_request", 400, "BadRequest"],
|
|
71
|
+
["unauthorized", 401, "Unauthorized"],
|
|
72
|
+
["forbidden", 403, "Forbidden"],
|
|
73
|
+
["not_found", 404, "NotFound"],
|
|
74
|
+
["conflict", 409, "Conflict"],
|
|
75
|
+
["too_many_requests", 429, "TooManyRequests"],
|
|
76
|
+
// Thrown by the purchases decorators, so they are pre-seeded like the
|
|
77
|
+
// other built-ins: a project never declares them, but every gated or
|
|
78
|
+
// metered route must surface them as TYPED errors on the client.
|
|
79
|
+
["entitlement_required", 403, "EntitlementRequired"],
|
|
80
|
+
["quota_exceeded", 429, "QuotaExceeded"],
|
|
81
|
+
["credit_insufficient", 429, "CreditInsufficient"]
|
|
82
|
+
]) {
|
|
83
|
+
const dataSchema = BUILTIN_DATA_SCHEMAS[code];
|
|
84
|
+
m.set(code, {
|
|
85
|
+
code,
|
|
86
|
+
status,
|
|
87
|
+
className,
|
|
88
|
+
builtin: true,
|
|
89
|
+
...dataSchema ? { dataSchema, dataDigest: digestOf(code, dataSchema) } : {}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
g[ERROR_REGISTRY] = m;
|
|
93
|
+
}
|
|
94
|
+
return g[ERROR_REGISTRY];
|
|
95
|
+
}
|
|
96
|
+
function defineError(code, status, dataSchema) {
|
|
97
|
+
if (!Number.isInteger(status) || status < 400 || status > 599) {
|
|
98
|
+
throw new Error(
|
|
99
|
+
`defineError: status for "${code}" must be a 4xx/5xx integer, got ${status} \u2014 error responses must not clobber success responses in the project spec.`
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
const registry = getErrorRegistry();
|
|
103
|
+
const existing = registry.get(code);
|
|
104
|
+
const className = defaultClassName(code);
|
|
105
|
+
const dataDigest = dataSchema ? digestOf(code, dataSchema) : void 0;
|
|
106
|
+
if (existing) {
|
|
107
|
+
const sameShape = existing.status === status && !existing.builtin && existing.dataDigest === dataDigest;
|
|
108
|
+
if (!sameShape) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
`defineError: duplicate error code "${code}" with a different shape (existing: status ${existing.status}${existing.builtin ? ", built-in" : ""}${existing.status === status && !existing.builtin ? ", different data schema" : ""}). Error codes are project-unique.`
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const makeWithData = (schema) => class extends HttpError {
|
|
115
|
+
static code = code;
|
|
116
|
+
static status = status;
|
|
117
|
+
constructor(data, message) {
|
|
118
|
+
super(status, code, message ?? humanize(code), schema.parse(data));
|
|
119
|
+
this.name = className;
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
const makeWithoutData = () => class extends HttpError {
|
|
123
|
+
static code = code;
|
|
124
|
+
static status = status;
|
|
125
|
+
constructor(message) {
|
|
126
|
+
super(status, code, message ?? humanize(code));
|
|
127
|
+
this.name = className;
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
const cls = dataSchema ? makeWithData(dataSchema) : makeWithoutData();
|
|
131
|
+
Object.defineProperty(cls, "name", { value: className });
|
|
132
|
+
registry.set(code, {
|
|
133
|
+
code,
|
|
134
|
+
status,
|
|
135
|
+
className,
|
|
136
|
+
builtin: false,
|
|
137
|
+
...dataSchema ? { dataSchema } : {},
|
|
138
|
+
...dataDigest !== void 0 ? { dataDigest } : {}
|
|
139
|
+
});
|
|
140
|
+
return cls;
|
|
141
|
+
}
|
|
142
|
+
function digestOf(code, dataSchema) {
|
|
143
|
+
const TMP_REF = "__PalbaseErrorDataDigest";
|
|
144
|
+
const tmpRegistry = new OpenAPIRegistry();
|
|
145
|
+
tmpRegistry.register(TMP_REF, z.object({ data: dataSchema }).openapi(TMP_REF));
|
|
146
|
+
const generated = new OpenApiGeneratorV31(tmpRegistry.definitions).generateComponents();
|
|
147
|
+
const out = generated.components?.schemas?.[TMP_REF];
|
|
148
|
+
const digest = JSON.stringify(out ?? null);
|
|
149
|
+
if (digest.includes('"$ref"')) {
|
|
150
|
+
throw new Error(
|
|
151
|
+
`defineError: dataSchema for "${code}" carries .openapi(refId) metadata \u2014 it would emit dangling $ref pointers in the project spec. Use a plain zod schema (z.object({...})) without .openapi(...).`
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
return digest;
|
|
155
|
+
}
|
|
156
|
+
function defaultClassName(code) {
|
|
157
|
+
return code.split("_").map((p) => p.charAt(0).toUpperCase() + p.slice(1)).join("");
|
|
158
|
+
}
|
|
159
|
+
function humanize(code) {
|
|
160
|
+
const s = code.replace(/_/g, " ");
|
|
161
|
+
return s.charAt(0).toUpperCase() + s.slice(1);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export {
|
|
165
|
+
recordEntitlement,
|
|
166
|
+
recordSpend,
|
|
167
|
+
entitlementFor,
|
|
168
|
+
spendFor,
|
|
169
|
+
getErrorRegistry,
|
|
170
|
+
defineError
|
|
171
|
+
};
|
|
172
|
+
//# sourceMappingURL=chunk-N32VDWKH.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/purchases/registry.ts","../src/error-registry.ts"],"sourcesContent":["/**\n * Per-method purchases metadata, stored on the controller class the same way\n * `decorators/registry.ts` stores routes: a symbol-keyed static, plain data, no\n * `reflect-metadata`.\n *\n * Its only consumer is the OpenAPI step, which needs to know that a route is\n * gated (declare 403) or metered (declare 429) so the iOS client codegen emits\n * TYPED errors instead of a bare status number. Enforcement does NOT read this\n * — the decorators wrap the method directly — so a stale registry can never\n * cause a missed gate.\n */\n\nimport type { EntitlementKey, LimitKey } from \"./keys.js\";\n\nconst ENTITLEMENTS: unique symbol = Symbol.for(\"palbase.backend.purchases.entitlements\");\nconst SPENDS: unique symbol = Symbol.for(\"palbase.backend.purchases.spends\");\n\n/** What `@Spend` recorded for one method. */\nexport interface SpendMeta {\n key: LimitKey;\n count: number;\n}\n\ninterface PurchasesCarrier {\n [ENTITLEMENTS]?: Record<string, EntitlementKey>;\n [SPENDS]?: Record<string, SpendMeta>;\n}\n\n/** Resolve a decorated target (prototype or constructor) to the constructor\n * that owns the metadata, mirroring `carrierOf` in decorators/registry.ts. */\nfunction carrierOf(target: object): PurchasesCarrier {\n return (\n typeof target === \"function\"\n ? (target as unknown as PurchasesCarrier)\n : (((target as { constructor?: unknown }).constructor ?? target) as PurchasesCarrier)\n );\n}\n\n/** Own (not inherited) slot, so a subclass never mutates its base's metadata. */\nfunction own<K extends typeof ENTITLEMENTS | typeof SPENDS>(\n carrier: PurchasesCarrier,\n slot: K,\n): NonNullable<PurchasesCarrier[K]> {\n if (!Object.prototype.hasOwnProperty.call(carrier, slot)) {\n carrier[slot] = {} as never;\n }\n return carrier[slot] as NonNullable<PurchasesCarrier[K]>;\n}\n\nexport function recordEntitlement(target: object, fnName: string, key: EntitlementKey): void {\n own(carrierOf(target), ENTITLEMENTS)[fnName] = key;\n}\n\nexport function recordSpend(target: object, fnName: string, meta: SpendMeta): void {\n own(carrierOf(target), SPENDS)[fnName] = meta;\n}\n\n/** The entitlement `fnName` is gated on, or undefined when it is not gated. */\nexport function entitlementFor(ctor: object, fnName: string): EntitlementKey | undefined {\n return carrierOf(ctor)[ENTITLEMENTS]?.[fnName];\n}\n\n/** What `fnName` spends, or undefined when it spends nothing. */\nexport function spendFor(ctor: object, fnName: string): SpendMeta | undefined {\n return carrierOf(ctor)[SPENDS]?.[fnName];\n}\n","// defineError — project-defined typed error classes + the module-global error\n// registry the OpenAPI spec twins join against.\n//\n// `defineError(code, status, dataSchema?)` returns an `HttpError` subclass whose\n// constructor is typed by the zod schema (`new TodoLocked({ retryAfter: 30 })`).\n// Each call self-registers `{ code, status, className, dataSchema }` in a\n// registry anchored on `globalThis` under `Symbol.for(...)` — mirroring the\n// ROUTES pattern — so the deploy bundle's registrations land on the single SDK\n// instance the extractor `require()`s. The registry is pre-seeded with the named\n// built-in classes (`NotFound`, `Conflict`, …) so throw inference can resolve\n// ad-hoc `throw new NotFound(...)` sites by their canonical wire codes.\n//\n// Duplicate code with a DIFFERENT shape (status OR converted data schema) is a\n// hard error at registration time (deploy fails loudly — error codes are\n// project-unique); structurally-identical re-registration is idempotent (each\n// controller BUNDLE carries its own copy of a shared models/errors.ts, so the\n// same defineError line legitimately runs once per bundle in one process).\nimport {\n OpenAPIRegistry,\n OpenApiGeneratorV31,\n extendZodWithOpenApi,\n} from \"@asteasolutions/zod-to-openapi\";\nimport { z, type ZodTypeAny } from \"zod\";\nimport { HttpError } from \"./errors.js\";\n\nextendZodWithOpenApi(z);\n\n/** One registered error: the wire code, HTTP status, the class name the\n * analyzer sees, the optional data payload schema, and whether it is one of the\n * pre-seeded built-in named classes. */\nexport interface RegisteredError {\n code: string;\n status: number;\n className: string;\n dataSchema?: ZodTypeAny;\n /** JSON.stringify of the converted JSON schema — equal digest ⇔ the spec\n * twins would emit an identical `data` schema for this error. */\n dataDigest?: string;\n builtin: boolean;\n}\n\n/** The cross-module-instance registry anchor (the pinned contract symbol). */\nconst ERROR_REGISTRY: unique symbol = Symbol.for(\"palbase.backend.errorRegistry\");\n\ninterface RegistryCarrier {\n [ERROR_REGISTRY]?: Map<string, RegisteredError>;\n}\n\n/** Get (creating + pre-seeding if absent) the project-global error registry,\n * keyed by wire code. Anchored on `globalThis` so every copy of the SDK in a\n * process shares the one registry. */\n/** The fixed data schemas the built-in named errors ship. Declared HERE (not in\n * errors.ts, which stays zod-free) because this is where the registry converts\n * them to JSON-schema for the spec — keying codegen's typed `error.data`. Only\n * the errors that semantically carry a payload have a schema; the rest are\n * data-less. `.int()` is preserved so the wire/codegen sees an integer. */\nconst BUILTIN_DATA_SCHEMAS: Record<string, ZodTypeAny> = {\n bad_request: z.object({\n fields: z.array(z.object({ field: z.string(), message: z.string() })),\n }),\n too_many_requests: z.object({ retryAfter: z.number().int() }),\n // Purchases (@RequireEntitlement / @Spend). Their payloads are what make a\n // paywall renderable on the client: WHICH entitlement was missing, and for a\n // spend the real ceiling and reset time rather than a guessed retry delay.\n entitlement_required: z.object({ entitlement: z.string().nullable() }),\n quota_exceeded: z.object({\n key: z.string(),\n scope: z.string(),\n window: z.string(),\n used: z.number().int(),\n reserved: z.number().int(),\n max: z.number().int(),\n remaining: z.number().int(),\n resetAt: z.string(),\n }),\n credit_insufficient: z.object({\n key: z.string(),\n balance: z.number().int(),\n reserved: z.number().int(),\n remaining: z.number().int(),\n }),\n};\n\nexport function getErrorRegistry(): Map<string, RegisteredError> {\n const g = globalThis as RegistryCarrier;\n if (!g[ERROR_REGISTRY]) {\n const m = new Map<string, RegisteredError>();\n for (const [code, status, className] of [\n [\"bad_request\", 400, \"BadRequest\"],\n [\"unauthorized\", 401, \"Unauthorized\"],\n [\"forbidden\", 403, \"Forbidden\"],\n [\"not_found\", 404, \"NotFound\"],\n [\"conflict\", 409, \"Conflict\"],\n [\"too_many_requests\", 429, \"TooManyRequests\"],\n // Thrown by the purchases decorators, so they are pre-seeded like the\n // other built-ins: a project never declares them, but every gated or\n // metered route must surface them as TYPED errors on the client.\n [\"entitlement_required\", 403, \"EntitlementRequired\"],\n [\"quota_exceeded\", 429, \"QuotaExceeded\"],\n [\"credit_insufficient\", 429, \"CreditInsufficient\"],\n ] as const) {\n const dataSchema = BUILTIN_DATA_SCHEMAS[code];\n m.set(code, {\n code,\n status,\n className,\n builtin: true,\n ...(dataSchema ? { dataSchema, dataDigest: digestOf(code, dataSchema) } : {}),\n });\n }\n g[ERROR_REGISTRY] = m;\n }\n return g[ERROR_REGISTRY];\n}\n\n/** Class returned for schema-bearing errors: ctor(data, message?). */\nexport interface DefinedErrorWithData<S extends ZodTypeAny> {\n new (data: z.input<S>, message?: string): HttpError;\n readonly code: string;\n readonly status: number;\n}\n\n/** Class returned for schema-less errors: ctor(message?). */\nexport interface DefinedError {\n new (message?: string): HttpError;\n readonly code: string;\n readonly status: number;\n}\n\n/**\n * Define a project error class. MUST be called with literal args (string\n * literal `code`, numeric literal `status`) at module top level — the deploy\n * stager's static analyzer reads those literals to resolve throw sites.\n *\n * @example\n * // models/todos/errors.ts\n * export const TodoLocked = defineError(\"todo_locked\", 409, z.object({ retryAfter: z.number() }));\n * // services/todo.service.ts\n * throw new TodoLocked({ retryAfter: 30 });\n */\nexport function defineError<S extends ZodTypeAny>(\n code: string,\n status: number,\n dataSchema: S,\n): DefinedErrorWithData<S>;\nexport function defineError(code: string, status: number): DefinedError;\nexport function defineError(code: string, status: number, dataSchema?: ZodTypeAny): unknown {\n if (!Number.isInteger(status) || status < 400 || status > 599) {\n throw new Error(\n `defineError: status for \"${code}\" must be a 4xx/5xx integer, got ${status} — ` +\n `error responses must not clobber success responses in the project spec.`,\n );\n }\n const registry = getErrorRegistry();\n const existing = registry.get(code);\n const className = defaultClassName(code);\n const dataDigest = dataSchema ? digestOf(code, dataSchema) : undefined;\n if (existing) {\n const sameShape =\n existing.status === status && !existing.builtin && existing.dataDigest === dataDigest;\n if (!sameShape) {\n throw new Error(\n `defineError: duplicate error code \"${code}\" with a different shape ` +\n `(existing: status ${existing.status}${existing.builtin ? \", built-in\" : \"\"}` +\n `${existing.status === status && !existing.builtin ? \", different data schema\" : \"\"}). ` +\n `Error codes are project-unique.`,\n );\n }\n }\n // Local factories so the class-constructor closures capture a NON-optional\n // schema binding (narrowing does not flow into closures created in a branch).\n const makeWithData = (schema: ZodTypeAny) =>\n class extends HttpError {\n static readonly code = code;\n static readonly status = status;\n constructor(data: unknown, message?: string) {\n super(status, code, message ?? humanize(code), schema.parse(data));\n this.name = className;\n }\n };\n const makeWithoutData = () =>\n class extends HttpError {\n static readonly code = code;\n static readonly status = status;\n constructor(message?: string) {\n super(status, code, message ?? humanize(code));\n this.name = className;\n }\n };\n const cls = dataSchema ? makeWithData(dataSchema) : makeWithoutData();\n Object.defineProperty(cls, \"name\", { value: className });\n registry.set(code, {\n code,\n status,\n className,\n builtin: false,\n ...(dataSchema ? { dataSchema } : {}),\n ...(dataDigest !== undefined ? { dataDigest } : {}),\n });\n return cls;\n}\n\n/** Convert a data schema through the SAME library the spec twins use and\n * return its JSON-schema digest (the duplicate-shape comparison key). Doubles\n * as the self-containment guard: a schema carrying `.openapi(refId)` metadata\n * (top-level or nested) converts to `$ref` pointers into components the\n * project spec never emits — a dangling ref every spec consumer would choke\n * on — so it is rejected here, at the definition site, with a fixable message. */\nfunction digestOf(code: string, dataSchema: ZodTypeAny): string {\n const TMP_REF = \"__PalbaseErrorDataDigest\";\n const tmpRegistry = new OpenAPIRegistry();\n // Wrap the schema as a property — exactly how the envelope embeds it — so a\n // TOP-LEVEL .openapi(refId) is not masked by the registration refId and\n // surfaces as the same $ref the real emission would produce.\n tmpRegistry.register(TMP_REF, z.object({ data: dataSchema }).openapi(TMP_REF));\n const generated = new OpenApiGeneratorV31(tmpRegistry.definitions).generateComponents();\n const out = generated.components?.schemas?.[TMP_REF];\n const digest = JSON.stringify(out ?? null);\n if (digest.includes('\"$ref\"')) {\n throw new Error(\n `defineError: dataSchema for \"${code}\" carries .openapi(refId) metadata — ` +\n `it would emit dangling $ref pointers in the project spec. ` +\n `Use a plain zod schema (z.object({...})) without .openapi(...).`,\n );\n }\n return digest;\n}\n\n/** \"todo_locked\" → \"TodoLocked\" (used as Error.name; the source class NAME the\n * analyzer sees is whatever the user assigned the const to — they should match\n * by convention, and the descriptor carries the analyzed const name anyway). */\nfunction defaultClassName(code: string): string {\n return code\n .split(\"_\")\n .map((p) => p.charAt(0).toUpperCase() + p.slice(1))\n .join(\"\");\n}\n\n/** \"todo_locked\" → \"Todo locked\" — the default human-readable message. */\nfunction humanize(code: string): string {\n const s = code.replace(/_/g, \" \");\n return s.charAt(0).toUpperCase() + s.slice(1);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TxPlanBuilder,
|
|
3
|
+
runTxPlan
|
|
4
|
+
} from "./chunk-W5ODXPY3.js";
|
|
5
|
+
|
|
6
|
+
// src/runtime.ts
|
|
7
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
8
|
+
var __requestALS = new AsyncLocalStorage();
|
|
9
|
+
var runtime = null;
|
|
10
|
+
function __setRuntime(services) {
|
|
11
|
+
runtime = services;
|
|
12
|
+
}
|
|
13
|
+
function __runWithRuntime(services, fn) {
|
|
14
|
+
return __requestALS.run({ runtime: services }, fn);
|
|
15
|
+
}
|
|
16
|
+
function __getRuntime() {
|
|
17
|
+
const scoped = __requestALS.getStore();
|
|
18
|
+
if (scoped) return scoped.runtime;
|
|
19
|
+
if (runtime === null) {
|
|
20
|
+
throw new Error(
|
|
21
|
+
"Palbase services accessed outside a request scope. The Database/Documents/\u2026 singletons are only available inside an endpoint handler (or after the runtime has called __runWithRuntime / __setRuntime)."
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
return runtime;
|
|
25
|
+
}
|
|
26
|
+
function makeServiceProxy(key) {
|
|
27
|
+
const handler = {
|
|
28
|
+
get(_target, prop, receiver) {
|
|
29
|
+
const client = __getRuntime()[key];
|
|
30
|
+
const value = Reflect.get(client, prop, receiver);
|
|
31
|
+
return typeof value === "function" ? value.bind(client) : value;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
return new Proxy({}, handler);
|
|
35
|
+
}
|
|
36
|
+
function makeTablesAccessor(ops) {
|
|
37
|
+
const tablesProxy = new Proxy(
|
|
38
|
+
{},
|
|
39
|
+
{
|
|
40
|
+
get(_t, prop) {
|
|
41
|
+
if (typeof prop !== "string") return void 0;
|
|
42
|
+
const name = prop;
|
|
43
|
+
return {
|
|
44
|
+
insert: (data) => ops().insert(name, data),
|
|
45
|
+
update: (id, data) => ops().update(name, id, data),
|
|
46
|
+
delete: (id) => ops().delete(name, id),
|
|
47
|
+
findById: (id) => ops().findById(name, id),
|
|
48
|
+
findMany: (query) => ops().findMany(name, query)
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
);
|
|
53
|
+
return tablesProxy;
|
|
54
|
+
}
|
|
55
|
+
var rawDatabase = makeServiceProxy("Database");
|
|
56
|
+
function makeTypedSurface(raw) {
|
|
57
|
+
const ops = {
|
|
58
|
+
query: (sql, params) => raw.query(sql, params),
|
|
59
|
+
insert: (table, data) => raw.insert(table, data),
|
|
60
|
+
update: (table, id, data) => raw.update(table, id, data),
|
|
61
|
+
delete: (table, id) => raw.delete(table, id),
|
|
62
|
+
findById: (table, id) => raw.findById(table, id),
|
|
63
|
+
findMany: (table, query) => raw.findMany(table, query)
|
|
64
|
+
};
|
|
65
|
+
return Object.assign(ops, {
|
|
66
|
+
tables: makeTablesAccessor(() => raw),
|
|
67
|
+
transaction(fn) {
|
|
68
|
+
const builder = new TxPlanBuilder();
|
|
69
|
+
return runTxPlan(raw, makeTxTablesAccessor(builder), builder, fn);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
function makeTxTablesAccessor(builder) {
|
|
74
|
+
const tablesProxy = new Proxy(
|
|
75
|
+
{},
|
|
76
|
+
{
|
|
77
|
+
get(_t, prop) {
|
|
78
|
+
if (typeof prop !== "string") return void 0;
|
|
79
|
+
return builder.table(prop);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
);
|
|
83
|
+
return tablesProxy;
|
|
84
|
+
}
|
|
85
|
+
var Database = Object.assign(makeTypedSurface(rawDatabase), {
|
|
86
|
+
/**
|
|
87
|
+
* Lazily resolve the runtime's service-role sibling on each call. We do NOT
|
|
88
|
+
* cache it: `rawDatabase.asService()` reads the CURRENT request scope through
|
|
89
|
+
* the runtime proxy, and the per-request runtime injects a service client
|
|
90
|
+
* bound to that request's identity headers — caching would leak one request's
|
|
91
|
+
* sibling into another concurrent request.
|
|
92
|
+
*/
|
|
93
|
+
asService() {
|
|
94
|
+
return makeTypedSurface(rawDatabase.asService());
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
var Documents = makeServiceProxy("Documents");
|
|
98
|
+
function makeBucketsAccessor(storage) {
|
|
99
|
+
return new Proxy(
|
|
100
|
+
{},
|
|
101
|
+
{
|
|
102
|
+
get(_t, prop) {
|
|
103
|
+
if (typeof prop !== "string") return void 0;
|
|
104
|
+
return storage().bucket(prop);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
var rawStorage = makeServiceProxy("Storage");
|
|
110
|
+
var Storage = Object.assign(
|
|
111
|
+
{
|
|
112
|
+
// FORWARDED explicitly, not assigned onto the service proxy.
|
|
113
|
+
//
|
|
114
|
+
// `Object.assign(rawStorage, {buckets})` writes onto the proxy's TARGET, and
|
|
115
|
+
// the proxy's only trap is `get`, which forwards every read to the module
|
|
116
|
+
// client — so the property landed somewhere nothing reads and
|
|
117
|
+
// `Storage.buckets.docs` was `undefined` in a deployed handler. It
|
|
118
|
+
// typechecked, every test passed, and the live call answered
|
|
119
|
+
// "TypeError: undefined is not an object". `Database` never had the bug
|
|
120
|
+
// because it builds a plain surface the same way this now does.
|
|
121
|
+
bucket: (name) => rawStorage.bucket(name)
|
|
122
|
+
},
|
|
123
|
+
{ buckets: makeBucketsAccessor(() => rawStorage) }
|
|
124
|
+
);
|
|
125
|
+
var Cache = makeServiceProxy("Cache");
|
|
126
|
+
var Secrets = makeServiceProxy("Secrets");
|
|
127
|
+
var Log = makeServiceProxy("Log");
|
|
128
|
+
var Notifications = makeServiceProxy("Notifications");
|
|
129
|
+
var Purchases = makeServiceProxy("Purchases");
|
|
130
|
+
var rawFlags = makeServiceProxy("Flags");
|
|
131
|
+
var Flags = Object.assign(
|
|
132
|
+
{
|
|
133
|
+
isEnabled(flagName, context) {
|
|
134
|
+
return rawFlags.isEnabled(flagName, context);
|
|
135
|
+
},
|
|
136
|
+
getVariant(flagName, context) {
|
|
137
|
+
return rawFlags.getVariant(flagName, context);
|
|
138
|
+
},
|
|
139
|
+
getAll(context) {
|
|
140
|
+
return rawFlags.getAll(context);
|
|
141
|
+
},
|
|
142
|
+
/**
|
|
143
|
+
* Resolve a flag's value, with an optional fallback.
|
|
144
|
+
*
|
|
145
|
+
* FORWARDED as of 2026-08-15. This surface is written out by hand, method
|
|
146
|
+
* by method, and `get` was missing from it — so the client implemented it,
|
|
147
|
+
* thirty assertions covered it, and `Flags.get("x")` was `undefined` in a
|
|
148
|
+
* deployed handler. Exactly the shape of the `Storage.buckets` defect found
|
|
149
|
+
* the same day: a hand-maintained forwarding list is a list somebody has to
|
|
150
|
+
* remember to update.
|
|
151
|
+
*/
|
|
152
|
+
get(flagName, defaultOrContext, maybeContext) {
|
|
153
|
+
return rawFlags.get(flagName, defaultOrContext, maybeContext);
|
|
154
|
+
},
|
|
155
|
+
setOverride(key, value) {
|
|
156
|
+
return rawFlags.setOverride(key, value);
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
{
|
|
160
|
+
/**
|
|
161
|
+
* Lazily resolve the runtime's cross-user sibling on each call. We do NOT
|
|
162
|
+
* cache it: `rawFlags.asService()` reads the CURRENT request scope through
|
|
163
|
+
* the runtime proxy, so caching would leak one request's sibling into
|
|
164
|
+
* another concurrent request. Mirrors `Database.asService()`.
|
|
165
|
+
*/
|
|
166
|
+
asService() {
|
|
167
|
+
return rawFlags.asService();
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
);
|
|
171
|
+
var Realtime = makeServiceProxy("Realtime");
|
|
172
|
+
|
|
173
|
+
export {
|
|
174
|
+
__requestALS,
|
|
175
|
+
__setRuntime,
|
|
176
|
+
__runWithRuntime,
|
|
177
|
+
__getRuntime,
|
|
178
|
+
Database,
|
|
179
|
+
Documents,
|
|
180
|
+
Storage,
|
|
181
|
+
Cache,
|
|
182
|
+
Secrets,
|
|
183
|
+
Log,
|
|
184
|
+
Notifications,
|
|
185
|
+
Purchases,
|
|
186
|
+
Flags,
|
|
187
|
+
Realtime
|
|
188
|
+
};
|
|
189
|
+
//# sourceMappingURL=chunk-POYAFBLF.js.map
|