@palbase/backend 22.1.0 → 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.
Files changed (71) hide show
  1. package/dist/bin/palbase-backend.cjs +439 -18
  2. package/dist/bin/palbase-backend.cjs.map +1 -1
  3. package/dist/bin/palbase-backend.js +4 -4
  4. package/dist/{chunk-SQC5EIWY.js → chunk-FSGSB42K.js} +11 -6
  5. package/dist/chunk-FSGSB42K.js.map +1 -0
  6. package/dist/{chunk-I3ON7MYF.js → chunk-HAF67F2H.js} +73 -16
  7. package/dist/chunk-HAF67F2H.js.map +1 -0
  8. package/dist/{chunk-QMVK4X3V.js → chunk-OMRTHM4X.js} +98 -98
  9. package/dist/chunk-OMRTHM4X.js.map +1 -0
  10. package/dist/{chunk-74XDEF5J.js → chunk-REZU6UKT.js} +432 -17
  11. package/dist/chunk-REZU6UKT.js.map +1 -0
  12. package/dist/{chunk-YL4C5NRY.js → chunk-Y5HXVUMP.js} +2 -2
  13. package/dist/{chunk-N32VDWKH.js → chunk-ZC6Q2BRD.js} +4 -59
  14. package/dist/chunk-ZC6Q2BRD.js.map +1 -0
  15. package/dist/db/env.cjs.map +1 -1
  16. package/dist/db/env.d.cts +3 -21
  17. package/dist/db/env.d.ts +3 -21
  18. package/dist/db/index.cjs +72 -15
  19. package/dist/db/index.cjs.map +1 -1
  20. package/dist/db/index.d.cts +3 -2
  21. package/dist/db/index.d.ts +3 -2
  22. package/dist/db/index.js +1 -1
  23. package/dist/{endpoint-BVT6jcVW.d.cts → endpoint-BavvbW4P.d.ts} +77 -7
  24. package/dist/{endpoint-BVT6jcVW.d.ts → endpoint-i8TTCohk.d.cts} +77 -7
  25. package/dist/engine/index.cjs +439 -18
  26. package/dist/engine/index.cjs.map +1 -1
  27. package/dist/engine/index.d.cts +6 -5
  28. package/dist/engine/index.d.ts +6 -5
  29. package/dist/engine/index.js +3 -3
  30. package/dist/{index-vwHoS0l2.d.ts → index-B3jmmItD.d.ts} +111 -5
  31. package/dist/{index-CCZqzych.d.ts → index-B7YBEG5w.d.ts} +96 -71
  32. package/dist/{index-BqCiHao8.d.cts → index-Bmvx1EvJ.d.cts} +111 -5
  33. package/dist/{index-BS1gW4nV.d.cts → index-E7OscPJT.d.cts} +96 -71
  34. package/dist/index.cjs +285 -1099
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.cts +89 -1134
  37. package/dist/index.d.ts +89 -1134
  38. package/dist/index.js +107 -902
  39. package/dist/index.js.map +1 -1
  40. package/dist/openapi/index.cjs +3 -57
  41. package/dist/openapi/index.cjs.map +1 -1
  42. package/dist/openapi/index.d.cts +3 -2
  43. package/dist/openapi/index.d.ts +3 -2
  44. package/dist/openapi/index.js +5 -22
  45. package/dist/openapi/index.js.map +1 -1
  46. package/dist/{registry-Bsuf-orT.d.ts → registry-C3H2uPeZ.d.cts} +13 -169
  47. package/dist/{registry-BWttGlaT.d.cts → registry-DY3d9l1k.d.ts} +13 -169
  48. package/dist/{purchases/keys.cjs → stack.cjs} +4 -4
  49. package/dist/stack.cjs.map +1 -0
  50. package/dist/stack.d.cts +76 -0
  51. package/dist/stack.d.ts +76 -0
  52. package/dist/stack.js +1 -0
  53. package/docs/README.md +7 -6
  54. package/docs/llms-full.txt +7 -260
  55. package/docs/llms.txt +0 -2
  56. package/package.json +6 -6
  57. package/template/package.json +1 -1
  58. package/dist/chunk-74XDEF5J.js.map +0 -1
  59. package/dist/chunk-I3ON7MYF.js.map +0 -1
  60. package/dist/chunk-N32VDWKH.js.map +0 -1
  61. package/dist/chunk-QMVK4X3V.js.map +0 -1
  62. package/dist/chunk-SQC5EIWY.js.map +0 -1
  63. package/dist/purchases/keys.cjs.map +0 -1
  64. package/dist/purchases/keys.d.cts +0 -42
  65. package/dist/purchases/keys.d.ts +0 -42
  66. package/dist/purchases/keys.js +0 -1
  67. package/docs/config.md +0 -147
  68. package/docs/resources.md +0 -97
  69. package/template/config/secrets.ts +0 -24
  70. /package/dist/{chunk-YL4C5NRY.js.map → chunk-Y5HXVUMP.js.map} +0 -0
  71. /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":[]}
@@ -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/**\n * One typed bucket: which renditions `config/storage.ts` declared for it.\n *\n * `variants` is a UNION of the declared names, so asking for one the bucket\n * does not have is a compile error rather than a 404 somebody finds in\n * production. The generated `palbase-env.d.ts` fills it; the default `never`\n * means a bucket declared without variants accepts no variant name at all,\n * which is exactly right.\n */\nexport interface BucketTypes {\n variants: string;\n}\n\n/**\n * The project's storage buckets, keyed by bucket name. EMPTY by default; the\n * generated `palbase-env.d.ts` augments it, so `Storage.buckets` is typed\n * everywhere with no per-file import the same mechanism `Tables` uses, and\n * deliberately so: an author learns one pattern.\n */\n// biome-ignore lint/suspicious/noEmptyInterface: augmentation target filled by generated palbase-env.d.ts.\nexport interface Buckets {}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
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 { BucketTypes, Buckets, TableTypes, Tables };
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 { BucketTypes, Buckets, TableTypes, Tables };
61
+ export type { TableTypes, Tables };
package/dist/db/index.cjs CHANGED
@@ -121,27 +121,81 @@ function defineSchema(input) {
121
121
  if (table.checks !== void 0 && table.checks.length > 0) tableDef.checks = table.checks.slice();
122
122
  if (table.indexes !== void 0 && table.indexes.length > 0) tableDef.indexes = table.indexes.slice();
123
123
  if (table.search !== void 0) {
124
- if (table.search.text === void 0 && table.search.vector === void 0) {
125
- throw new Error(
126
- `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`
127
- );
128
- }
129
- if (table.search.text !== void 0 && table.search.text.length === 0) {
130
- throw new Error(`table ${String(name)}: search.text bo\u015F olamaz \u2014 FTS kolu istemiyorsan alan\u0131 hi\xE7 yazma`);
131
- }
132
- const legs = table.search.vector === void 0 ? [] : Array.isArray(table.search.vector) ? table.search.vector : [table.search.vector];
133
- for (const leg of legs) {
134
- if (leg.model !== void 0 && (leg.from === void 0 || leg.from.length === 0)) {
135
- throw new Error(`table ${String(name)}: search.vector.model beyan edildi ama 'from' yok \u2014 embed kayna\u011F\u0131 kolonlar zorunlu (C-2)`);
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`);
136
145
  }
137
- if (leg.model === void 0 && leg.from !== void 0) {
138
- throw new Error(`table ${String(name)}: search.vector.from model'siz anlams\u0131z \u2014 auto-embed i\xE7in model verin`);
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`);
139
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`);
140
169
  }
141
- tableDef.search = table.search;
170
+ tableDef.memory = table.memory;
142
171
  }
143
172
  tables[name] = tableDef;
144
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
+ }
145
199
  const extensions = [...new Set(input.extensions ?? [])];
146
200
  return { tables, extensions };
147
201
  }
@@ -369,6 +423,9 @@ var openai = {
369
423
  ...opts?.dimensions !== void 0 ? { dimensions: opts.dimensions } : {},
370
424
  ...opts?.baseURL !== void 0 ? { baseURL: opts.baseURL } : {}
371
425
  };
426
+ },
427
+ chat(model) {
428
+ return { provider: "openai", model };
372
429
  }
373
430
  };
374
431