@palbase/backend 30.0.1 → 33.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 (69) hide show
  1. package/dist/bin/palbase-backend.cjs +210 -24
  2. package/dist/bin/palbase-backend.cjs.map +1 -1
  3. package/dist/bin/palbase-backend.js +7 -7
  4. package/dist/{chunk-TWX6JTGJ.js → chunk-26GH54D2.js} +3 -5
  5. package/dist/{chunk-TWX6JTGJ.js.map → chunk-26GH54D2.js.map} +1 -1
  6. package/dist/{chunk-VVMJEVQP.js → chunk-BRLJOXWS.js} +2 -2
  7. package/dist/{chunk-3TUJWHC2.js → chunk-C4ZA5AT2.js} +378 -19
  8. package/dist/chunk-C4ZA5AT2.js.map +1 -0
  9. package/dist/{chunk-RADVOY7Y.js → chunk-DCDHAKF3.js} +147 -33
  10. package/dist/chunk-DCDHAKF3.js.map +1 -0
  11. package/dist/{chunk-ENZ2RFFJ.js → chunk-GOPZPM2A.js} +3 -310
  12. package/dist/chunk-GOPZPM2A.js.map +1 -0
  13. package/dist/{chunk-CGNN2PUH.js → chunk-KATPXCJ5.js} +37 -4
  14. package/dist/{chunk-CGNN2PUH.js.map → chunk-KATPXCJ5.js.map} +1 -1
  15. package/dist/{chunk-YRVIWHJC.js → chunk-N54QZER3.js} +23 -323
  16. package/dist/chunk-N54QZER3.js.map +1 -0
  17. package/dist/chunk-QMFOL2K6.js +371 -0
  18. package/dist/chunk-QMFOL2K6.js.map +1 -0
  19. package/dist/chunk-XOX6RFPZ.js +383 -0
  20. package/dist/chunk-XOX6RFPZ.js.map +1 -0
  21. package/dist/db/index.cjs +41 -2
  22. package/dist/db/index.cjs.map +1 -1
  23. package/dist/db/index.d.cts +1 -1
  24. package/dist/db/index.d.ts +1 -1
  25. package/dist/db/index.js +12 -13
  26. package/dist/engine/index.cjs +210 -24
  27. package/dist/engine/index.cjs.map +1 -1
  28. package/dist/engine/index.d.cts +3 -3
  29. package/dist/engine/index.d.ts +3 -3
  30. package/dist/engine/index.js +7 -7
  31. package/dist/{index-DtCgaZAg.d.cts → index-BWgnGj68.d.cts} +185 -35
  32. package/dist/{index-b-Q3l7W5.d.ts → index-Bi74dcOu.d.ts} +9 -2
  33. package/dist/{index-8qy3kIuA.d.ts → index-Dg10RnZO.d.ts} +185 -35
  34. package/dist/{index-Zi7MptvP.d.cts → index-g6iQyYci.d.cts} +9 -2
  35. package/dist/index.cjs +135 -8
  36. package/dist/index.cjs.map +1 -1
  37. package/dist/index.d.cts +6 -23
  38. package/dist/index.d.ts +6 -23
  39. package/dist/index.js +48 -48
  40. package/dist/index.js.map +1 -1
  41. package/dist/openapi/index.d.cts +2 -2
  42. package/dist/openapi/index.d.ts +2 -2
  43. package/dist/openapi/index.js +3 -4
  44. package/dist/openapi/index.js.map +1 -1
  45. package/dist/{registry-CEYLH-Iz.d.cts → registry-6VT5RPeO.d.cts} +1 -1
  46. package/dist/{registry-BGJ-Al6F.d.ts → registry-B-sxJJN0.d.ts} +1 -1
  47. package/dist/test/index.cjs +733 -17
  48. package/dist/test/index.cjs.map +1 -1
  49. package/dist/test/index.d.cts +59 -4
  50. package/dist/test/index.d.ts +59 -4
  51. package/dist/test/index.js +56 -16
  52. package/dist/test/index.js.map +1 -1
  53. package/docs/README.md +1 -1
  54. package/docs/auth.md +102 -20
  55. package/docs/database.md +54 -0
  56. package/docs/llms-full.txt +215 -21
  57. package/docs/schema.md +58 -0
  58. package/package.json +1 -1
  59. package/template/db/public.ts +23 -0
  60. package/template/package.json +1 -1
  61. package/dist/chunk-3TUJWHC2.js.map +0 -1
  62. package/dist/chunk-ENZ2RFFJ.js.map +0 -1
  63. package/dist/chunk-RADVOY7Y.js.map +0 -1
  64. package/dist/chunk-SG4UTNOP.js +0 -374
  65. package/dist/chunk-SG4UTNOP.js.map +0 -1
  66. package/dist/chunk-VXPNPVAG.js +0 -40
  67. package/dist/chunk-VXPNPVAG.js.map +0 -1
  68. package/dist/chunk-YRVIWHJC.js.map +0 -1
  69. /package/dist/{chunk-VVMJEVQP.js.map → chunk-BRLJOXWS.js.map} +0 -0
@@ -1,5 +1,5 @@
1
1
  import { T as Token } from '../module-Dl1KFVtc.cjs';
2
- import { D as DBClient } from '../index-DtCgaZAg.cjs';
2
+ import { E as EnvServiceDatabase, D as DBClient, a as RuntimeServices } from '../index-BWgnGj68.cjs';
3
3
  import '../db/env.cjs';
4
4
  import '../stack.cjs';
5
5
  import 'zod';
@@ -163,8 +163,26 @@ interface RecordedQuery {
163
163
  }
164
164
  /** What a service-layer test is handed. */
165
165
  interface FakeDatabase {
166
- /** Pass this where the service expects `Database`. */
167
- db: DBClient;
166
+ /** Pass this where the service expects `Database`.
167
+ *
168
+ * Built with the SAME constructor the production `Database` uses
169
+ * (`makeTypedSurface`), so the fake's surface cannot drift from the real one
170
+ * — including the `$`-prefixed raw ops and the typed `public` schema surface. */
171
+ db: EnvServiceDatabase;
172
+ /**
173
+ * The RAW `DBClient` behind {@link FakeDatabase.db} (FR-028).
174
+ *
175
+ * `RuntimeServices.Database` is the raw client — the ambient `Database`
176
+ * singleton is what WRAPS it with `makeTypedSurface`. So a test that installs
177
+ * the fake as the ambient runtime passes THIS:
178
+ *
179
+ * withServices({ Database: fake.raw }, () => …)
180
+ *
181
+ * Passing `db` there would wrap an already-wrapped surface and every `$op`
182
+ * would miss (measured). Two fields because there are two call sites: `db`
183
+ * goes straight to a service, `raw` goes into the runtime.
184
+ */
185
+ raw: DBClient;
168
186
  /** Every `query()` call, in order. */
169
187
  queries: readonly RecordedQuery[];
170
188
  /** Put rows in a table before the code under test runs. */
@@ -178,4 +196,41 @@ interface FakeDatabase {
178
196
  }
179
197
  declare function fakeDatabase(): FakeDatabase;
180
198
 
181
- export { type CallOptions, type ErrorEnvelope, type FakeDatabase, type IsolatedContainer, type RecordedQuery, type RecordedRequest, type TestApi, type TestApiConfig, TestApiError, type TestIdentity, api, createTestApi, fakeDatabase, isolated };
199
+ /**
200
+ * `withServices()` — run code with only the platform services a test supplies.
201
+ *
202
+ * WHY IT IS PUBLIC. `isolated()` substitutes CONSTRUCTOR dependencies, and its
203
+ * own documentation says why it cannot help here: "platform services are
204
+ * ambient rather than injected". So the repository layer — the one place that
205
+ * really does `import { Database } from "@palbase/backend"` — had no supported
206
+ * way to be unit-tested, while `__runWithRuntime` demands EVERY service. A
207
+ * measured consumer project wrote this helper by hand, exploding proxies
208
+ * included, and 30.0.0 then added a tenth required service (`Auth`) which broke
209
+ * that hand-written copy. Shipping it here is what stops that from recurring.
210
+ *
211
+ * WHAT IS ABSENT IS LOUD. An unsupplied service is neither `undefined` nor a
212
+ * silent no-op: touching it throws and NAMES itself, so a test that quietly
213
+ * grew a dependency on `Cache` fails saying "Cache", not "cannot read
214
+ * properties of undefined".
215
+ *
216
+ * SCOPE IS THE REQUEST ALS, never the process-global slot. Two tests in one
217
+ * file must not be able to see each other's services, and a helper that wrote
218
+ * the global slot would leak the first test's fake into the second.
219
+ *
220
+ * EKSİKSİZLİK DERLEYİCİYE SORULUR, BİR LİSTEYE DEĞİL. İlk yazımı servis
221
+ * adlarını bir diziye koyup `satisfies readonly (keyof RuntimeServices)[]` ile
222
+ * pinliyordu — ama `satisfies` yalnız "her ELEMAN bir anahtar mı" diye bakar,
223
+ * "her ANAHTAR listede mi" diye BAKMAZ. Ölçüldü: `RuntimeServices`'a on birinci
224
+ * bir servis eklenip liste dokunulmadan bırakıldığında `tsc --strict` TEMİZ
225
+ * derliyor, ve eksik servise erişen tüketici `TypeError: Reflect.get called on
226
+ * non-object` alıyor — ne servisin adı, ne çaresi. Yani bu dosyanın var olma
227
+ * sebebi olan kusurun ta kendisi, bu dosya tarafından üretiliyordu.
228
+ *
229
+ * Şimdi `filled` düz bir `RuntimeServices` nesne literali: bir alan eksik
230
+ * kalırsa TypeScript `TS2741: Property '<ad>' is missing` diyor ve derleme
231
+ * durur. `as unknown as` yok.
232
+ */
233
+
234
+ declare function withServices<T>(services: Partial<RuntimeServices>, fn: () => T): T;
235
+
236
+ export { type CallOptions, type ErrorEnvelope, type FakeDatabase, type IsolatedContainer, type RecordedQuery, type RecordedRequest, type TestApi, type TestApiConfig, TestApiError, type TestIdentity, api, createTestApi, fakeDatabase, isolated, withServices };
@@ -1,5 +1,5 @@
1
1
  import { T as Token } from '../module-Dl1KFVtc.js';
2
- import { D as DBClient } from '../index-8qy3kIuA.js';
2
+ import { E as EnvServiceDatabase, D as DBClient, a as RuntimeServices } from '../index-Dg10RnZO.js';
3
3
  import '../db/env.js';
4
4
  import '../stack.js';
5
5
  import 'zod';
@@ -163,8 +163,26 @@ interface RecordedQuery {
163
163
  }
164
164
  /** What a service-layer test is handed. */
165
165
  interface FakeDatabase {
166
- /** Pass this where the service expects `Database`. */
167
- db: DBClient;
166
+ /** Pass this where the service expects `Database`.
167
+ *
168
+ * Built with the SAME constructor the production `Database` uses
169
+ * (`makeTypedSurface`), so the fake's surface cannot drift from the real one
170
+ * — including the `$`-prefixed raw ops and the typed `public` schema surface. */
171
+ db: EnvServiceDatabase;
172
+ /**
173
+ * The RAW `DBClient` behind {@link FakeDatabase.db} (FR-028).
174
+ *
175
+ * `RuntimeServices.Database` is the raw client — the ambient `Database`
176
+ * singleton is what WRAPS it with `makeTypedSurface`. So a test that installs
177
+ * the fake as the ambient runtime passes THIS:
178
+ *
179
+ * withServices({ Database: fake.raw }, () => …)
180
+ *
181
+ * Passing `db` there would wrap an already-wrapped surface and every `$op`
182
+ * would miss (measured). Two fields because there are two call sites: `db`
183
+ * goes straight to a service, `raw` goes into the runtime.
184
+ */
185
+ raw: DBClient;
168
186
  /** Every `query()` call, in order. */
169
187
  queries: readonly RecordedQuery[];
170
188
  /** Put rows in a table before the code under test runs. */
@@ -178,4 +196,41 @@ interface FakeDatabase {
178
196
  }
179
197
  declare function fakeDatabase(): FakeDatabase;
180
198
 
181
- export { type CallOptions, type ErrorEnvelope, type FakeDatabase, type IsolatedContainer, type RecordedQuery, type RecordedRequest, type TestApi, type TestApiConfig, TestApiError, type TestIdentity, api, createTestApi, fakeDatabase, isolated };
199
+ /**
200
+ * `withServices()` — run code with only the platform services a test supplies.
201
+ *
202
+ * WHY IT IS PUBLIC. `isolated()` substitutes CONSTRUCTOR dependencies, and its
203
+ * own documentation says why it cannot help here: "platform services are
204
+ * ambient rather than injected". So the repository layer — the one place that
205
+ * really does `import { Database } from "@palbase/backend"` — had no supported
206
+ * way to be unit-tested, while `__runWithRuntime` demands EVERY service. A
207
+ * measured consumer project wrote this helper by hand, exploding proxies
208
+ * included, and 30.0.0 then added a tenth required service (`Auth`) which broke
209
+ * that hand-written copy. Shipping it here is what stops that from recurring.
210
+ *
211
+ * WHAT IS ABSENT IS LOUD. An unsupplied service is neither `undefined` nor a
212
+ * silent no-op: touching it throws and NAMES itself, so a test that quietly
213
+ * grew a dependency on `Cache` fails saying "Cache", not "cannot read
214
+ * properties of undefined".
215
+ *
216
+ * SCOPE IS THE REQUEST ALS, never the process-global slot. Two tests in one
217
+ * file must not be able to see each other's services, and a helper that wrote
218
+ * the global slot would leak the first test's fake into the second.
219
+ *
220
+ * EKSİKSİZLİK DERLEYİCİYE SORULUR, BİR LİSTEYE DEĞİL. İlk yazımı servis
221
+ * adlarını bir diziye koyup `satisfies readonly (keyof RuntimeServices)[]` ile
222
+ * pinliyordu — ama `satisfies` yalnız "her ELEMAN bir anahtar mı" diye bakar,
223
+ * "her ANAHTAR listede mi" diye BAKMAZ. Ölçüldü: `RuntimeServices`'a on birinci
224
+ * bir servis eklenip liste dokunulmadan bırakıldığında `tsc --strict` TEMİZ
225
+ * derliyor, ve eksik servise erişen tüketici `TypeError: Reflect.get called on
226
+ * non-object` alıyor — ne servisin adı, ne çaresi. Yani bu dosyanın var olma
227
+ * sebebi olan kusurun ta kendisi, bu dosya tarafından üretiliyordu.
228
+ *
229
+ * Şimdi `filled` düz bir `RuntimeServices` nesne literali: bir alan eksik
230
+ * kalırsa TypeScript `TS2741: Property '<ad>' is missing` diyor ve derleme
231
+ * durur. `as unknown as` yok.
232
+ */
233
+
234
+ declare function withServices<T>(services: Partial<RuntimeServices>, fn: () => T): T;
235
+
236
+ export { type CallOptions, type ErrorEnvelope, type FakeDatabase, type IsolatedContainer, type RecordedQuery, type RecordedRequest, type TestApi, type TestApiConfig, TestApiError, type TestIdentity, api, createTestApi, fakeDatabase, isolated, withServices };
@@ -1,3 +1,7 @@
1
+ import {
2
+ __requestALS,
3
+ makeTypedSurface
4
+ } from "../chunk-QMFOL2K6.js";
1
5
  import {
2
6
  assertNoExpressionHandles,
3
7
  assertUsableFilter,
@@ -7,10 +11,10 @@ import {
7
11
  isColumnExpr,
8
12
  isNowExpr,
9
13
  isSqlFragment
10
- } from "../chunk-3TUJWHC2.js";
14
+ } from "../chunk-C4ZA5AT2.js";
11
15
  import {
12
16
  __name
13
- } from "../chunk-VXPNPVAG.js";
17
+ } from "../chunk-KATPXCJ5.js";
14
18
 
15
19
  // ../core/dist/index.js
16
20
  var CACHE_TTL_MS = 5 * 60 * 1e3;
@@ -527,7 +531,7 @@ function createMockDB() {
527
531
  // ops write to, so a test that writes three rows and counts them gets 3 —
528
532
  // a mock that answered 0 would make the surface look broken in exactly the
529
533
  // tests meant to prove it works.
530
- async updateMany(table, where, set) {
534
+ async updateMany(table, where, set, opts) {
531
535
  if (Object.keys(where).length === 0) throw new Error(`updateMany(${table}): bo\u015F filtre`);
532
536
  assertUsableFilter("updateMany", table, where);
533
537
  refuseFragment("updateMany", table, where);
@@ -547,7 +551,8 @@ function createMockDB() {
547
551
  row[k] = v;
548
552
  }
549
553
  }
550
- return hit;
554
+ for (const row of hit) track(tracked.updated, table, row);
555
+ return opts?.returning === false ? hit.length : hit;
551
556
  },
552
557
  async deleteMany(table, where) {
553
558
  if (Object.keys(where).length === 0) throw new Error(`deleteMany(${table}): bo\u015F filtre`);
@@ -624,7 +629,7 @@ function createMockDB() {
624
629
  const vals = bucket.map((r) => r[c]).filter((v) => v !== null && v !== void 0);
625
630
  (out["avg"] ??= {})[c] = vals.length === 0 ? null : String(Number(vals.reduce((a, v) => addDecimal(a, v, 1), "0")) / vals.length);
626
631
  }
627
- for (const [fn, pick] of [
632
+ for (const [fn, pick2] of [
628
633
  [
629
634
  "min",
630
635
  -1
@@ -636,7 +641,7 @@ function createMockDB() {
636
641
  ]) {
637
642
  for (const c of q[fn] ?? []) {
638
643
  const vals = bucket.map((r) => r[c]).filter((v) => v !== null && v !== void 0);
639
- (out[fn] ??= {})[c] = vals.length === 0 ? null : vals.reduce((a, v) => cmp(v, a, pick === 1 ? "gt" : "lt") ? v : a);
644
+ (out[fn] ??= {})[c] = vals.length === 0 ? null : vals.reduce((a, v) => cmp(v, a, pick2 === 1 ? "gt" : "lt") ? v : a);
640
645
  }
641
646
  }
642
647
  if (q.count === true) out["count"] = bucket.length;
@@ -785,23 +790,20 @@ function createMockDB() {
785
790
  applied[k] = v;
786
791
  }
787
792
  assertNoExpressionHandles("update", table, Object.keys(applied), applied);
788
- const updated = idx >= 0 ? {
793
+ if (idx < 0) return null;
794
+ const updated = {
789
795
  ...rows[idx],
790
796
  ...applied
791
- } : {
792
- id,
793
- ...applied
794
797
  };
795
- if (idx >= 0) {
796
- rows[idx] = updated;
797
- }
798
+ rows[idx] = updated;
798
799
  track(tracked.updated, table, updated);
799
800
  return updated;
800
801
  },
801
802
  async delete(table, id) {
802
803
  const rows = store.get(table) ?? [];
803
804
  const idx = rows.findIndex((r) => r["id"] === id);
804
- if (idx >= 0) rows.splice(idx, 1);
805
+ if (idx < 0) return;
806
+ rows.splice(idx, 1);
805
807
  const list = tracked.deleted.get(table);
806
808
  if (list) list.push(id);
807
809
  else tracked.deleted.set(table, [
@@ -819,6 +821,9 @@ function createMockDB() {
819
821
  async findMany(table, query, opts) {
820
822
  assertUsableFilter("findMany", table, query);
821
823
  refuseFragment("findMany", table, query);
824
+ if (opts?.with !== void 0 && Object.keys(opts.with).length > 0) {
825
+ 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.`);
826
+ }
822
827
  const rows = store.get(table) ?? [];
823
828
  let out = query ? rows.filter((row) => rowMatchesFilter("findMany", table, row, query)) : [
824
829
  ...rows
@@ -1114,7 +1119,8 @@ function fakeDatabase() {
1114
1119
  }, "query")
1115
1120
  });
1116
1121
  return {
1117
- db,
1122
+ db: makeTypedSurface(db),
1123
+ raw: db,
1118
1124
  queries,
1119
1125
  seed: /* @__PURE__ */ __name((table, rows) => mock.seed(table, rows), "seed"),
1120
1126
  inserted: /* @__PURE__ */ __name((table) => mock.inserted(table), "inserted"),
@@ -1123,11 +1129,45 @@ function fakeDatabase() {
1123
1129
  };
1124
1130
  }
1125
1131
  __name(fakeDatabase, "fakeDatabase");
1132
+
1133
+ // src/test/with-services.ts
1134
+ function absent(name) {
1135
+ return new Proxy({}, {
1136
+ get(_target, prop) {
1137
+ throw new Error(`${String(name)}.${String(prop)} was read, but this test did not provide ${String(name)}. Pass it: withServices({ ${String(name)}: \u2026 }, () => \u2026)`);
1138
+ }
1139
+ });
1140
+ }
1141
+ __name(absent, "absent");
1142
+ function pick(services, key) {
1143
+ return services[key] ?? absent(key);
1144
+ }
1145
+ __name(pick, "pick");
1146
+ function withServices(services, fn) {
1147
+ const filled = {
1148
+ Database: pick(services, "Database"),
1149
+ Auth: pick(services, "Auth"),
1150
+ Secrets: pick(services, "Secrets"),
1151
+ Documents: pick(services, "Documents"),
1152
+ Storage: pick(services, "Storage"),
1153
+ Cache: pick(services, "Cache"),
1154
+ Log: pick(services, "Log"),
1155
+ Notifications: pick(services, "Notifications"),
1156
+ Flags: pick(services, "Flags"),
1157
+ Realtime: pick(services, "Realtime")
1158
+ };
1159
+ return __requestALS.run({
1160
+ runtime: filled,
1161
+ userId: null
1162
+ }, fn);
1163
+ }
1164
+ __name(withServices, "withServices");
1126
1165
  export {
1127
1166
  TestApiError,
1128
1167
  api,
1129
1168
  createTestApi,
1130
1169
  fakeDatabase,
1131
- isolated
1170
+ isolated,
1171
+ withServices
1132
1172
  };
1133
1173
  //# sourceMappingURL=index.js.map