@palbase/backend 22.0.1 → 23.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 +764 -25
- package/dist/bin/palbase-backend.cjs.map +1 -1
- package/dist/bin/palbase-backend.js +4 -4
- package/dist/{chunk-POYAFBLF.js → chunk-FSGSB42K.js} +13 -6
- package/dist/chunk-FSGSB42K.js.map +1 -0
- package/dist/{chunk-SSGAMC26.js → chunk-HAF67F2H.js} +114 -5
- package/dist/chunk-HAF67F2H.js.map +1 -0
- package/dist/{chunk-QMVK4X3V.js → chunk-OMRTHM4X.js} +98 -98
- package/dist/chunk-OMRTHM4X.js.map +1 -0
- package/dist/{chunk-QYOHMVUW.js → chunk-REZU6UKT.js} +755 -24
- package/dist/chunk-REZU6UKT.js.map +1 -0
- package/dist/{chunk-YL4C5NRY.js → chunk-Y5HXVUMP.js} +2 -2
- package/dist/{chunk-N32VDWKH.js → chunk-ZC6Q2BRD.js} +4 -59
- package/dist/chunk-ZC6Q2BRD.js.map +1 -0
- package/dist/db/env.cjs.map +1 -1
- package/dist/db/env.d.cts +3 -21
- package/dist/db/env.d.ts +3 -21
- package/dist/db/index.cjs +117 -6
- package/dist/db/index.cjs.map +1 -1
- package/dist/db/index.d.cts +3 -2
- package/dist/db/index.d.ts +3 -2
- package/dist/db/index.js +7 -3
- package/dist/{endpoint-B0LpZixz.d.cts → endpoint-BavvbW4P.d.ts} +116 -14
- package/dist/{endpoint-B0LpZixz.d.ts → endpoint-i8TTCohk.d.cts} +116 -14
- package/dist/engine/index.cjs +764 -25
- package/dist/engine/index.cjs.map +1 -1
- package/dist/engine/index.d.cts +6 -5
- package/dist/engine/index.d.ts +6 -5
- package/dist/engine/index.js +3 -3
- package/dist/{index-g-EzitI-.d.ts → index-B3jmmItD.d.ts} +203 -7
- package/dist/{index-BCNtlG1w.d.ts → index-B7YBEG5w.d.ts} +117 -93
- package/dist/{index-BGSCWlUa.d.cts → index-Bmvx1EvJ.d.cts} +203 -7
- package/dist/{index-B4W6d2VJ.d.cts → index-E7OscPJT.d.cts} +117 -93
- package/dist/index.cjs +509 -1085
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +203 -1133
- package/dist/index.d.ts +203 -1133
- package/dist/index.js +288 -900
- package/dist/index.js.map +1 -1
- package/dist/openapi/index.cjs +3 -57
- package/dist/openapi/index.cjs.map +1 -1
- package/dist/openapi/index.d.cts +3 -2
- package/dist/openapi/index.d.ts +3 -2
- package/dist/openapi/index.js +5 -22
- package/dist/openapi/index.js.map +1 -1
- package/dist/{registry-3BLYv4si.d.ts → registry-C3H2uPeZ.d.cts} +13 -169
- package/dist/{registry-Cw0YEYCg.d.cts → registry-DY3d9l1k.d.ts} +13 -169
- package/dist/{purchases/keys.cjs → stack.cjs} +4 -4
- package/dist/stack.cjs.map +1 -0
- package/dist/stack.d.cts +76 -0
- package/dist/stack.d.ts +76 -0
- package/dist/stack.js +1 -0
- package/docs/README.md +7 -6
- package/docs/endpoints.md +1 -1
- package/docs/errors.md +9 -0
- package/docs/llms-full.txt +17 -261
- package/docs/llms.txt +0 -2
- package/package.json +7 -7
- package/template/package.json +1 -1
- package/dist/chunk-N32VDWKH.js.map +0 -1
- package/dist/chunk-POYAFBLF.js.map +0 -1
- package/dist/chunk-QMVK4X3V.js.map +0 -1
- package/dist/chunk-QYOHMVUW.js.map +0 -1
- package/dist/chunk-SSGAMC26.js.map +0 -1
- package/dist/purchases/keys.cjs.map +0 -1
- package/dist/purchases/keys.d.cts +0 -42
- package/dist/purchases/keys.d.ts +0 -42
- package/dist/purchases/keys.js +0 -1
- package/docs/config.md +0 -147
- package/docs/resources.md +0 -97
- package/template/config/secrets.ts +0 -24
- /package/dist/{chunk-YL4C5NRY.js.map → chunk-Y5HXVUMP.js.map} +0 -0
- /package/dist/{purchases/keys.js.map → stack.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/error-registry.ts"],"sourcesContent":["// 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};\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 ] 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":[]}
|
package/dist/db/env.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/db/env.ts"],"sourcesContent":["/**\n * `@palbase/backend/env` — the controlled global augmentation target for the\n * project's schema.\n *\n * `Database.tables.<name>` is typed against the `Tables` interface declared\n * here. By default `Tables` is EMPTY; the generated `palbase-env.d.ts`\n * (emitted from `db/schema.ts` by {@link makeEnvDts}) augments it with one\n * member per table:\n *\n * // palbase-env.d.ts (generated — do not edit)\n * declare module \"@palbase/backend/env\" {\n * interface Tables {\n * todos: { row: {...}; insert: {...} };\n * }\n * }\n *\n * Because a `br-<ref>` pod is single-project, augmenting one global `Tables`\n * interface is safe — there is no cross-project \"leak\" to worry about (a pod\n * only ever compiles one project's schema). This is the C5 decision: typed\n * `Database.tables.*` with NO import and NO generic in handler code.\n *\n * Authors who want the row type explicitly can import it:\n *\n * import type { Tables } from \"@palbase/backend/env\";\n * type Todo = Tables[\"todos\"][\"row\"];\n */\n\n/**\n * One typed table's shapes and its position in the user-rooted graph:\n *\n * - `row` — the full row, every column present.\n * - `insert` — the write payload, required vs optional columns.\n * - `owner` — the column that foreign-keys `auth.users` (declared in\n * `db/schema.ts` with `.referencesAuthUser(...)`), or `null` when the table\n * is not user-rooted. This is what makes a seed/fixture DSL able to OMIT the\n * owner column from the author-facing type: the apply step fills it.\n * - `children` — tables that foreign-key THIS table, mapped to the FK column on\n * the child (`{ todos: \"list_id\" }`). Lets a nested seed attach children\n * without the author ever writing the FK. Self-FKs and cycle back-edges are\n * omitted by the generator so recursive types over `children` terminate.\n *\n * The generated `palbase-env.d.ts` fills `row`/`insert` with flat object types —\n * no `ColumnBuilder` phantom types ever appear in the generated output — and\n * `owner`/`children` with string literals.\n */\nexport interface TableTypes {\n row: Record<string, unknown>;\n insert: Record<string, unknown>;\n owner: string | null;\n children: Record<string, string>;\n}\n\n/**\n * The project's tables, keyed by table name. EMPTY by default; the generated\n * `palbase-env.d.ts` augments this interface (module augmentation), so\n * `Database.tables` is typed everywhere with no per-file import.\n */\n// biome-ignore lint/suspicious/noEmptyInterface: augmentation target — filled by generated palbase-env.d.ts.\nexport interface Tables {}\n\n
|
|
1
|
+
{"version":3,"sources":["../../src/db/env.ts"],"sourcesContent":["/**\n * `@palbase/backend/env` — the controlled global augmentation target for the\n * project's schema.\n *\n * `Database.tables.<name>` is typed against the `Tables` interface declared\n * here. By default `Tables` is EMPTY; the generated `palbase-env.d.ts`\n * (emitted from `db/schema.ts` by {@link makeEnvDts}) augments it with one\n * member per table:\n *\n * // palbase-env.d.ts (generated — do not edit)\n * declare module \"@palbase/backend/env\" {\n * interface Tables {\n * todos: { row: {...}; insert: {...} };\n * }\n * }\n *\n * Because a `br-<ref>` pod is single-project, augmenting one global `Tables`\n * interface is safe — there is no cross-project \"leak\" to worry about (a pod\n * only ever compiles one project's schema). This is the C5 decision: typed\n * `Database.tables.*` with NO import and NO generic in handler code.\n *\n * Authors who want the row type explicitly can import it:\n *\n * import type { Tables } from \"@palbase/backend/env\";\n * type Todo = Tables[\"todos\"][\"row\"];\n */\n\n/**\n * One typed table's shapes and its position in the user-rooted graph:\n *\n * - `row` — the full row, every column present.\n * - `insert` — the write payload, required vs optional columns.\n * - `owner` — the column that foreign-keys `auth.users` (declared in\n * `db/schema.ts` with `.referencesAuthUser(...)`), or `null` when the table\n * is not user-rooted. This is what makes a seed/fixture DSL able to OMIT the\n * owner column from the author-facing type: the apply step fills it.\n * - `children` — tables that foreign-key THIS table, mapped to the FK column on\n * the child (`{ todos: \"list_id\" }`). Lets a nested seed attach children\n * without the author ever writing the FK. Self-FKs and cycle back-edges are\n * omitted by the generator so recursive types over `children` terminate.\n *\n * The generated `palbase-env.d.ts` fills `row`/`insert` with flat object types —\n * no `ColumnBuilder` phantom types ever appear in the generated output — and\n * `owner`/`children` with string literals.\n */\nexport interface TableTypes {\n row: Record<string, unknown>;\n insert: Record<string, unknown>;\n owner: string | null;\n children: Record<string, string>;\n}\n\n/**\n * The project's tables, keyed by table name. EMPTY by default; the generated\n * `palbase-env.d.ts` augments this interface (module augmentation), so\n * `Database.tables` is typed everywhere with no per-file import.\n */\n// biome-ignore lint/suspicious/noEmptyInterface: augmentation target — filled by generated palbase-env.d.ts.\nexport interface Tables {}\n\n// BUCKETS MOVED TO `@palbase/backend/stack` (2026-08-29).\n//\n// `BucketTypes` and `Buckets` lived here because `config/storage.ts` declared\n// buckets and the deploy folded them into `palbase-env.d.ts` beside the schema's\n// tables. Buckets never came from the schema, and they do not come from a\n// declaration any more: the STACK holds them, so their types are generated into\n// `palbase-stack.d.ts` with the secrets and flags.\n//\n// Re-exported here so `Storage.buckets` keeps its one import site and no caller\n// has to learn that the source moved.\nexport type { BucketTypes, Buckets } from \"../stack.js\";\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
|
package/dist/db/env.d.cts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export { BucketTypes, Buckets } from '../stack.cjs';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* `@palbase/backend/env` — the controlled global augmentation target for the
|
|
3
5
|
* project's schema.
|
|
@@ -55,25 +57,5 @@ interface TableTypes {
|
|
|
55
57
|
*/
|
|
56
58
|
interface Tables {
|
|
57
59
|
}
|
|
58
|
-
/**
|
|
59
|
-
* One typed bucket: which renditions `config/storage.ts` declared for it.
|
|
60
|
-
*
|
|
61
|
-
* `variants` is a UNION of the declared names, so asking for one the bucket
|
|
62
|
-
* does not have is a compile error rather than a 404 somebody finds in
|
|
63
|
-
* production. The generated `palbase-env.d.ts` fills it; the default `never`
|
|
64
|
-
* means a bucket declared without variants accepts no variant name at all,
|
|
65
|
-
* which is exactly right.
|
|
66
|
-
*/
|
|
67
|
-
interface BucketTypes {
|
|
68
|
-
variants: string;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* The project's storage buckets, keyed by bucket name. EMPTY by default; the
|
|
72
|
-
* generated `palbase-env.d.ts` augments it, so `Storage.buckets` is typed
|
|
73
|
-
* everywhere with no per-file import — the same mechanism `Tables` uses, and
|
|
74
|
-
* deliberately so: an author learns one pattern.
|
|
75
|
-
*/
|
|
76
|
-
interface Buckets {
|
|
77
|
-
}
|
|
78
60
|
|
|
79
|
-
export type {
|
|
61
|
+
export type { TableTypes, Tables };
|
package/dist/db/env.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export { BucketTypes, Buckets } from '../stack.js';
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* `@palbase/backend/env` — the controlled global augmentation target for the
|
|
3
5
|
* project's schema.
|
|
@@ -55,25 +57,5 @@ interface TableTypes {
|
|
|
55
57
|
*/
|
|
56
58
|
interface Tables {
|
|
57
59
|
}
|
|
58
|
-
/**
|
|
59
|
-
* One typed bucket: which renditions `config/storage.ts` declared for it.
|
|
60
|
-
*
|
|
61
|
-
* `variants` is a UNION of the declared names, so asking for one the bucket
|
|
62
|
-
* does not have is a compile error rather than a 404 somebody finds in
|
|
63
|
-
* production. The generated `palbase-env.d.ts` fills it; the default `never`
|
|
64
|
-
* means a bucket declared without variants accepts no variant name at all,
|
|
65
|
-
* which is exactly right.
|
|
66
|
-
*/
|
|
67
|
-
interface BucketTypes {
|
|
68
|
-
variants: string;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* The project's storage buckets, keyed by bucket name. EMPTY by default; the
|
|
72
|
-
* generated `palbase-env.d.ts` augments it, so `Storage.buckets` is typed
|
|
73
|
-
* everywhere with no per-file import — the same mechanism `Tables` uses, and
|
|
74
|
-
* deliberately so: an author learns one pattern.
|
|
75
|
-
*/
|
|
76
|
-
interface Buckets {
|
|
77
|
-
}
|
|
78
60
|
|
|
79
|
-
export type {
|
|
61
|
+
export type { TableTypes, Tables };
|
package/dist/db/index.cjs
CHANGED
|
@@ -36,11 +36,13 @@ __export(db_exports, {
|
|
|
36
36
|
jsonb: () => jsonb,
|
|
37
37
|
makeTypedDB: () => makeTypedDB,
|
|
38
38
|
now: () => now,
|
|
39
|
+
openai: () => openai,
|
|
39
40
|
policy: () => policy,
|
|
40
41
|
raw: () => raw,
|
|
41
42
|
text: () => text,
|
|
42
43
|
timestamp: () => timestamp,
|
|
43
|
-
uuid: () => uuid
|
|
44
|
+
uuid: () => uuid,
|
|
45
|
+
vector: () => vector
|
|
44
46
|
});
|
|
45
47
|
module.exports = __toCommonJS(db_exports);
|
|
46
48
|
|
|
@@ -118,8 +120,82 @@ function defineSchema(input) {
|
|
|
118
120
|
if (table.raw !== void 0 && table.raw.length > 0) tableDef.raw = table.raw.slice();
|
|
119
121
|
if (table.checks !== void 0 && table.checks.length > 0) tableDef.checks = table.checks.slice();
|
|
120
122
|
if (table.indexes !== void 0 && table.indexes.length > 0) tableDef.indexes = table.indexes.slice();
|
|
123
|
+
if (table.search !== void 0) {
|
|
124
|
+
const s = table.search;
|
|
125
|
+
if (s.from !== void 0) {
|
|
126
|
+
if (s.vector !== void 0) {
|
|
127
|
+
throw new Error(
|
|
128
|
+
`table ${String(name)}: search beyan\u0131nda tek bi\xE7im kullan\u0131n \u2014 'from' (yeni) ile 'vector' (eski) birlikte olamaz`
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
if (s.from.length === 0) {
|
|
132
|
+
throw new Error(`table ${String(name)}: search.from bo\u015F olamaz`);
|
|
133
|
+
}
|
|
134
|
+
if (s.model === void 0) {
|
|
135
|
+
throw new Error(
|
|
136
|
+
`table ${String(name)}: search.from model'siz anlams\u0131z \u2014 auto-embed i\xE7in model verin (BYO i\xE7in eski 'vector' bi\xE7imini kullan\u0131n)`
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
if (Array.isArray(s.text) && s.text.length === 0) {
|
|
140
|
+
throw new Error(`table ${String(name)}: search.text bo\u015F dizi olamaz \u2014 FTS istemiyorsan text: false yaz\u0131n`);
|
|
141
|
+
}
|
|
142
|
+
} else {
|
|
143
|
+
if (typeof s.text === "boolean") {
|
|
144
|
+
throw new Error(`table ${String(name)}: text:${String(s.text)} yaln\u0131z yeni bi\xE7imde ('from' ile) ge\xE7erli`);
|
|
145
|
+
}
|
|
146
|
+
if (s.text === void 0 && s.vector === void 0) {
|
|
147
|
+
throw new Error(
|
|
148
|
+
`table ${String(name)}: search beyan\u0131 bo\u015F \u2014 en az bir kol (text ya da vector) verin, yoksa alan\u0131 hi\xE7 yazmay\u0131n`
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
if (s.text !== void 0 && s.text.length === 0) {
|
|
152
|
+
throw new Error(`table ${String(name)}: search.text bo\u015F olamaz \u2014 FTS kolu istemiyorsan alan\u0131 hi\xE7 yazma`);
|
|
153
|
+
}
|
|
154
|
+
const legs = s.vector === void 0 ? [] : Array.isArray(s.vector) ? s.vector : [s.vector];
|
|
155
|
+
for (const leg of legs) {
|
|
156
|
+
if (leg.model !== void 0 && (leg.from === void 0 || leg.from.length === 0)) {
|
|
157
|
+
throw new Error(`table ${String(name)}: search.vector.model beyan edildi ama 'from' yok \u2014 embed kayna\u011F\u0131 kolonlar zorunlu (C-2)`);
|
|
158
|
+
}
|
|
159
|
+
if (leg.model === void 0 && leg.from !== void 0) {
|
|
160
|
+
throw new Error(`table ${String(name)}: search.vector.from model'siz anlams\u0131z \u2014 auto-embed i\xE7in model verin`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
tableDef.search = s;
|
|
165
|
+
}
|
|
166
|
+
if (table.memory !== void 0) {
|
|
167
|
+
if (table.memory.from.length === 0) {
|
|
168
|
+
throw new Error(`table ${String(name)}: memory.from bo\u015F olamaz`);
|
|
169
|
+
}
|
|
170
|
+
tableDef.memory = table.memory;
|
|
171
|
+
}
|
|
121
172
|
tables[name] = tableDef;
|
|
122
173
|
}
|
|
174
|
+
for (const [name, def] of Object.entries(tables)) {
|
|
175
|
+
const m = def.memory;
|
|
176
|
+
if (m === void 0) continue;
|
|
177
|
+
const target = Object.values(tables).find((t) => t.name === m.into);
|
|
178
|
+
if (target === void 0) {
|
|
179
|
+
throw new Error(`table ${name}: memory.into "${m.into}" \u015Femada declared de\u011Fil`);
|
|
180
|
+
}
|
|
181
|
+
const subject = m.subject ?? "owner";
|
|
182
|
+
if (!(subject in def.columns)) {
|
|
183
|
+
throw new Error(`table ${name}: memory.subject "${subject}" kolonu kaynak tabloda yok`);
|
|
184
|
+
}
|
|
185
|
+
if (!(subject in target.columns)) {
|
|
186
|
+
throw new Error(`table ${name}: memory.subject "${subject}" kolonu hedef "${m.into}" tablosunda yok`);
|
|
187
|
+
}
|
|
188
|
+
const factCol = target.columns["fact"];
|
|
189
|
+
const factDef = factCol !== void 0 && "_def" in factCol ? factCol._def : factCol;
|
|
190
|
+
if (factDef === void 0 || factDef.type !== "text") {
|
|
191
|
+
throw new Error(`table ${name}: memory.into "${m.into}" tablosunda "fact" (text) kolonu zorunlu`);
|
|
192
|
+
}
|
|
193
|
+
for (const c of m.from) {
|
|
194
|
+
if (!(c in def.columns)) {
|
|
195
|
+
throw new Error(`table ${name}: memory.from kolonu "${c}" kaynak tabloda yok`);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
123
199
|
const extensions = [...new Set(input.extensions ?? [])];
|
|
124
200
|
return { tables, extensions };
|
|
125
201
|
}
|
|
@@ -137,8 +213,6 @@ var PALBASE_EXTENSIONS = [
|
|
|
137
213
|
"citext",
|
|
138
214
|
// case-insensitive text type
|
|
139
215
|
// Geospatial / location
|
|
140
|
-
"postgis",
|
|
141
|
-
// geospatial types + queries (maps, "near me")
|
|
142
216
|
"cube",
|
|
143
217
|
// multi-dimensional cubes (dependency of earthdistance)
|
|
144
218
|
"earthdistance",
|
|
@@ -153,8 +227,6 @@ var PALBASE_EXTENSIONS = [
|
|
|
153
227
|
// GiST operator classes for scalar types — needed for EXCLUDE
|
|
154
228
|
// constraints that mix "=" with a range/&& overlap (e.g. no-double-booking).
|
|
155
229
|
// Scheduling
|
|
156
|
-
"pg_cron",
|
|
157
|
-
// schedule jobs inside the database
|
|
158
230
|
// Crypto / ids (also installed by default; listable for explicitness)
|
|
159
231
|
"pgcrypto",
|
|
160
232
|
// cryptographic functions (hashing, encryption)
|
|
@@ -169,6 +241,11 @@ function isPalbaseExtension(name) {
|
|
|
169
241
|
}
|
|
170
242
|
|
|
171
243
|
// src/db/columns.ts
|
|
244
|
+
function refuseOnVector(def, modifier) {
|
|
245
|
+
if (def.type === "vector") {
|
|
246
|
+
throw new Error(`vector column: .${modifier}() is not supported (FR-002 \u2014 allowed: nullable()/notNull())`);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
172
249
|
var ColumnBuilder = class _ColumnBuilder {
|
|
173
250
|
_def;
|
|
174
251
|
constructor(type, existingDef) {
|
|
@@ -180,6 +257,7 @@ var ColumnBuilder = class _ColumnBuilder {
|
|
|
180
257
|
}
|
|
181
258
|
/** Mark this column as the primary key. */
|
|
182
259
|
primaryKey() {
|
|
260
|
+
refuseOnVector(this._def, "primaryKey");
|
|
183
261
|
this._def.primaryKey = true;
|
|
184
262
|
return new _ColumnBuilder(this._def.type, this._def);
|
|
185
263
|
}
|
|
@@ -195,16 +273,19 @@ var ColumnBuilder = class _ColumnBuilder {
|
|
|
195
273
|
}
|
|
196
274
|
/** Set a default value. */
|
|
197
275
|
default(value) {
|
|
276
|
+
refuseOnVector(this._def, "default");
|
|
198
277
|
this._def.defaultValue = value;
|
|
199
278
|
return new _ColumnBuilder(this._def.type, this._def);
|
|
200
279
|
}
|
|
201
280
|
/** UUID: generate a random default (gen_random_uuid()). */
|
|
202
281
|
defaultRandom() {
|
|
282
|
+
refuseOnVector(this._def, "defaultRandom");
|
|
203
283
|
this._def.defaultRandom = true;
|
|
204
284
|
return new _ColumnBuilder(this._def.type, this._def);
|
|
205
285
|
}
|
|
206
286
|
/** Timestamp: default to now(). */
|
|
207
287
|
defaultNow() {
|
|
288
|
+
refuseOnVector(this._def, "defaultNow");
|
|
208
289
|
this._def.defaultNow = true;
|
|
209
290
|
return new _ColumnBuilder(this._def.type, this._def);
|
|
210
291
|
}
|
|
@@ -225,6 +306,7 @@ var ColumnBuilder = class _ColumnBuilder {
|
|
|
225
306
|
return this;
|
|
226
307
|
}
|
|
227
308
|
references(table, column) {
|
|
309
|
+
refuseOnVector(this._def, "references");
|
|
228
310
|
this._def.references = { table, column };
|
|
229
311
|
return new _ColumnBuilder(this._def.type, this._def);
|
|
230
312
|
}
|
|
@@ -249,6 +331,7 @@ var ColumnBuilder = class _ColumnBuilder {
|
|
|
249
331
|
* as the real boundary; this signature is the compile-time DX mirror.
|
|
250
332
|
*/
|
|
251
333
|
referencesAuthUser(onDelete) {
|
|
334
|
+
refuseOnVector(this._def, "referencesAuthUser");
|
|
252
335
|
this._def.references = { table: "auth.users", column: "id" };
|
|
253
336
|
this._def.onDeleteAction = onDelete;
|
|
254
337
|
return new _ColumnBuilder(this._def.type, this._def);
|
|
@@ -272,6 +355,7 @@ var ColumnBuilder = class _ColumnBuilder {
|
|
|
272
355
|
* boundary; this signature is the compile-time DX mirror.
|
|
273
356
|
*/
|
|
274
357
|
referencesInstallation(onDelete) {
|
|
358
|
+
refuseOnVector(this._def, "referencesInstallation");
|
|
275
359
|
this._def.references = { table: "auth.installations", column: "id" };
|
|
276
360
|
this._def.onDeleteAction = onDelete;
|
|
277
361
|
return new _ColumnBuilder(this._def.type, this._def);
|
|
@@ -283,6 +367,7 @@ var ColumnBuilder = class _ColumnBuilder {
|
|
|
283
367
|
}
|
|
284
368
|
/** Add a single-column UNIQUE constraint. */
|
|
285
369
|
unique() {
|
|
370
|
+
refuseOnVector(this._def, "unique");
|
|
286
371
|
this._def.unique = true;
|
|
287
372
|
return new _ColumnBuilder(this._def.type, this._def);
|
|
288
373
|
}
|
|
@@ -314,12 +399,36 @@ function enumType(name, values) {
|
|
|
314
399
|
builder._def.enumValues = [...values];
|
|
315
400
|
return builder;
|
|
316
401
|
}
|
|
402
|
+
function vector(dimensions) {
|
|
403
|
+
if (!Number.isInteger(dimensions) || dimensions < 1 || dimensions > 2e3) {
|
|
404
|
+
throw new Error(`vector(): dimensions must be an integer in [1, 2000], got ${String(dimensions)}`);
|
|
405
|
+
}
|
|
406
|
+
const b = new ColumnBuilder("vector");
|
|
407
|
+
b._def.dimensions = dimensions;
|
|
408
|
+
return b;
|
|
409
|
+
}
|
|
317
410
|
|
|
318
411
|
// src/db/raw.ts
|
|
319
412
|
function raw(name, up, opts) {
|
|
320
413
|
return { name, up, ...opts?.down != null ? { down: opts.down } : {} };
|
|
321
414
|
}
|
|
322
415
|
|
|
416
|
+
// src/db/embedding.ts
|
|
417
|
+
var openai = {
|
|
418
|
+
embedding(model, opts) {
|
|
419
|
+
return {
|
|
420
|
+
provider: "openai",
|
|
421
|
+
model,
|
|
422
|
+
apiKeyName: opts?.apiKeyName ?? "OPENAI_API_KEY",
|
|
423
|
+
...opts?.dimensions !== void 0 ? { dimensions: opts.dimensions } : {},
|
|
424
|
+
...opts?.baseURL !== void 0 ? { baseURL: opts.baseURL } : {}
|
|
425
|
+
};
|
|
426
|
+
},
|
|
427
|
+
chat(model) {
|
|
428
|
+
return { provider: "openai", model };
|
|
429
|
+
}
|
|
430
|
+
};
|
|
431
|
+
|
|
323
432
|
// src/db/tx-plan.ts
|
|
324
433
|
var TxRefError = class extends Error {
|
|
325
434
|
constructor(message) {
|
|
@@ -783,10 +892,12 @@ function makeTypedDB(schema, raw2) {
|
|
|
783
892
|
jsonb,
|
|
784
893
|
makeTypedDB,
|
|
785
894
|
now,
|
|
895
|
+
openai,
|
|
786
896
|
policy,
|
|
787
897
|
raw,
|
|
788
898
|
text,
|
|
789
899
|
timestamp,
|
|
790
|
-
uuid
|
|
900
|
+
uuid,
|
|
901
|
+
vector
|
|
791
902
|
});
|
|
792
903
|
//# sourceMappingURL=index.cjs.map
|