@palbase/backend 30.0.0 → 32.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 +136 -3
- package/dist/bin/palbase-backend.cjs.map +1 -1
- package/dist/bin/palbase-backend.js +7 -7
- package/dist/{chunk-TWX6JTGJ.js → chunk-26GH54D2.js} +3 -5
- package/dist/{chunk-TWX6JTGJ.js.map → chunk-26GH54D2.js.map} +1 -1
- package/dist/{chunk-5J5W75A6.js → chunk-5C5UCILO.js} +27 -325
- package/dist/chunk-5C5UCILO.js.map +1 -0
- package/dist/{chunk-VN7NMDUH.js → chunk-AZJIKCOR.js} +113 -15
- package/dist/chunk-AZJIKCOR.js.map +1 -0
- package/dist/{chunk-VVMJEVQP.js → chunk-BRLJOXWS.js} +2 -2
- package/dist/{chunk-3TUJWHC2.js → chunk-IXAX5CON.js} +378 -19
- package/dist/chunk-IXAX5CON.js.map +1 -0
- package/dist/{chunk-CGNN2PUH.js → chunk-KATPXCJ5.js} +37 -4
- package/dist/{chunk-CGNN2PUH.js.map → chunk-KATPXCJ5.js.map} +1 -1
- package/dist/chunk-KGP6ALIU.js +361 -0
- package/dist/chunk-KGP6ALIU.js.map +1 -0
- package/dist/chunk-XEGZ3S2Q.js +346 -0
- package/dist/chunk-XEGZ3S2Q.js.map +1 -0
- package/dist/{chunk-ENZ2RFFJ.js → chunk-Z4CZRMNF.js} +3 -310
- package/dist/chunk-Z4CZRMNF.js.map +1 -0
- package/dist/db/index.cjs +29 -0
- package/dist/db/index.cjs.map +1 -1
- package/dist/db/index.d.cts +1 -1
- package/dist/db/index.d.ts +1 -1
- package/dist/db/index.js +12 -13
- package/dist/engine/index.cjs +136 -3
- package/dist/engine/index.cjs.map +1 -1
- package/dist/engine/index.d.cts +3 -3
- package/dist/engine/index.d.ts +3 -3
- package/dist/engine/index.js +7 -7
- package/dist/{index-b-Q3l7W5.d.ts → index-BILC9WwS.d.ts} +9 -2
- package/dist/{index-8qy3kIuA.d.ts → index-BgWnP07a.d.ts} +119 -17
- package/dist/{index-Zi7MptvP.d.cts → index-CWGiJ2Up.d.cts} +9 -2
- package/dist/{index-DtCgaZAg.d.cts → index-DgYkdNT9.d.cts} +119 -17
- package/dist/index.cjs +87 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -23
- package/dist/index.d.ts +6 -23
- package/dist/index.js +48 -48
- package/dist/index.js.map +1 -1
- package/dist/openapi/index.d.cts +2 -2
- package/dist/openapi/index.d.ts +2 -2
- package/dist/openapi/index.js +3 -4
- package/dist/openapi/index.js.map +1 -1
- package/dist/{registry-CEYLH-Iz.d.cts → registry-BcRYIQ-R.d.cts} +1 -1
- package/dist/{registry-BGJ-Al6F.d.ts → registry-ClzjxIWy.d.ts} +1 -1
- package/dist/test/index.cjs +703 -14
- package/dist/test/index.cjs.map +1 -1
- package/dist/test/index.d.cts +59 -4
- package/dist/test/index.d.ts +59 -4
- package/dist/test/index.js +51 -13
- package/dist/test/index.js.map +1 -1
- package/docs/README.md +1 -1
- package/docs/database.md +54 -0
- package/docs/llms-full.txt +87 -1
- package/docs/schema.md +32 -0
- package/package.json +1 -1
- package/template/package.json +1 -1
- package/dist/chunk-3TUJWHC2.js.map +0 -1
- package/dist/chunk-5J5W75A6.js.map +0 -1
- package/dist/chunk-ENZ2RFFJ.js.map +0 -1
- package/dist/chunk-SG4UTNOP.js +0 -374
- package/dist/chunk-SG4UTNOP.js.map +0 -1
- package/dist/chunk-VN7NMDUH.js.map +0 -1
- package/dist/chunk-VXPNPVAG.js +0 -40
- package/dist/chunk-VXPNPVAG.js.map +0 -1
- /package/dist/{chunk-VVMJEVQP.js.map → chunk-BRLJOXWS.js.map} +0 -0
package/dist/test/index.cjs
CHANGED
|
@@ -25,7 +25,8 @@ __export(test_exports, {
|
|
|
25
25
|
api: () => api,
|
|
26
26
|
createTestApi: () => createTestApi,
|
|
27
27
|
fakeDatabase: () => fakeDatabase,
|
|
28
|
-
isolated: () => isolated
|
|
28
|
+
isolated: () => isolated,
|
|
29
|
+
withServices: () => withServices
|
|
29
30
|
});
|
|
30
31
|
module.exports = __toCommonJS(test_exports);
|
|
31
32
|
|
|
@@ -53,8 +54,8 @@ var encoder = new TextEncoder();
|
|
|
53
54
|
var hasher = null;
|
|
54
55
|
async function digester() {
|
|
55
56
|
if (hasher) return hasher;
|
|
56
|
-
const
|
|
57
|
-
const nodeish =
|
|
57
|
+
const runtime2 = globalThis;
|
|
58
|
+
const nodeish = runtime2.process?.versions?.node !== void 0 || runtime2.process?.versions?.bun !== void 0;
|
|
58
59
|
if (nodeish) {
|
|
59
60
|
try {
|
|
60
61
|
const mod = await import(
|
|
@@ -350,6 +351,14 @@ __name(isolated, "isolated");
|
|
|
350
351
|
|
|
351
352
|
// src/db/input-guards.ts
|
|
352
353
|
var REF_BRAND = /* @__PURE__ */ Symbol.for("palbase.db.ref");
|
|
354
|
+
function brandRef(v, kind) {
|
|
355
|
+
Object.defineProperty(v, REF_BRAND, {
|
|
356
|
+
value: kind,
|
|
357
|
+
enumerable: false
|
|
358
|
+
});
|
|
359
|
+
return v;
|
|
360
|
+
}
|
|
361
|
+
__name(brandRef, "brandRef");
|
|
353
362
|
function brandOf(v) {
|
|
354
363
|
if (typeof v !== "object" || v === null) return void 0;
|
|
355
364
|
return Object.hasOwn(v, REF_BRAND) ? v[REF_BRAND] : void 0;
|
|
@@ -569,12 +578,130 @@ function makeRowHandle(op) {
|
|
|
569
578
|
});
|
|
570
579
|
}
|
|
571
580
|
__name(makeRowHandle, "makeRowHandle");
|
|
581
|
+
function refDescriptor(v) {
|
|
582
|
+
if (typeof v !== "object" || v === null) return null;
|
|
583
|
+
const d = v[REF];
|
|
584
|
+
return isRefDescriptor(d) ? d : null;
|
|
585
|
+
}
|
|
586
|
+
__name(refDescriptor, "refDescriptor");
|
|
587
|
+
function isRefDescriptor(d) {
|
|
588
|
+
return typeof d === "object" && d !== null && typeof d.op === "number" && typeof d.field === "string";
|
|
589
|
+
}
|
|
590
|
+
__name(isRefDescriptor, "isRefDescriptor");
|
|
591
|
+
function rowOpIndex(v) {
|
|
592
|
+
if (typeof v !== "object" || v === null) return null;
|
|
593
|
+
const op = v[ROW];
|
|
594
|
+
return typeof op === "number" ? op : null;
|
|
595
|
+
}
|
|
596
|
+
__name(rowOpIndex, "rowOpIndex");
|
|
572
597
|
function exprOf(v) {
|
|
573
598
|
if (typeof v !== "object" || v === null) return null;
|
|
574
599
|
const e = v[EXPR];
|
|
575
600
|
return typeof e === "object" && e !== null ? e : null;
|
|
576
601
|
}
|
|
577
602
|
__name(exprOf, "exprOf");
|
|
603
|
+
function isRowsHandle(v) {
|
|
604
|
+
return typeof v === "object" && v !== null && v[ROWS] !== void 0;
|
|
605
|
+
}
|
|
606
|
+
__name(isRowsHandle, "isRowsHandle");
|
|
607
|
+
function encodeValue(value, column, allowColumnExpr) {
|
|
608
|
+
const ref = refDescriptor(value);
|
|
609
|
+
if (ref) return brandRef({
|
|
610
|
+
$ref: {
|
|
611
|
+
op: ref.op,
|
|
612
|
+
field: ref.field
|
|
613
|
+
}
|
|
614
|
+
}, "ref");
|
|
615
|
+
if (isNowExpr(value)) return {
|
|
616
|
+
$expr: {
|
|
617
|
+
fn: "now"
|
|
618
|
+
}
|
|
619
|
+
};
|
|
620
|
+
const expr = exprOf(value);
|
|
621
|
+
if (expr) {
|
|
622
|
+
if (expr.fn !== "now" && !allowColumnExpr) {
|
|
623
|
+
throw new TxPlanError(`\`${column}\`: ${expr.fn}() reads the column's current value, so it is only valid in updateWhere(where, set).`);
|
|
624
|
+
}
|
|
625
|
+
return {
|
|
626
|
+
$expr: expr
|
|
627
|
+
};
|
|
628
|
+
}
|
|
629
|
+
if (rowOpIndex(value) !== null) {
|
|
630
|
+
throw new TxPlanError(`\`${column}\`: a row handle is not a value. Read the column you meant (e.g. \`row.id\`).`);
|
|
631
|
+
}
|
|
632
|
+
if (isRowsHandle(value)) {
|
|
633
|
+
throw new TxPlanError(`\`${column}\`: an operation result is not a value. Declare an expectation first (\`.expectOne(err)\`) and read a column from the row.`);
|
|
634
|
+
}
|
|
635
|
+
assertNoNestedHandles(value, column);
|
|
636
|
+
return value;
|
|
637
|
+
}
|
|
638
|
+
__name(encodeValue, "encodeValue");
|
|
639
|
+
function encodeFilterValue(value, column) {
|
|
640
|
+
const ref = refDescriptor(value);
|
|
641
|
+
if (ref) return brandRef({
|
|
642
|
+
$ref: {
|
|
643
|
+
op: ref.op,
|
|
644
|
+
field: ref.field
|
|
645
|
+
}
|
|
646
|
+
}, "ref");
|
|
647
|
+
const expr = exprOf(value);
|
|
648
|
+
if (expr) {
|
|
649
|
+
throw new TxPlanError(`\`${column}\`: ${expr.fn}() bir YAZMA ifadesi, kar\u015F\u0131la\u015Ft\u0131rma de\u011Fil \u2014 filtrede kullan\u0131lamaz. Kolonu bir de\u011Ferle ya da col() ile kar\u015F\u0131la\u015Ft\u0131r\u0131n.`);
|
|
650
|
+
}
|
|
651
|
+
if (rowOpIndex(value) !== null) {
|
|
652
|
+
throw new TxPlanError(`\`${column}\`: a row handle is not a value. Read the column you meant (e.g. \`row.id\`).`);
|
|
653
|
+
}
|
|
654
|
+
if (isRowsHandle(value)) {
|
|
655
|
+
throw new TxPlanError(`\`${column}\`: an operation result is not a value. Declare an expectation first (\`.expectOne(err)\`) and read a column from the row.`);
|
|
656
|
+
}
|
|
657
|
+
if (Array.isArray(value)) return value.map((v) => encodeFilterValue(v, column));
|
|
658
|
+
if (value !== null && typeof value === "object" && !(value instanceof Date) && !isColRef(value) && !isSqlFragment(value)) {
|
|
659
|
+
const out = {};
|
|
660
|
+
for (const [k, v] of Object.entries(value)) {
|
|
661
|
+
out[k] = encodeFilterValue(v, column);
|
|
662
|
+
}
|
|
663
|
+
return out;
|
|
664
|
+
}
|
|
665
|
+
return value;
|
|
666
|
+
}
|
|
667
|
+
__name(encodeFilterValue, "encodeFilterValue");
|
|
668
|
+
function encodeFilterMap(map) {
|
|
669
|
+
const out = {};
|
|
670
|
+
for (const key of Object.keys(map).sort()) {
|
|
671
|
+
const value = map[key];
|
|
672
|
+
if (value === void 0) continue;
|
|
673
|
+
out[key] = encodeFilterValue(value, key);
|
|
674
|
+
}
|
|
675
|
+
return out;
|
|
676
|
+
}
|
|
677
|
+
__name(encodeFilterMap, "encodeFilterMap");
|
|
678
|
+
function assertNoNestedHandles(value, column) {
|
|
679
|
+
if (typeof value !== "object" || value === null) return;
|
|
680
|
+
if (value instanceof Date) return;
|
|
681
|
+
if (refDescriptor(value) || exprOf(value) || rowOpIndex(value) !== null || isRowsHandle(value)) {
|
|
682
|
+
throw new TxPlanError(`\`${column}\`: a plan handle is nested inside a value. The server would store it as literal JSON, not resolve it. Put the reference directly in the column.`);
|
|
683
|
+
}
|
|
684
|
+
if (Array.isArray(value)) {
|
|
685
|
+
for (const item of value) assertNoNestedHandles(item, column);
|
|
686
|
+
return;
|
|
687
|
+
}
|
|
688
|
+
for (const item of Object.values(value)) {
|
|
689
|
+
assertNoNestedHandles(item, column);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
__name(assertNoNestedHandles, "assertNoNestedHandles");
|
|
693
|
+
function encodeMap(map, allowColumnExpr) {
|
|
694
|
+
const out = {};
|
|
695
|
+
for (const key of Object.keys(map).sort()) {
|
|
696
|
+
const value = map[key];
|
|
697
|
+
if (value === void 0) {
|
|
698
|
+
throw new TxPlanError(`${key} de\u011Feri undefined \u2014 bu bir yazma de\u011Feri de\u011Fil. Anahtar duruyor ama de\u011Feri yok, yani kolon sessizce YAZILMAZDI (istek g\xF6vdesinden gelen bir alan\u0131n eksik olmas\u0131 bu \u015Fekilde g\xF6r\xFCn\xFCr). Kolon varsay\u0131lan\u0131n\u0131 istiyorsan\u0131z anahtar\u0131 hi\xE7 koymay\u0131n; NULL yazmak istiyorsan\u0131z a\xE7\u0131k\xE7a null yaz\u0131n.`);
|
|
699
|
+
}
|
|
700
|
+
out[key] = encodeValue(value, key, allowColumnExpr);
|
|
701
|
+
}
|
|
702
|
+
return out;
|
|
703
|
+
}
|
|
704
|
+
__name(encodeMap, "encodeMap");
|
|
578
705
|
var SKIPPED_OP = -1;
|
|
579
706
|
var TxRowsImpl = class TxRowsImpl2 {
|
|
580
707
|
static {
|
|
@@ -631,6 +758,225 @@ function assertGuardCount(n, fn) {
|
|
|
631
758
|
}
|
|
632
759
|
}
|
|
633
760
|
__name(assertGuardCount, "assertGuardCount");
|
|
761
|
+
var MAX_OPS = 1e3;
|
|
762
|
+
var MAX_ROWS = 5e3;
|
|
763
|
+
var TxPlanBuilder = class {
|
|
764
|
+
static {
|
|
765
|
+
__name(this, "TxPlanBuilder");
|
|
766
|
+
}
|
|
767
|
+
ops = [];
|
|
768
|
+
/** Errors handed to expectations, indexed by the `slot` the server echoes. */
|
|
769
|
+
slots = [];
|
|
770
|
+
/** The table surface handed to the callback. Untyped here; the public
|
|
771
|
+
* `transaction()` signatures put the schema types on top. */
|
|
772
|
+
table(name) {
|
|
773
|
+
return {
|
|
774
|
+
insert: /* @__PURE__ */ __name((values) => {
|
|
775
|
+
const encoded = encodeMap(values, false);
|
|
776
|
+
if (Object.keys(encoded).length === 0) {
|
|
777
|
+
throw new TxPlanError(`${name}.insert() needs at least one column`);
|
|
778
|
+
}
|
|
779
|
+
return this.push({
|
|
780
|
+
op: "insert",
|
|
781
|
+
table: name,
|
|
782
|
+
values: encoded
|
|
783
|
+
}, `${name}.insert()`);
|
|
784
|
+
}, "insert"),
|
|
785
|
+
put: /* @__PURE__ */ __name((values, options) => {
|
|
786
|
+
const encoded = encodeMap(values, false);
|
|
787
|
+
if (Object.keys(encoded).length === 0) {
|
|
788
|
+
throw new TxPlanError(`${name}.put() needs at least one column`);
|
|
789
|
+
}
|
|
790
|
+
if (options.onConflict.length === 0) {
|
|
791
|
+
throw new TxPlanError(`${name}.put() needs at least one onConflict column`);
|
|
792
|
+
}
|
|
793
|
+
return this.push({
|
|
794
|
+
op: "upsert",
|
|
795
|
+
table: name,
|
|
796
|
+
values: encoded,
|
|
797
|
+
onConflict: options.onConflict
|
|
798
|
+
}, `${name}.upsert()`);
|
|
799
|
+
}, "put"),
|
|
800
|
+
insertMany: /* @__PURE__ */ __name((rows, opts) => {
|
|
801
|
+
if (rows.length === 0) {
|
|
802
|
+
return new TxRowsImpl(this, SKIPPED_OP, `${name}.insertMany()`);
|
|
803
|
+
}
|
|
804
|
+
if (rows.length > MAX_ROWS) {
|
|
805
|
+
throw new TxPlanError(`${name}.insertMany() has ${rows.length} rows; the limit is ${MAX_ROWS}. Split the write across requests.`);
|
|
806
|
+
}
|
|
807
|
+
const encoded = rows.map((row) => encodeMap(row, false));
|
|
808
|
+
assertUniformRows(encoded, name);
|
|
809
|
+
if (opts !== void 0 && opts.onConflict.length === 0) {
|
|
810
|
+
throw new TxPlanError(`${name}.insertMany() was given a conflict action with no onConflict columns. Postgres matches a collision on columns, so name them.`);
|
|
811
|
+
}
|
|
812
|
+
return this.push({
|
|
813
|
+
op: "insertMany",
|
|
814
|
+
table: name,
|
|
815
|
+
rows: encoded,
|
|
816
|
+
// Omitted entirely when no options were given, so the op a plain
|
|
817
|
+
// insertMany produces is byte-identical to the one it produced
|
|
818
|
+
// before this option existed.
|
|
819
|
+
...opts !== void 0 ? {
|
|
820
|
+
onConflict: opts.onConflict,
|
|
821
|
+
action: opts.action ?? "ignore"
|
|
822
|
+
} : {}
|
|
823
|
+
}, `${name}.insertMany()`);
|
|
824
|
+
}, "insertMany"),
|
|
825
|
+
updateWhere: /* @__PURE__ */ __name((where, set) => {
|
|
826
|
+
const encodedWhere = encodeFilterMap(where);
|
|
827
|
+
const encodedSet = encodeMap(set, true);
|
|
828
|
+
if (Object.keys(encodedWhere).length === 0) {
|
|
829
|
+
throw new TxPlanError(`${name}.updateWhere() needs a filter. An update with no filter rewrites the whole table.`);
|
|
830
|
+
}
|
|
831
|
+
if (Object.keys(encodedSet).length === 0) {
|
|
832
|
+
throw new TxPlanError(`${name}.updateWhere() needs at least one column to set`);
|
|
833
|
+
}
|
|
834
|
+
return this.push({
|
|
835
|
+
op: "update",
|
|
836
|
+
table: name,
|
|
837
|
+
set: encodedSet,
|
|
838
|
+
where: encodedWhere
|
|
839
|
+
}, `${name}.updateWhere()`);
|
|
840
|
+
}, "updateWhere"),
|
|
841
|
+
deleteWhere: /* @__PURE__ */ __name((where) => {
|
|
842
|
+
const encodedWhere = encodeFilterMap(where);
|
|
843
|
+
if (Object.keys(encodedWhere).length === 0) {
|
|
844
|
+
throw new TxPlanError(`${name}.deleteWhere() needs a filter. A delete with no filter empties the table.`);
|
|
845
|
+
}
|
|
846
|
+
return this.push({
|
|
847
|
+
op: "delete",
|
|
848
|
+
table: name,
|
|
849
|
+
where: encodedWhere
|
|
850
|
+
}, `${name}.deleteWhere()`);
|
|
851
|
+
}, "deleteWhere"),
|
|
852
|
+
select: /* @__PURE__ */ __name((where, options) => {
|
|
853
|
+
const op = {
|
|
854
|
+
op: "select",
|
|
855
|
+
table: name
|
|
856
|
+
};
|
|
857
|
+
const encodedWhere = encodeFilterMap(where ?? {});
|
|
858
|
+
if (Object.keys(encodedWhere).length > 0) op.where = encodedWhere;
|
|
859
|
+
if (options?.limit !== void 0) {
|
|
860
|
+
if (!Number.isInteger(options.limit) || options.limit < 0) {
|
|
861
|
+
throw new TxPlanError(`${name}.select(): limit needs a non-negative integer, got ${String(options.limit)}`);
|
|
862
|
+
}
|
|
863
|
+
op.limit = options.limit;
|
|
864
|
+
}
|
|
865
|
+
if (options?.lock !== void 0) op.lock = options.lock;
|
|
866
|
+
return this.push(op, `${name}.select()`);
|
|
867
|
+
}, "select")
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
push(op, what) {
|
|
871
|
+
if (this.ops.length >= MAX_OPS) {
|
|
872
|
+
throw new TxPlanError(`this transaction has ${MAX_OPS} operations, which is the limit. Use insertMany() for bulk writes, or split the work across requests.`);
|
|
873
|
+
}
|
|
874
|
+
const index = this.ops.length;
|
|
875
|
+
this.ops.push(op);
|
|
876
|
+
return new TxRowsImpl(this, index, what);
|
|
877
|
+
}
|
|
878
|
+
/** Attach an expectation to an op and record its error in the slot table. */
|
|
879
|
+
attachGuard(opIndex, kind, n, error) {
|
|
880
|
+
const op = this.ops[opIndex];
|
|
881
|
+
if (!op) throw new TxPlanError(`internal: expectation on unknown operation ${opIndex}`);
|
|
882
|
+
const slot = this.slots.length;
|
|
883
|
+
this.slots.push(error);
|
|
884
|
+
op.guard = {
|
|
885
|
+
kind,
|
|
886
|
+
n,
|
|
887
|
+
slot
|
|
888
|
+
};
|
|
889
|
+
}
|
|
890
|
+
/** The serialisable plan. Empty when the callback described no writes. */
|
|
891
|
+
body() {
|
|
892
|
+
return {
|
|
893
|
+
ops: this.ops
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
/** The error the server's `slot` selects, or `null` when it names one this
|
|
897
|
+
* plan never declared (a server/client disagreement, not a tenant error). */
|
|
898
|
+
errorForSlot(slot) {
|
|
899
|
+
return this.slots[slot] ?? null;
|
|
900
|
+
}
|
|
901
|
+
};
|
|
902
|
+
function assertUniformRows(rows, table) {
|
|
903
|
+
const first = rows[0];
|
|
904
|
+
if (!first) return;
|
|
905
|
+
const want = Object.keys(first);
|
|
906
|
+
const wantKey = want.join(",");
|
|
907
|
+
for (let i = 1; i < rows.length; i++) {
|
|
908
|
+
const got = Object.keys(rows[i]);
|
|
909
|
+
if (got.join(",") !== wantKey) {
|
|
910
|
+
throw new TxPlanError(`${table}.insertMany(): every row must set the same columns. Row 0 sets [${want.join(", ")}] but row ${i} sets [${got.join(", ")}]. (A property set to \`undefined\` counts as absent \u2014 use \`null\`.)`);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
__name(assertUniformRows, "assertUniformRows");
|
|
915
|
+
function materializeResult(value, results) {
|
|
916
|
+
const ref = refDescriptor(value);
|
|
917
|
+
if (ref) {
|
|
918
|
+
const row = rowOf(results, ref.op, `\`${ref.field}\``);
|
|
919
|
+
if (!(ref.field in row)) {
|
|
920
|
+
throw new TxPlanError(`the transaction's operation ${ref.op} returned no column \`${ref.field}\`.`);
|
|
921
|
+
}
|
|
922
|
+
return row[ref.field];
|
|
923
|
+
}
|
|
924
|
+
const rowOp = rowOpIndex(value);
|
|
925
|
+
if (rowOp !== null) return rowOf(results, rowOp, "a row");
|
|
926
|
+
if (isRowsHandle(value)) {
|
|
927
|
+
throw new TxPlanError("an operation result cannot be returned from a transaction callback: its row count is not known until the plan runs. Declare an expectation (`.expectOne(err)`) and return the row, or a column of it.");
|
|
928
|
+
}
|
|
929
|
+
if (Array.isArray(value)) return value.map((item) => materializeResult(item, results));
|
|
930
|
+
if (isPlainObject(value)) {
|
|
931
|
+
const out = {};
|
|
932
|
+
for (const [key, item] of Object.entries(value)) out[key] = materializeResult(item, results);
|
|
933
|
+
return out;
|
|
934
|
+
}
|
|
935
|
+
return value;
|
|
936
|
+
}
|
|
937
|
+
__name(materializeResult, "materializeResult");
|
|
938
|
+
function rowOf(results, opIndex, what) {
|
|
939
|
+
const result = results[opIndex];
|
|
940
|
+
if (!result) {
|
|
941
|
+
throw new TxPlanError(`the transaction returned no result for operation ${opIndex}, so ${what} cannot be read.`);
|
|
942
|
+
}
|
|
943
|
+
const row = result.rows[0];
|
|
944
|
+
if (!row) {
|
|
945
|
+
throw new TxPlanError(`the transaction's operation ${opIndex} returned no row, so ${what} cannot be read.`);
|
|
946
|
+
}
|
|
947
|
+
return row;
|
|
948
|
+
}
|
|
949
|
+
__name(rowOf, "rowOf");
|
|
950
|
+
function isPlainObject(value) {
|
|
951
|
+
if (typeof value !== "object" || value === null) return false;
|
|
952
|
+
const proto = Object.getPrototypeOf(value);
|
|
953
|
+
return proto === Object.prototype || proto === null;
|
|
954
|
+
}
|
|
955
|
+
__name(isPlainObject, "isPlainObject");
|
|
956
|
+
async function runTxPlan(transport, handle, builder, fn) {
|
|
957
|
+
const returned = fn(handle);
|
|
958
|
+
const body = builder.body();
|
|
959
|
+
if (body.ops.length === 0) {
|
|
960
|
+
return materializeResult(returned, []);
|
|
961
|
+
}
|
|
962
|
+
let response;
|
|
963
|
+
try {
|
|
964
|
+
response = await transport.txPlan(body);
|
|
965
|
+
} catch (err) {
|
|
966
|
+
throw translateRejection(err, builder);
|
|
967
|
+
}
|
|
968
|
+
return materializeResult(returned, response.results);
|
|
969
|
+
}
|
|
970
|
+
__name(runTxPlan, "runTxPlan");
|
|
971
|
+
function translateRejection(err, builder) {
|
|
972
|
+
if (typeof err !== "object" || err === null) return err;
|
|
973
|
+
const rejection = err;
|
|
974
|
+
if (rejection.error_code !== "tx_guard_failed" || typeof rejection.slot !== "number") {
|
|
975
|
+
return err;
|
|
976
|
+
}
|
|
977
|
+
return builder.errorForSlot(rejection.slot) ?? err;
|
|
978
|
+
}
|
|
979
|
+
__name(translateRejection, "translateRejection");
|
|
634
980
|
|
|
635
981
|
// src/__tests__/helpers/mock-db.ts
|
|
636
982
|
function refuseFragment(caller, table, where) {
|
|
@@ -925,7 +1271,7 @@ function createMockDB() {
|
|
|
925
1271
|
const vals = bucket.map((r) => r[c]).filter((v) => v !== null && v !== void 0);
|
|
926
1272
|
(out["avg"] ??= {})[c] = vals.length === 0 ? null : String(Number(vals.reduce((a, v) => addDecimal(a, v, 1), "0")) / vals.length);
|
|
927
1273
|
}
|
|
928
|
-
for (const [fn,
|
|
1274
|
+
for (const [fn, pick2] of [
|
|
929
1275
|
[
|
|
930
1276
|
"min",
|
|
931
1277
|
-1
|
|
@@ -937,7 +1283,7 @@ function createMockDB() {
|
|
|
937
1283
|
]) {
|
|
938
1284
|
for (const c of q[fn] ?? []) {
|
|
939
1285
|
const vals = bucket.map((r) => r[c]).filter((v) => v !== null && v !== void 0);
|
|
940
|
-
(out[fn] ??= {})[c] = vals.length === 0 ? null : vals.reduce((a, v) => cmp(v, a,
|
|
1286
|
+
(out[fn] ??= {})[c] = vals.length === 0 ? null : vals.reduce((a, v) => cmp(v, a, pick2 === 1 ? "gt" : "lt") ? v : a);
|
|
941
1287
|
}
|
|
942
1288
|
}
|
|
943
1289
|
if (q.count === true) out["count"] = bucket.length;
|
|
@@ -1086,16 +1432,12 @@ function createMockDB() {
|
|
|
1086
1432
|
applied[k] = v;
|
|
1087
1433
|
}
|
|
1088
1434
|
assertNoExpressionHandles("update", table, Object.keys(applied), applied);
|
|
1089
|
-
|
|
1435
|
+
if (idx < 0) return null;
|
|
1436
|
+
const updated = {
|
|
1090
1437
|
...rows[idx],
|
|
1091
1438
|
...applied
|
|
1092
|
-
} : {
|
|
1093
|
-
id,
|
|
1094
|
-
...applied
|
|
1095
1439
|
};
|
|
1096
|
-
|
|
1097
|
-
rows[idx] = updated;
|
|
1098
|
-
}
|
|
1440
|
+
rows[idx] = updated;
|
|
1099
1441
|
track(tracked.updated, table, updated);
|
|
1100
1442
|
return updated;
|
|
1101
1443
|
},
|
|
@@ -1120,6 +1462,9 @@ function createMockDB() {
|
|
|
1120
1462
|
async findMany(table, query, opts) {
|
|
1121
1463
|
assertUsableFilter("findMany", table, query);
|
|
1122
1464
|
refuseFragment("findMany", table, query);
|
|
1465
|
+
if (opts?.with !== void 0 && Object.keys(opts.with).length > 0) {
|
|
1466
|
+
throw new Error(`findMany(${table}): fakeDatabase \`with\` desteklemiyor \u2014 ili\u015Fki grafi\u011Fi yabanc\u0131 anahtarlardan T\xDCRET\u0130L\u0130YOR ve fake \u015Femay\u0131 okumuyor. \u0130li\u015Fkili sat\u0131rlar\u0131 \xF6l\xE7en bir test ger\xE7ek motoru kullanmal\u0131; fake ile \xF6l\xE7mek istiyorsan\u0131z ilgili sat\u0131rlar\u0131 \`seed\` ile ayr\u0131 koyup ayr\u0131 sorgulay\u0131n.`);
|
|
1467
|
+
}
|
|
1123
1468
|
const rows = store.get(table) ?? [];
|
|
1124
1469
|
let out = query ? rows.filter((row) => rowMatchesFilter("findMany", table, row, query)) : [
|
|
1125
1470
|
...rows
|
|
@@ -1401,6 +1746,315 @@ function txRejection(status, code, extra) {
|
|
|
1401
1746
|
}
|
|
1402
1747
|
__name(txRejection, "txRejection");
|
|
1403
1748
|
|
|
1749
|
+
// src/runtime.ts
|
|
1750
|
+
var import_node_async_hooks = require("async_hooks");
|
|
1751
|
+
|
|
1752
|
+
// src/errors.ts
|
|
1753
|
+
var HTTP_ERROR_BRAND = /* @__PURE__ */ Symbol.for("palbase.backend.httpError");
|
|
1754
|
+
var HttpError = class extends Error {
|
|
1755
|
+
static {
|
|
1756
|
+
__name(this, "HttpError");
|
|
1757
|
+
}
|
|
1758
|
+
status;
|
|
1759
|
+
error;
|
|
1760
|
+
errorDescription;
|
|
1761
|
+
data;
|
|
1762
|
+
/** See {@link HTTP_ERROR_BRAND} — how the engine recognises this across SDK copies. */
|
|
1763
|
+
[HTTP_ERROR_BRAND] = true;
|
|
1764
|
+
constructor(status, error, errorDescription, data) {
|
|
1765
|
+
super(errorDescription);
|
|
1766
|
+
this.name = "HttpError";
|
|
1767
|
+
this.status = status;
|
|
1768
|
+
this.error = error;
|
|
1769
|
+
this.errorDescription = errorDescription;
|
|
1770
|
+
if (data !== void 0) {
|
|
1771
|
+
this.data = data;
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
/**
|
|
1775
|
+
* Serialize to the standard Palbase error response format.
|
|
1776
|
+
* The `requestId` is injected by the runtime layer from the request context.
|
|
1777
|
+
* When called without arguments (e.g. JSON.stringify), request_id is omitted.
|
|
1778
|
+
* When `data` is set, it is appended as a strict-superset field.
|
|
1779
|
+
*/
|
|
1780
|
+
toJSON(requestId) {
|
|
1781
|
+
const result = {
|
|
1782
|
+
error: this.error,
|
|
1783
|
+
error_description: this.errorDescription,
|
|
1784
|
+
status: this.status
|
|
1785
|
+
};
|
|
1786
|
+
if (requestId) {
|
|
1787
|
+
result.request_id = requestId;
|
|
1788
|
+
}
|
|
1789
|
+
if (this.data !== void 0) {
|
|
1790
|
+
result.data = this.data;
|
|
1791
|
+
}
|
|
1792
|
+
return result;
|
|
1793
|
+
}
|
|
1794
|
+
};
|
|
1795
|
+
function isRetryable(e) {
|
|
1796
|
+
return typeof e === "object" && e !== null && e.retryable === true && typeof e.sqlstate === "string";
|
|
1797
|
+
}
|
|
1798
|
+
__name(isRetryable, "isRetryable");
|
|
1799
|
+
|
|
1800
|
+
// src/db/typed-db.ts
|
|
1801
|
+
async function withRetry(fn, opts = {}) {
|
|
1802
|
+
const budget = opts.retry ?? 0;
|
|
1803
|
+
let attempt = 0;
|
|
1804
|
+
for (; ; ) {
|
|
1805
|
+
try {
|
|
1806
|
+
return await fn();
|
|
1807
|
+
} catch (e) {
|
|
1808
|
+
if (attempt >= budget || !isRetryable(e)) throw e;
|
|
1809
|
+
attempt += 1;
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
}
|
|
1813
|
+
__name(withRetry, "withRetry");
|
|
1814
|
+
|
|
1815
|
+
// src/db/schema-json.ts
|
|
1816
|
+
function qualifiedTableKey(schemaName, tableName) {
|
|
1817
|
+
return schemaName === "" || schemaName === "public" ? tableName : `${schemaName}.${tableName}`;
|
|
1818
|
+
}
|
|
1819
|
+
__name(qualifiedTableKey, "qualifiedTableKey");
|
|
1820
|
+
|
|
1821
|
+
// src/runtime.ts
|
|
1822
|
+
var REQUEST_ALS = /* @__PURE__ */ Symbol.for("palbase.backend.requestALS");
|
|
1823
|
+
var __requestALS = (() => {
|
|
1824
|
+
const g = globalThis;
|
|
1825
|
+
return g[REQUEST_ALS] ??= new import_node_async_hooks.AsyncLocalStorage();
|
|
1826
|
+
})();
|
|
1827
|
+
var runtime = null;
|
|
1828
|
+
function __getRuntime() {
|
|
1829
|
+
const scoped = __requestALS.getStore();
|
|
1830
|
+
if (scoped) return scoped.runtime;
|
|
1831
|
+
if (runtime === null) {
|
|
1832
|
+
throw new Error("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).");
|
|
1833
|
+
}
|
|
1834
|
+
return runtime;
|
|
1835
|
+
}
|
|
1836
|
+
__name(__getRuntime, "__getRuntime");
|
|
1837
|
+
function makeServiceProxy(key) {
|
|
1838
|
+
const handler = {
|
|
1839
|
+
get(_target, prop, receiver) {
|
|
1840
|
+
const client = __getRuntime()[key];
|
|
1841
|
+
const value = Reflect.get(client, prop, receiver);
|
|
1842
|
+
return typeof value === "function" ? value.bind(client) : value;
|
|
1843
|
+
}
|
|
1844
|
+
};
|
|
1845
|
+
return new Proxy({}, handler);
|
|
1846
|
+
}
|
|
1847
|
+
__name(makeServiceProxy, "makeServiceProxy");
|
|
1848
|
+
function makeTableProxy(ops, prefix) {
|
|
1849
|
+
return new Proxy({}, {
|
|
1850
|
+
get(_t, prop) {
|
|
1851
|
+
if (typeof prop !== "string") return void 0;
|
|
1852
|
+
const name = `${prefix}${prop}`;
|
|
1853
|
+
return {
|
|
1854
|
+
insert: /* @__PURE__ */ __name((data) => ops().insert(name, data), "insert"),
|
|
1855
|
+
// DÖRDÜNCÜ FİİL, aynı gerekçeyle: tip söz veriyor, ops katmanı
|
|
1856
|
+
// uyguluyor, ve bir handler'ın gerçekten dokunduğu yer BURASI.
|
|
1857
|
+
// `runtime-table-verbs` kapısı bunu adıyla saydı.
|
|
1858
|
+
aggregate: /* @__PURE__ */ __name((q) => ops().aggregate(name, q), "aggregate"),
|
|
1859
|
+
insertMany: /* @__PURE__ */ __name((rows, opts) => ops().insertMany(name, rows, opts), "insertMany"),
|
|
1860
|
+
update: /* @__PURE__ */ __name((q) => ops().update(name, q.where.id, q.set), "update"),
|
|
1861
|
+
delete: /* @__PURE__ */ __name((id) => ops().delete(name, id), "delete"),
|
|
1862
|
+
findById: /* @__PURE__ */ __name((id) => ops().findById(name, id), "findById"),
|
|
1863
|
+
findMany: /* @__PURE__ */ __name((q) => {
|
|
1864
|
+
const { where, ...opts } = q ?? {};
|
|
1865
|
+
return ops().findMany(name, where, opts);
|
|
1866
|
+
}, "findMany"),
|
|
1867
|
+
put: /* @__PURE__ */ __name((q) => ops().put(name, q.data, {
|
|
1868
|
+
onConflict: q.onConflict
|
|
1869
|
+
}), "put"),
|
|
1870
|
+
// THREE VERBS THE TYPE PROMISED AND THIS PROXY DID NOT EMIT.
|
|
1871
|
+
//
|
|
1872
|
+
// `EnvTypedTableBase` declares `updateMany`, `deleteMany` and `count`
|
|
1873
|
+
// (typed-db.ts) and the ops layer implements all three — only this
|
|
1874
|
+
// proxy, which is what a handler actually touches, left them out. So
|
|
1875
|
+
// the type said the verb exists, autocomplete offered it, and the call
|
|
1876
|
+
// answered `undefined is not a function`.
|
|
1877
|
+
//
|
|
1878
|
+
// Older than this run, but the run rewrote this proxy for
|
|
1879
|
+
// `Database.schema(name).tables.*` and would have carried the gap onto
|
|
1880
|
+
// the new surface too.
|
|
1881
|
+
updateMany: /* @__PURE__ */ __name((q) => ops().updateMany(name, q.where, q.set), "updateMany"),
|
|
1882
|
+
deleteMany: /* @__PURE__ */ __name((q) => ops().deleteMany(name, q.where), "deleteMany"),
|
|
1883
|
+
count: /* @__PURE__ */ __name((q) => ops().count(name, q?.where), "count"),
|
|
1884
|
+
search: /* @__PURE__ */ __name((params) => ops().search(name, params), "search"),
|
|
1885
|
+
similar: /* @__PURE__ */ __name((id, params) => ops().similar(name, id, params), "similar"),
|
|
1886
|
+
recommend: /* @__PURE__ */ __name((params) => ops().recommend(name, params), "recommend"),
|
|
1887
|
+
facets: /* @__PURE__ */ __name((params) => ops().facets(name, params), "facets"),
|
|
1888
|
+
supersede: /* @__PURE__ */ __name((id, row) => ops().supersede(name, id, row), "supersede"),
|
|
1889
|
+
claim: /* @__PURE__ */ __name((unique, extra) => ops().claim(name, unique, extra), "claim")
|
|
1890
|
+
};
|
|
1891
|
+
}
|
|
1892
|
+
});
|
|
1893
|
+
}
|
|
1894
|
+
__name(makeTableProxy, "makeTableProxy");
|
|
1895
|
+
var rawDatabase = makeServiceProxy("Database");
|
|
1896
|
+
function makeTypedSurface(raw) {
|
|
1897
|
+
const reco = raw;
|
|
1898
|
+
const ops = {
|
|
1899
|
+
$query: /* @__PURE__ */ __name((sql, params) => raw.query(sql, params), "$query"),
|
|
1900
|
+
$insert: /* @__PURE__ */ __name((table, data) => raw.insert(table, data), "$insert"),
|
|
1901
|
+
$update: /* @__PURE__ */ __name((table, id, data) => raw.update(table, id, data), "$update"),
|
|
1902
|
+
$delete: /* @__PURE__ */ __name((table, id) => raw.delete(table, id), "$delete"),
|
|
1903
|
+
$findById: /* @__PURE__ */ __name((table, id) => raw.findById(table, id), "$findById"),
|
|
1904
|
+
$findMany: /* @__PURE__ */ __name((table, query, opts) => raw.findMany(table, query, opts), "$findMany"),
|
|
1905
|
+
$put: /* @__PURE__ */ __name((table, data, opts) => raw.put(table, data, opts), "$put"),
|
|
1906
|
+
$updateMany: /* @__PURE__ */ __name((table, where, set) => raw.updateMany(table, where, set), "$updateMany"),
|
|
1907
|
+
$deleteMany: /* @__PURE__ */ __name((table, where) => raw.deleteMany(table, where), "$deleteMany"),
|
|
1908
|
+
$count: /* @__PURE__ */ __name((table, where) => raw.count(table, where), "$count"),
|
|
1909
|
+
$search: /* @__PURE__ */ __name((table, params) => raw.search(table, params), "$search"),
|
|
1910
|
+
$similar: /* @__PURE__ */ __name((table, id, params) => reco.similar(table, id, params), "$similar"),
|
|
1911
|
+
$recommend: /* @__PURE__ */ __name((table, params) => reco.recommend(table, params), "$recommend"),
|
|
1912
|
+
$facets: /* @__PURE__ */ __name((table, params) => reco.facets(table, params), "$facets"),
|
|
1913
|
+
$claim: /* @__PURE__ */ __name((table, unique, extra) => reco.claim(table, unique, extra), "$claim"),
|
|
1914
|
+
$lockRows: /* @__PURE__ */ __name((table, ids) => reco.lockRows(table, ids), "$lockRows"),
|
|
1915
|
+
$advisoryXactLock: /* @__PURE__ */ __name((key) => reco.advisoryXactLock(key), "$advisoryXactLock"),
|
|
1916
|
+
$aggregate: /* @__PURE__ */ __name((table, q) => raw.aggregate(table, q), "$aggregate"),
|
|
1917
|
+
$insertMany: /* @__PURE__ */ __name((table, rows, opts) => raw.insertMany(table, rows, opts), "$insertMany"),
|
|
1918
|
+
$supersede: /* @__PURE__ */ __name((table, id, row) => raw.supersede(table, id, row), "$supersede")
|
|
1919
|
+
};
|
|
1920
|
+
const base = Object.assign(ops, {
|
|
1921
|
+
// Both surfaces get it: a savepoint on the service transaction is as useful
|
|
1922
|
+
// as one on the request's, and each is bound to its own connection.
|
|
1923
|
+
$attempt: /* @__PURE__ */ __name((fn) => raw.attempt(fn), "$attempt"),
|
|
1924
|
+
$transaction(fn, opts) {
|
|
1925
|
+
return withRetry(() => {
|
|
1926
|
+
const builder = new TxPlanBuilder();
|
|
1927
|
+
return runTxPlan(raw, makeTxPlanHandle(builder), builder, fn);
|
|
1928
|
+
}, opts ?? {});
|
|
1929
|
+
}
|
|
1930
|
+
});
|
|
1931
|
+
return new Proxy(base, {
|
|
1932
|
+
get(target, prop, receiver) {
|
|
1933
|
+
if (prop === "tables") return makeTableProxy(() => reco, "");
|
|
1934
|
+
if (typeof prop === "string" && !prop.startsWith("$") && !(prop in target)) {
|
|
1935
|
+
return makeTableProxy(() => reco, qualifiedTableKey(prop, ""));
|
|
1936
|
+
}
|
|
1937
|
+
return Reflect.get(target, prop, receiver);
|
|
1938
|
+
}
|
|
1939
|
+
});
|
|
1940
|
+
}
|
|
1941
|
+
__name(makeTypedSurface, "makeTypedSurface");
|
|
1942
|
+
function makeTxTablesAccessor(builder, prefix = "") {
|
|
1943
|
+
const tablesProxy = new Proxy({}, {
|
|
1944
|
+
get(_t, prop) {
|
|
1945
|
+
if (typeof prop !== "string") return void 0;
|
|
1946
|
+
return builder.table(prefix + prop);
|
|
1947
|
+
}
|
|
1948
|
+
});
|
|
1949
|
+
return tablesProxy;
|
|
1950
|
+
}
|
|
1951
|
+
__name(makeTxTablesAccessor, "makeTxTablesAccessor");
|
|
1952
|
+
function makeTxPlanHandle(builder) {
|
|
1953
|
+
const publicTables = makeTxTablesAccessor(builder);
|
|
1954
|
+
return new Proxy({}, {
|
|
1955
|
+
get(_t, prop) {
|
|
1956
|
+
if (typeof prop !== "string") return void 0;
|
|
1957
|
+
if (prop === "tables") return publicTables;
|
|
1958
|
+
return makeTxTablesAccessor(builder, qualifiedTableKey(prop, ""));
|
|
1959
|
+
}
|
|
1960
|
+
});
|
|
1961
|
+
}
|
|
1962
|
+
__name(makeTxPlanHandle, "makeTxPlanHandle");
|
|
1963
|
+
var Database = Object.assign(makeTypedSurface(rawDatabase), {
|
|
1964
|
+
/**
|
|
1965
|
+
* Lazily resolve the runtime's service-role sibling on each call. We do NOT
|
|
1966
|
+
* cache it: `rawDatabase.asService()` reads the CURRENT request scope through
|
|
1967
|
+
* the runtime proxy, and the per-request runtime injects a service client
|
|
1968
|
+
* bound to that request's identity headers — caching would leak one request's
|
|
1969
|
+
* sibling into another concurrent request.
|
|
1970
|
+
*/
|
|
1971
|
+
$asService() {
|
|
1972
|
+
return makeTypedSurface(rawDatabase.asService());
|
|
1973
|
+
}
|
|
1974
|
+
});
|
|
1975
|
+
var Documents = makeServiceProxy("Documents");
|
|
1976
|
+
function makeBucketsAccessor(storage) {
|
|
1977
|
+
return new Proxy({}, {
|
|
1978
|
+
get(_t, prop) {
|
|
1979
|
+
if (typeof prop !== "string") return void 0;
|
|
1980
|
+
return storage().bucket(prop);
|
|
1981
|
+
}
|
|
1982
|
+
});
|
|
1983
|
+
}
|
|
1984
|
+
__name(makeBucketsAccessor, "makeBucketsAccessor");
|
|
1985
|
+
var rawStorage = makeServiceProxy("Storage");
|
|
1986
|
+
var Storage = Object.assign({
|
|
1987
|
+
// FORWARDED explicitly, not assigned onto the service proxy.
|
|
1988
|
+
//
|
|
1989
|
+
// `Object.assign(rawStorage, {buckets})` writes onto the proxy's TARGET, and
|
|
1990
|
+
// the proxy's only trap is `get`, which forwards every read to the module
|
|
1991
|
+
// client — so the property landed somewhere nothing reads and
|
|
1992
|
+
// `Storage.buckets.docs` was `undefined` in a deployed handler. It
|
|
1993
|
+
// typechecked, every test passed, and the live call answered
|
|
1994
|
+
// "TypeError: undefined is not an object". `Database` never had the bug
|
|
1995
|
+
// because it builds a plain surface the same way this now does.
|
|
1996
|
+
bucket: /* @__PURE__ */ __name((name) => rawStorage.bucket(name), "bucket")
|
|
1997
|
+
}, {
|
|
1998
|
+
buckets: makeBucketsAccessor(() => rawStorage)
|
|
1999
|
+
});
|
|
2000
|
+
var Cache = makeServiceProxy("Cache");
|
|
2001
|
+
var Secrets = makeServiceProxy("Secrets");
|
|
2002
|
+
var Auth = makeServiceProxy("Auth");
|
|
2003
|
+
var Log = makeServiceProxy("Log");
|
|
2004
|
+
var Notifications = makeServiceProxy("Notifications");
|
|
2005
|
+
var rawFlags = makeServiceProxy("Flags");
|
|
2006
|
+
var Flags = Object.assign({
|
|
2007
|
+
isEnabled(flagName, context) {
|
|
2008
|
+
return rawFlags.isEnabled(flagName, context);
|
|
2009
|
+
},
|
|
2010
|
+
getVariant(flagName, context) {
|
|
2011
|
+
return rawFlags.getVariant(flagName, context);
|
|
2012
|
+
},
|
|
2013
|
+
getAll(context) {
|
|
2014
|
+
return rawFlags.getAll(context);
|
|
2015
|
+
},
|
|
2016
|
+
/**
|
|
2017
|
+
* Resolve a flag's value, with an optional fallback.
|
|
2018
|
+
*
|
|
2019
|
+
* FORWARDED as of 2026-08-15. This surface is written out by hand, method
|
|
2020
|
+
* by method, and `get` was missing from it — so the client implemented it,
|
|
2021
|
+
* thirty assertions covered it, and `Flags.get("x")` was `undefined` in a
|
|
2022
|
+
* deployed handler. Exactly the shape of the `Storage.buckets` defect found
|
|
2023
|
+
* the same day: a hand-maintained forwarding list is a list somebody has to
|
|
2024
|
+
* remember to update.
|
|
2025
|
+
*/
|
|
2026
|
+
get(flagName, defaultOrContext, maybeContext) {
|
|
2027
|
+
return rawFlags.get(flagName, defaultOrContext, maybeContext);
|
|
2028
|
+
},
|
|
2029
|
+
setOverride(key, value) {
|
|
2030
|
+
return rawFlags.setOverride(key, value);
|
|
2031
|
+
}
|
|
2032
|
+
}, {
|
|
2033
|
+
/**
|
|
2034
|
+
* Lazily resolve the runtime's cross-user sibling on each call. We do NOT
|
|
2035
|
+
* cache it: `rawFlags.asService()` reads the CURRENT request scope through
|
|
2036
|
+
* the runtime proxy, so caching would leak one request's sibling into
|
|
2037
|
+
* another concurrent request. Mirrors `Database.asService()`.
|
|
2038
|
+
*/
|
|
2039
|
+
$asService() {
|
|
2040
|
+
return rawFlags.asService();
|
|
2041
|
+
},
|
|
2042
|
+
/**
|
|
2043
|
+
* The name this member carried before 31.0.0.
|
|
2044
|
+
*
|
|
2045
|
+
* It does NOT work silently. A retired member that quietly keeps returning
|
|
2046
|
+
* is how a rename becomes a mystery: the old call site goes on compiling,
|
|
2047
|
+
* the new name never spreads, and the two live side by side until somebody
|
|
2048
|
+
* greps for one and misses half the codebase. `Database` made this split
|
|
2049
|
+
* first (`$asService`), and this JSDoc promised "exactly like
|
|
2050
|
+
* Database.$asService()" while the promise went unkept.
|
|
2051
|
+
*/
|
|
2052
|
+
asService() {
|
|
2053
|
+
throw new Error("Flags.asService() was renamed to Flags.$asService() in 31.0.0 \u2014 system members carry the `$` prefix, like Database.$asService().");
|
|
2054
|
+
}
|
|
2055
|
+
});
|
|
2056
|
+
var Realtime = makeServiceProxy("Realtime");
|
|
2057
|
+
|
|
1404
2058
|
// src/test/fake-db.ts
|
|
1405
2059
|
function fakeDatabase() {
|
|
1406
2060
|
const mock = createMockDB();
|
|
@@ -1415,7 +2069,8 @@ function fakeDatabase() {
|
|
|
1415
2069
|
}, "query")
|
|
1416
2070
|
});
|
|
1417
2071
|
return {
|
|
1418
|
-
db,
|
|
2072
|
+
db: makeTypedSurface(db),
|
|
2073
|
+
raw: db,
|
|
1419
2074
|
queries,
|
|
1420
2075
|
seed: /* @__PURE__ */ __name((table, rows) => mock.seed(table, rows), "seed"),
|
|
1421
2076
|
inserted: /* @__PURE__ */ __name((table) => mock.inserted(table), "inserted"),
|
|
@@ -1424,12 +2079,46 @@ function fakeDatabase() {
|
|
|
1424
2079
|
};
|
|
1425
2080
|
}
|
|
1426
2081
|
__name(fakeDatabase, "fakeDatabase");
|
|
2082
|
+
|
|
2083
|
+
// src/test/with-services.ts
|
|
2084
|
+
function absent(name) {
|
|
2085
|
+
return new Proxy({}, {
|
|
2086
|
+
get(_target, prop) {
|
|
2087
|
+
throw new Error(`${String(name)}.${String(prop)} was read, but this test did not provide ${String(name)}. Pass it: withServices({ ${String(name)}: \u2026 }, () => \u2026)`);
|
|
2088
|
+
}
|
|
2089
|
+
});
|
|
2090
|
+
}
|
|
2091
|
+
__name(absent, "absent");
|
|
2092
|
+
function pick(services, key) {
|
|
2093
|
+
return services[key] ?? absent(key);
|
|
2094
|
+
}
|
|
2095
|
+
__name(pick, "pick");
|
|
2096
|
+
function withServices(services, fn) {
|
|
2097
|
+
const filled = {
|
|
2098
|
+
Database: pick(services, "Database"),
|
|
2099
|
+
Auth: pick(services, "Auth"),
|
|
2100
|
+
Secrets: pick(services, "Secrets"),
|
|
2101
|
+
Documents: pick(services, "Documents"),
|
|
2102
|
+
Storage: pick(services, "Storage"),
|
|
2103
|
+
Cache: pick(services, "Cache"),
|
|
2104
|
+
Log: pick(services, "Log"),
|
|
2105
|
+
Notifications: pick(services, "Notifications"),
|
|
2106
|
+
Flags: pick(services, "Flags"),
|
|
2107
|
+
Realtime: pick(services, "Realtime")
|
|
2108
|
+
};
|
|
2109
|
+
return __requestALS.run({
|
|
2110
|
+
runtime: filled,
|
|
2111
|
+
userId: null
|
|
2112
|
+
}, fn);
|
|
2113
|
+
}
|
|
2114
|
+
__name(withServices, "withServices");
|
|
1427
2115
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1428
2116
|
0 && (module.exports = {
|
|
1429
2117
|
TestApiError,
|
|
1430
2118
|
api,
|
|
1431
2119
|
createTestApi,
|
|
1432
2120
|
fakeDatabase,
|
|
1433
|
-
isolated
|
|
2121
|
+
isolated,
|
|
2122
|
+
withServices
|
|
1434
2123
|
});
|
|
1435
2124
|
//# sourceMappingURL=index.cjs.map
|