@palbase/backend 33.0.2 → 34.1.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 +2273 -358
- package/dist/bin/palbase-backend.cjs.map +1 -1
- package/dist/bin/palbase-backend.js +9 -7
- package/dist/bin/palbase-backend.js.map +1 -1
- package/dist/{chunk-YIQ4RS4F.js → chunk-5UGPXKMJ.js} +331 -5
- package/dist/chunk-5UGPXKMJ.js.map +1 -0
- package/dist/chunk-GHNC2UHS.js +20 -0
- package/dist/chunk-GHNC2UHS.js.map +1 -0
- package/dist/{chunk-AAT5G7KY.js → chunk-JJW3L62T.js} +2 -2
- package/dist/chunk-JJW3L62T.js.map +1 -0
- package/dist/{chunk-SI4KGEM3.js → chunk-P7MGSAFT.js} +102 -96
- package/dist/chunk-P7MGSAFT.js.map +1 -0
- package/dist/{chunk-XOX6RFPZ.js → chunk-PRH45NZU.js} +46 -3
- package/dist/chunk-PRH45NZU.js.map +1 -0
- package/dist/{chunk-26GH54D2.js → chunk-RZLY4BOY.js} +5 -3
- package/dist/{chunk-26GH54D2.js.map → chunk-RZLY4BOY.js.map} +1 -1
- package/dist/{chunk-KATPXCJ5.js → chunk-UMESJJRS.js} +19 -40
- package/dist/chunk-UMESJJRS.js.map +1 -0
- package/dist/chunk-VXPNPVAG.js +40 -0
- package/dist/chunk-VXPNPVAG.js.map +1 -0
- package/dist/{chunk-WWUG2QXF.js → chunk-X2UYIXDS.js} +59 -4
- package/dist/chunk-X2UYIXDS.js.map +1 -0
- package/dist/{chunk-C6COAB3E.js → chunk-XZPDPYVY.js} +1905 -195
- package/dist/chunk-XZPDPYVY.js.map +1 -0
- package/dist/{chunk-TVCCR6SO.js → chunk-YO5GYM73.js} +58 -9
- package/dist/chunk-YO5GYM73.js.map +1 -0
- package/dist/db/env.cjs.map +1 -1
- package/dist/db/env.d.cts +2 -0
- package/dist/db/env.d.ts +2 -0
- package/dist/db/index.cjs +285 -2
- package/dist/db/index.cjs.map +1 -1
- package/dist/db/index.d.cts +46 -3
- package/dist/db/index.d.ts +46 -3
- package/dist/db/index.js +14 -5
- package/dist/{index-DMZG3kpo.d.ts → endpoint-BXPdsTA5.d.ts} +370 -58
- package/dist/{index-DB_nW-AV.d.cts → endpoint-DqhUP7RT.d.cts} +370 -58
- package/dist/engine/index.cjs +2279 -356
- package/dist/engine/index.cjs.map +1 -1
- package/dist/engine/index.d.cts +5 -5
- package/dist/engine/index.d.ts +5 -5
- package/dist/engine/index.js +17 -7
- package/dist/{index-DLveQoOf.d.cts → index-Co0Hj_aO.d.cts} +108 -50
- package/dist/{index-D-3duy8Y.d.ts → index-MhCL7ll4.d.ts} +108 -50
- package/dist/index.cjs +806 -386
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -6
- package/dist/index.d.ts +7 -6
- package/dist/index.js +27 -15
- package/dist/index.js.map +1 -1
- package/dist/openapi/index.cjs +7 -0
- package/dist/openapi/index.cjs.map +1 -1
- package/dist/openapi/index.d.cts +3 -3
- package/dist/openapi/index.d.ts +3 -3
- package/dist/openapi/index.js +4 -3
- package/dist/openapi/index.js.map +1 -1
- package/dist/{registry-DSTThhKf.d.cts → registry-BkIVHPzB.d.cts} +3 -1
- package/dist/{registry-JjF5lcj4.d.ts → registry-CvGLR5j1.d.ts} +3 -1
- package/dist/test/index.cjs +103 -66
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.d.cts +3 -3
- package/dist/test/index.d.ts +3 -3
- package/dist/test/index.js +33 -7
- package/dist/test/index.js.map +1 -1
- package/docs/README.md +1 -1
- package/docs/auth.md +58 -0
- package/docs/database.md +308 -0
- package/docs/examples/check-query-contracts.ts +5 -0
- package/docs/examples/database-dx.contract.json +65 -0
- package/docs/examples/database-dx.ts +63 -0
- package/docs/examples/money-transfer.ts +73 -0
- package/docs/llms-full.txt +382 -1
- package/docs/schema.md +15 -0
- package/package.json +1 -2
- package/template/db/public.ts +12 -0
- package/template/package.json +1 -1
- package/dist/chunk-AAT5G7KY.js.map +0 -1
- package/dist/chunk-C6COAB3E.js.map +0 -1
- package/dist/chunk-KATPXCJ5.js.map +0 -1
- package/dist/chunk-SI4KGEM3.js.map +0 -1
- package/dist/chunk-TVCCR6SO.js.map +0 -1
- package/dist/chunk-WWUG2QXF.js.map +0 -1
- package/dist/chunk-XOX6RFPZ.js.map +0 -1
- package/dist/chunk-YIQ4RS4F.js.map +0 -1
- package/stack-images.json +0 -24
package/dist/db/index.cjs
CHANGED
|
@@ -27,11 +27,14 @@ __export(db_exports, {
|
|
|
27
27
|
TABLE_META: () => TABLE_META,
|
|
28
28
|
TxPlanError: () => TxPlanError,
|
|
29
29
|
TxRefError: () => TxRefError,
|
|
30
|
+
assertQueryManifest: () => assertQueryManifest,
|
|
30
31
|
bigint: () => bigint,
|
|
31
32
|
boolean: () => boolean,
|
|
32
33
|
dec: () => dec,
|
|
34
|
+
defineQuery: () => defineQuery,
|
|
33
35
|
defineSchema: () => defineSchema,
|
|
34
36
|
defineTable: () => defineTable,
|
|
37
|
+
defineWorkload: () => defineWorkload,
|
|
35
38
|
enumType: () => enumType,
|
|
36
39
|
inc: () => inc,
|
|
37
40
|
installationRef: () => installationRef,
|
|
@@ -43,6 +46,7 @@ __export(db_exports, {
|
|
|
43
46
|
openai: () => openai,
|
|
44
47
|
ownedByUser: () => ownedByUser,
|
|
45
48
|
policy: () => policy,
|
|
49
|
+
queryManifest: () => queryManifest,
|
|
46
50
|
raw: () => raw,
|
|
47
51
|
text: () => text,
|
|
48
52
|
timestamp: () => timestamp,
|
|
@@ -59,6 +63,7 @@ function qualifiedTableKey(schemaName, tableName) {
|
|
|
59
63
|
__name(qualifiedTableKey, "qualifiedTableKey");
|
|
60
64
|
|
|
61
65
|
// src/db/policy.ts
|
|
66
|
+
var import_zod = require("zod");
|
|
62
67
|
var PolicyExprRef = class _PolicyExprRef {
|
|
63
68
|
static {
|
|
64
69
|
__name(this, "PolicyExprRef");
|
|
@@ -291,6 +296,24 @@ function policy(name) {
|
|
|
291
296
|
return new PolicyBuilder(name);
|
|
292
297
|
}
|
|
293
298
|
__name(policy, "policy");
|
|
299
|
+
function reservedCanRelation(tableName, columns) {
|
|
300
|
+
if (tableName === "can") {
|
|
301
|
+
return `a table named "can" would put a relation named "can" on every parent it references \u2014 rename the table`;
|
|
302
|
+
}
|
|
303
|
+
for (const [col, b] of Object.entries(columns)) {
|
|
304
|
+
const def = b._def;
|
|
305
|
+
if (def.referencesThunk === void 0 && def.selfRefColumn === void 0) continue;
|
|
306
|
+
if (def.reverseAs === "can") {
|
|
307
|
+
return `column "${col}" names its reverse relation "can" \u2014 the parent's rows would carry a relation where can is reserved; pick another reverseAs`;
|
|
308
|
+
}
|
|
309
|
+
const forward = def.owns === true ? "owner" : def.refAs ?? (col.endsWith("_id") ? col.slice(0, -3) : col);
|
|
310
|
+
if (forward === "can") {
|
|
311
|
+
return `column "${col}" would be read as a relation named "can" (${def.refAs === void 0 ? "derived from the column name" : "its as: option"}) \u2014 can is reserved for the policy echo; rename it`;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
__name(reservedCanRelation, "reservedCanRelation");
|
|
294
317
|
|
|
295
318
|
// src/db/schema.ts
|
|
296
319
|
var IndexBuilder = class {
|
|
@@ -364,6 +387,10 @@ function defineTable(name, input) {
|
|
|
364
387
|
if (name.trim() === "") {
|
|
365
388
|
throw new Error("defineTable(name, \u2026): name must not be empty");
|
|
366
389
|
}
|
|
390
|
+
{
|
|
391
|
+
const reserved = reservedCanRelation(name, input.columns);
|
|
392
|
+
if (reserved !== null) throw new Error(`defineTable(${JSON.stringify(name)}): ${reserved}`);
|
|
393
|
+
}
|
|
367
394
|
const policies = (input.policies?.(exprCtx(Object.keys(input.columns), 0, name)) ?? []).map(toPolicyDef);
|
|
368
395
|
const rls = policies.length > 0 || input.rls !== false;
|
|
369
396
|
const owned = {};
|
|
@@ -392,6 +419,7 @@ function defineTable(name, input) {
|
|
|
392
419
|
const declaredIndexes = (input.indexes?.(exprCtx(Object.keys(input.columns), 0, name)) ?? []).map((i) => i instanceof IndexBuilder ? i._def : i);
|
|
393
420
|
if (declaredIndexes.length > 0) def.indexes = declaredIndexes;
|
|
394
421
|
if (input.appendOnly === true) def.appendOnly = true;
|
|
422
|
+
if (input.ignored === true) def.ignored = true;
|
|
395
423
|
if (input.search !== void 0) {
|
|
396
424
|
const s = input.search;
|
|
397
425
|
if (s.from !== void 0) {
|
|
@@ -451,6 +479,13 @@ function defineTable(name, input) {
|
|
|
451
479
|
}
|
|
452
480
|
__name(defineTable, "defineTable");
|
|
453
481
|
var declaringSchemaOf = /* @__PURE__ */ new WeakMap();
|
|
482
|
+
function declaredTableKey(table) {
|
|
483
|
+
const metadata = table[TABLE_META];
|
|
484
|
+
const schema = declaringSchemaOf.get(metadata);
|
|
485
|
+
if (schema === void 0) throw new Error(`defineQuery: table ${metadata.name} must belong to defineSchema before defining its query`);
|
|
486
|
+
return qualifiedTableKey(schema, metadata.name);
|
|
487
|
+
}
|
|
488
|
+
__name(declaredTableKey, "declaredTableKey");
|
|
454
489
|
function resolveReferences(tables, schemaName) {
|
|
455
490
|
for (const table of Object.values(tables)) {
|
|
456
491
|
for (const [colName, builder] of Object.entries(table.columns)) {
|
|
@@ -541,6 +576,187 @@ function defineSchema(name, input) {
|
|
|
541
576
|
}
|
|
542
577
|
__name(defineSchema, "defineSchema");
|
|
543
578
|
|
|
579
|
+
// src/db/query.ts
|
|
580
|
+
var import_zod2 = require("zod");
|
|
581
|
+
var import_zod_to_openapi = require("@asteasolutions/zod-to-openapi");
|
|
582
|
+
|
|
583
|
+
// src/db/query-contract.ts
|
|
584
|
+
function queryColumnContract(column) {
|
|
585
|
+
const def = column && typeof column === "object" && "_def" in column ? column._def : column;
|
|
586
|
+
if (!def?.type) throw new Error("Query contract requires declared column types");
|
|
587
|
+
if (def.transform !== void 0 && def.codec === void 0) throw new Error("Query contracts require a named codec instead of a free-form column transform");
|
|
588
|
+
return {
|
|
589
|
+
type: def.type,
|
|
590
|
+
nullable: def.nullable === true,
|
|
591
|
+
...def.codec === void 0 ? {} : {
|
|
592
|
+
codec: def.codec
|
|
593
|
+
},
|
|
594
|
+
...def.enumValues === void 0 ? {} : {
|
|
595
|
+
enumValues: [
|
|
596
|
+
...def.enumValues
|
|
597
|
+
]
|
|
598
|
+
},
|
|
599
|
+
...def.dimensions === void 0 ? {} : {
|
|
600
|
+
dimensions: def.dimensions
|
|
601
|
+
}
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
__name(queryColumnContract, "queryColumnContract");
|
|
605
|
+
function stableContract(value) {
|
|
606
|
+
if (Array.isArray(value)) return `[${value.map(stableContract).join(",")}]`;
|
|
607
|
+
if (value && typeof value === "object") return `{${Object.keys(value).sort().map((key) => `${JSON.stringify(key)}:${stableContract(value[key])}`).join(",")}}`;
|
|
608
|
+
return JSON.stringify(value) ?? "null";
|
|
609
|
+
}
|
|
610
|
+
__name(stableContract, "stableContract");
|
|
611
|
+
|
|
612
|
+
// src/db/query.ts
|
|
613
|
+
(0, import_zod_to_openapi.extendZodWithOpenApi)(import_zod2.z);
|
|
614
|
+
function responseColumn(column, validator) {
|
|
615
|
+
const contract = queryColumnContract(column);
|
|
616
|
+
let schema;
|
|
617
|
+
if (validator) return validator;
|
|
618
|
+
if (contract.codec === "number") schema = import_zod2.z.number().finite();
|
|
619
|
+
else if (contract.codec === "decimal") schema = import_zod2.z.string();
|
|
620
|
+
else switch (contract.type) {
|
|
621
|
+
case "integer":
|
|
622
|
+
schema = import_zod2.z.number().int().min(-2147483648).max(2147483647);
|
|
623
|
+
break;
|
|
624
|
+
case "boolean":
|
|
625
|
+
schema = import_zod2.z.boolean();
|
|
626
|
+
break;
|
|
627
|
+
case "vector":
|
|
628
|
+
schema = import_zod2.z.array(import_zod2.z.number().finite());
|
|
629
|
+
break;
|
|
630
|
+
case "enum": {
|
|
631
|
+
const values = contract.enumValues;
|
|
632
|
+
if (!values?.length) throw new Error("Query response requires declared enum values");
|
|
633
|
+
schema = import_zod2.z.enum(values);
|
|
634
|
+
break;
|
|
635
|
+
}
|
|
636
|
+
case "uuid":
|
|
637
|
+
case "text":
|
|
638
|
+
case "timestamp":
|
|
639
|
+
case "numeric":
|
|
640
|
+
case "bigint":
|
|
641
|
+
schema = import_zod2.z.string();
|
|
642
|
+
break;
|
|
643
|
+
case "jsonb":
|
|
644
|
+
throw new Error("Query response requires a fields validator for a selected jsonb column: its TypeScript payload is erased at runtime");
|
|
645
|
+
default:
|
|
646
|
+
throw new Error(`Query response does not support column type ${contract.type}`);
|
|
647
|
+
}
|
|
648
|
+
return contract.nullable ? schema.nullable() : schema;
|
|
649
|
+
}
|
|
650
|
+
__name(responseColumn, "responseColumn");
|
|
651
|
+
function defineQuery(name, options) {
|
|
652
|
+
if (typeof name !== "string" || !/^[A-Za-z][A-Za-z0-9_]{0,127}$/.test(name)) throw new Error("defineQuery requires a stable name of 1\u2013128 letters, digits or underscores");
|
|
653
|
+
const table = declaredTableKey(options.table);
|
|
654
|
+
const select = [
|
|
655
|
+
...options.select
|
|
656
|
+
];
|
|
657
|
+
if (!select.length || new Set(select).size !== select.length) throw new Error(`Query ${name}: select must be explicit, nonempty and distinct`);
|
|
658
|
+
const shape = {}, columns = {};
|
|
659
|
+
for (const key of select) {
|
|
660
|
+
if (!Object.hasOwn(options.table[TABLE_META].columns, key)) throw new Error(`Query ${name}: unknown column ${key}`);
|
|
661
|
+
columns[key] = queryColumnContract(options.table[key]);
|
|
662
|
+
shape[key] = responseColumn(options.table[key], options.fields?.[key]);
|
|
663
|
+
}
|
|
664
|
+
const response = import_zod2.z.array(import_zod2.z.object(shape).strict());
|
|
665
|
+
const queryInput = options.input, where = options.where, limit = options.limit;
|
|
666
|
+
const orderBy = options.orderBy === void 0 ? void 0 : JSON.parse(JSON.stringify(options.orderBy));
|
|
667
|
+
const prepare = /* @__PURE__ */ __name((input) => {
|
|
668
|
+
const parsed = queryInput.parse(input);
|
|
669
|
+
const filter = where === void 0 ? {} : where(parsed);
|
|
670
|
+
if (!filter || typeof filter !== "object" || Array.isArray(filter) || typeof filter.then === "function") {
|
|
671
|
+
throw new Error(`Query ${name}: where must return a synchronous filter object`);
|
|
672
|
+
}
|
|
673
|
+
const size = typeof limit === "function" ? limit(parsed) : limit;
|
|
674
|
+
if (limit !== void 0 && (typeof size !== "number" || !Number.isSafeInteger(size) || size < 0)) throw new Error(`Query ${name}: limit must produce a nonnegative safe integer`);
|
|
675
|
+
return {
|
|
676
|
+
name,
|
|
677
|
+
table,
|
|
678
|
+
columns: JSON.parse(JSON.stringify(columns)),
|
|
679
|
+
where: filter,
|
|
680
|
+
options: {
|
|
681
|
+
select: [
|
|
682
|
+
...select
|
|
683
|
+
],
|
|
684
|
+
...orderBy === void 0 ? {} : {
|
|
685
|
+
orderBy: structuredClone(orderBy)
|
|
686
|
+
},
|
|
687
|
+
...limit === void 0 ? {} : {
|
|
688
|
+
limit: size
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
};
|
|
692
|
+
}, "prepare");
|
|
693
|
+
const attach = /* @__PURE__ */ __name((schema) => {
|
|
694
|
+
const api = {
|
|
695
|
+
queryName: name,
|
|
696
|
+
input: queryInput,
|
|
697
|
+
prepare,
|
|
698
|
+
async run(database, input) {
|
|
699
|
+
const request = prepare(input);
|
|
700
|
+
const rows = await ("$readQuery" in database ? database.$readQuery(request) : database.readQuery(request));
|
|
701
|
+
return schema.parse(rows);
|
|
702
|
+
},
|
|
703
|
+
sql(database, input) {
|
|
704
|
+
const request = prepare(input);
|
|
705
|
+
return "$compileQuery" in database ? database.$compileQuery(request) : database.compileQuery(request);
|
|
706
|
+
},
|
|
707
|
+
explain(database, input) {
|
|
708
|
+
const request = prepare(input);
|
|
709
|
+
return "$explainQuery" in database ? database.$explainQuery(request) : database.explainQuery(request);
|
|
710
|
+
},
|
|
711
|
+
contract() {
|
|
712
|
+
const registry = new import_zod_to_openapi.OpenAPIRegistry();
|
|
713
|
+
registry.register(`${name}Input`, queryInput);
|
|
714
|
+
registry.register(`${name}Response`, schema);
|
|
715
|
+
return JSON.parse(JSON.stringify({
|
|
716
|
+
name,
|
|
717
|
+
table,
|
|
718
|
+
select,
|
|
719
|
+
columns,
|
|
720
|
+
components: new import_zod_to_openapi.OpenApiGeneratorV31(registry.definitions).generateComponents().components
|
|
721
|
+
}));
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
for (const key of [
|
|
725
|
+
"min",
|
|
726
|
+
"max",
|
|
727
|
+
"length",
|
|
728
|
+
"describe",
|
|
729
|
+
"openapi"
|
|
730
|
+
]) {
|
|
731
|
+
const modify = schema[key].bind(schema);
|
|
732
|
+
Object.defineProperty(schema, key, {
|
|
733
|
+
value: /* @__PURE__ */ __name((...args) => attach(modify(...args)), "value"),
|
|
734
|
+
configurable: true
|
|
735
|
+
});
|
|
736
|
+
}
|
|
737
|
+
return Object.assign(schema, api);
|
|
738
|
+
}, "attach");
|
|
739
|
+
return attach(response);
|
|
740
|
+
}
|
|
741
|
+
__name(defineQuery, "defineQuery");
|
|
742
|
+
function queryManifest(queries) {
|
|
743
|
+
const sorted = [
|
|
744
|
+
...queries
|
|
745
|
+
].sort((a, b) => a.queryName.localeCompare(b.queryName));
|
|
746
|
+
if (new Set(sorted.map((query) => query.queryName)).size !== sorted.length) throw new Error("Query manifest contains duplicate names");
|
|
747
|
+
return JSON.parse(stableContract({
|
|
748
|
+
version: 1,
|
|
749
|
+
queries: sorted.map((query) => query.contract())
|
|
750
|
+
}));
|
|
751
|
+
}
|
|
752
|
+
__name(queryManifest, "queryManifest");
|
|
753
|
+
function assertQueryManifest(queries, baseline) {
|
|
754
|
+
if (stableContract(queryManifest(queries)) !== stableContract(baseline)) {
|
|
755
|
+
throw new Error("Query contract drift: compare queryManifest() with the committed manifest and review the API/client changes before updating it");
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
__name(assertQueryManifest, "assertQueryManifest");
|
|
759
|
+
|
|
544
760
|
// src/db/extensions.ts
|
|
545
761
|
var PALBASE_EXTENSIONS = [
|
|
546
762
|
// Search & text
|
|
@@ -1506,6 +1722,13 @@ function translateRejection(err, builder) {
|
|
|
1506
1722
|
}
|
|
1507
1723
|
__name(translateRejection, "translateRejection");
|
|
1508
1724
|
|
|
1725
|
+
// src/db/sqlstate.ts
|
|
1726
|
+
var SQLSTATE = Object.freeze({
|
|
1727
|
+
serializationFailure: "40001",
|
|
1728
|
+
deadlockDetected: "40P01",
|
|
1729
|
+
commandGuardFailed: "PBC01"
|
|
1730
|
+
});
|
|
1731
|
+
|
|
1509
1732
|
// src/errors.ts
|
|
1510
1733
|
var HTTP_ERROR_BRAND = /* @__PURE__ */ Symbol.for("palbase.backend.httpError");
|
|
1511
1734
|
var HttpError = class extends Error {
|
|
@@ -1550,6 +1773,39 @@ var HttpError = class extends Error {
|
|
|
1550
1773
|
}
|
|
1551
1774
|
};
|
|
1552
1775
|
|
|
1776
|
+
// src/db/workload.ts
|
|
1777
|
+
function defineWorkload(name, options) {
|
|
1778
|
+
const result = {
|
|
1779
|
+
name,
|
|
1780
|
+
concurrency: options.concurrency,
|
|
1781
|
+
queueLimit: options.queueLimit,
|
|
1782
|
+
tenantConcurrency: options.tenantConcurrency ?? options.concurrency,
|
|
1783
|
+
tenantQueueLimit: options.tenantQueueLimit ?? options.queueLimit,
|
|
1784
|
+
queueTimeoutMs: options.queueTimeoutMs ?? 1e3,
|
|
1785
|
+
tenantClaim: options.tenantClaim ?? "tenant_id"
|
|
1786
|
+
};
|
|
1787
|
+
validateWorkload(result);
|
|
1788
|
+
return Object.freeze(result);
|
|
1789
|
+
}
|
|
1790
|
+
__name(defineWorkload, "defineWorkload");
|
|
1791
|
+
function validateWorkload(value) {
|
|
1792
|
+
if (typeof value.name !== "string" || !/^[a-zA-Z][a-zA-Z0-9_.-]{0,63}$/.test(value.name)) throw new Error("workload name must be 1\u201364 letters/digits/dots/dashes");
|
|
1793
|
+
for (const name of [
|
|
1794
|
+
"concurrency",
|
|
1795
|
+
"tenantConcurrency",
|
|
1796
|
+
"queueLimit",
|
|
1797
|
+
"tenantQueueLimit",
|
|
1798
|
+
"queueTimeoutMs"
|
|
1799
|
+
]) {
|
|
1800
|
+
const n = value[name], min = name.endsWith("Limit") ? 0 : 1;
|
|
1801
|
+
const max = name === "queueTimeoutMs" ? 6e4 : name.endsWith("Limit") ? 1e4 : 1e3;
|
|
1802
|
+
if (!Number.isInteger(n) || n < min || n > max) throw new Error(`workload ${name} must be an integer between ${min} and ${max}`);
|
|
1803
|
+
}
|
|
1804
|
+
if (value.tenantConcurrency > value.concurrency || value.tenantQueueLimit > value.queueLimit) throw new Error("workload tenant limits cannot exceed global limits");
|
|
1805
|
+
if (typeof value.tenantClaim !== "string" || !/^[a-zA-Z][a-zA-Z0-9_]{0,63}$/.test(value.tenantClaim)) throw new Error("workload tenantClaim must be a top-level claim name");
|
|
1806
|
+
}
|
|
1807
|
+
__name(validateWorkload, "validateWorkload");
|
|
1808
|
+
|
|
1553
1809
|
// src/db/typed-db.ts
|
|
1554
1810
|
function makeTypedTable(name, raw2) {
|
|
1555
1811
|
return {
|
|
@@ -1561,7 +1817,14 @@ function makeTypedTable(name, raw2) {
|
|
|
1561
1817
|
}), "put"),
|
|
1562
1818
|
update: /* @__PURE__ */ __name((q) => raw2.update(name, q.where.id, q.set), "update"),
|
|
1563
1819
|
delete: /* @__PURE__ */ __name((id) => raw2.delete(name, id), "delete"),
|
|
1820
|
+
findUnique: /* @__PURE__ */ __name((q) => raw2.findUnique(name, q.where, {
|
|
1821
|
+
select: q.select
|
|
1822
|
+
}), "findUnique"),
|
|
1564
1823
|
findById: /* @__PURE__ */ __name((id) => raw2.findById(name, id), "findById"),
|
|
1824
|
+
page: /* @__PURE__ */ __name((q) => {
|
|
1825
|
+
const { where, ...opts } = q ?? {};
|
|
1826
|
+
return raw2.page(name, where, opts);
|
|
1827
|
+
}, "page"),
|
|
1565
1828
|
findMany: /* @__PURE__ */ __name((q) => {
|
|
1566
1829
|
const { where, ...opts } = q ?? {};
|
|
1567
1830
|
return raw2.findMany(name, where, opts);
|
|
@@ -1579,17 +1842,33 @@ function makeTypedDB(schema, raw2) {
|
|
|
1579
1842
|
for (const key of Object.keys(schema.tables)) {
|
|
1580
1843
|
const tableDef = schema.tables[key];
|
|
1581
1844
|
if (tableDef !== void 0) {
|
|
1582
|
-
tables[key] = makeTypedTable(tableDef.name, raw2);
|
|
1845
|
+
tables[key] = makeTypedTable(qualifiedTableKey(schema.name, tableDef.name), raw2);
|
|
1583
1846
|
}
|
|
1584
1847
|
}
|
|
1585
1848
|
const result = {
|
|
1586
1849
|
tables,
|
|
1850
|
+
diagnostics: /* @__PURE__ */ __name(() => raw2.diagnostics(), "diagnostics"),
|
|
1851
|
+
readQuery: /* @__PURE__ */ __name((query) => raw2.readQuery(query), "readQuery"),
|
|
1852
|
+
compileQuery: /* @__PURE__ */ __name((query) => raw2.compileQuery(query), "compileQuery"),
|
|
1853
|
+
explainQuery: /* @__PURE__ */ __name((query) => raw2.explainQuery(query), "explainQuery"),
|
|
1854
|
+
command(fn, options) {
|
|
1855
|
+
const builder = new TxPlanBuilder();
|
|
1856
|
+
const planTables = Object.fromEntries(Object.entries(schema.tables).map(([key, table]) => [
|
|
1857
|
+
key,
|
|
1858
|
+
builder.table(qualifiedTableKey(schema.name, table.name))
|
|
1859
|
+
]));
|
|
1860
|
+
return runTxPlan({
|
|
1861
|
+
txPlan: /* @__PURE__ */ __name((plan) => raw2.command(plan, options), "txPlan")
|
|
1862
|
+
}, {
|
|
1863
|
+
tables: planTables
|
|
1864
|
+
}, builder, fn);
|
|
1865
|
+
},
|
|
1587
1866
|
transaction(fn) {
|
|
1588
1867
|
const builder = new TxPlanBuilder();
|
|
1589
1868
|
const planTables = {};
|
|
1590
1869
|
for (const key of Object.keys(schema.tables)) {
|
|
1591
1870
|
const tableDef = schema.tables[key];
|
|
1592
|
-
if (tableDef !== void 0) planTables[key] = builder.table(tableDef.name);
|
|
1871
|
+
if (tableDef !== void 0) planTables[key] = builder.table(qualifiedTableKey(schema.name, tableDef.name));
|
|
1593
1872
|
}
|
|
1594
1873
|
return runTxPlan(raw2, {
|
|
1595
1874
|
tables: planTables
|
|
@@ -1607,11 +1886,14 @@ __name(makeTypedDB, "makeTypedDB");
|
|
|
1607
1886
|
TABLE_META,
|
|
1608
1887
|
TxPlanError,
|
|
1609
1888
|
TxRefError,
|
|
1889
|
+
assertQueryManifest,
|
|
1610
1890
|
bigint,
|
|
1611
1891
|
boolean,
|
|
1612
1892
|
dec,
|
|
1893
|
+
defineQuery,
|
|
1613
1894
|
defineSchema,
|
|
1614
1895
|
defineTable,
|
|
1896
|
+
defineWorkload,
|
|
1615
1897
|
enumType,
|
|
1616
1898
|
inc,
|
|
1617
1899
|
installationRef,
|
|
@@ -1623,6 +1905,7 @@ __name(makeTypedDB, "makeTypedDB");
|
|
|
1623
1905
|
openai,
|
|
1624
1906
|
ownedByUser,
|
|
1625
1907
|
policy,
|
|
1908
|
+
queryManifest,
|
|
1626
1909
|
raw,
|
|
1627
1910
|
text,
|
|
1628
1911
|
timestamp,
|