@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.
Files changed (67) hide show
  1. package/dist/bin/palbase-backend.cjs +136 -3
  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-5J5W75A6.js → chunk-5C5UCILO.js} +27 -325
  7. package/dist/chunk-5C5UCILO.js.map +1 -0
  8. package/dist/{chunk-VN7NMDUH.js → chunk-AZJIKCOR.js} +113 -15
  9. package/dist/chunk-AZJIKCOR.js.map +1 -0
  10. package/dist/{chunk-VVMJEVQP.js → chunk-BRLJOXWS.js} +2 -2
  11. package/dist/{chunk-3TUJWHC2.js → chunk-IXAX5CON.js} +378 -19
  12. package/dist/chunk-IXAX5CON.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-KGP6ALIU.js +361 -0
  16. package/dist/chunk-KGP6ALIU.js.map +1 -0
  17. package/dist/chunk-XEGZ3S2Q.js +346 -0
  18. package/dist/chunk-XEGZ3S2Q.js.map +1 -0
  19. package/dist/{chunk-ENZ2RFFJ.js → chunk-Z4CZRMNF.js} +3 -310
  20. package/dist/chunk-Z4CZRMNF.js.map +1 -0
  21. package/dist/db/index.cjs +29 -0
  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 +136 -3
  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-b-Q3l7W5.d.ts → index-BILC9WwS.d.ts} +9 -2
  32. package/dist/{index-8qy3kIuA.d.ts → index-BgWnP07a.d.ts} +119 -17
  33. package/dist/{index-Zi7MptvP.d.cts → index-CWGiJ2Up.d.cts} +9 -2
  34. package/dist/{index-DtCgaZAg.d.cts → index-DgYkdNT9.d.cts} +119 -17
  35. package/dist/index.cjs +87 -5
  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-BcRYIQ-R.d.cts} +1 -1
  46. package/dist/{registry-BGJ-Al6F.d.ts → registry-ClzjxIWy.d.ts} +1 -1
  47. package/dist/test/index.cjs +703 -14
  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 +51 -13
  52. package/dist/test/index.js.map +1 -1
  53. package/docs/README.md +1 -1
  54. package/docs/database.md +54 -0
  55. package/docs/llms-full.txt +87 -1
  56. package/docs/schema.md +32 -0
  57. package/package.json +1 -1
  58. package/template/package.json +1 -1
  59. package/dist/chunk-3TUJWHC2.js.map +0 -1
  60. package/dist/chunk-5J5W75A6.js.map +0 -1
  61. package/dist/chunk-ENZ2RFFJ.js.map +0 -1
  62. package/dist/chunk-SG4UTNOP.js +0 -374
  63. package/dist/chunk-SG4UTNOP.js.map +0 -1
  64. package/dist/chunk-VN7NMDUH.js.map +0 -1
  65. package/dist/chunk-VXPNPVAG.js +0 -40
  66. package/dist/chunk-VXPNPVAG.js.map +0 -1
  67. /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-DgYkdNT9.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-BgWnP07a.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-XEGZ3S2Q.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-IXAX5CON.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;
@@ -624,7 +628,7 @@ function createMockDB() {
624
628
  const vals = bucket.map((r) => r[c]).filter((v) => v !== null && v !== void 0);
625
629
  (out["avg"] ??= {})[c] = vals.length === 0 ? null : String(Number(vals.reduce((a, v) => addDecimal(a, v, 1), "0")) / vals.length);
626
630
  }
627
- for (const [fn, pick] of [
631
+ for (const [fn, pick2] of [
628
632
  [
629
633
  "min",
630
634
  -1
@@ -636,7 +640,7 @@ function createMockDB() {
636
640
  ]) {
637
641
  for (const c of q[fn] ?? []) {
638
642
  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);
643
+ (out[fn] ??= {})[c] = vals.length === 0 ? null : vals.reduce((a, v) => cmp(v, a, pick2 === 1 ? "gt" : "lt") ? v : a);
640
644
  }
641
645
  }
642
646
  if (q.count === true) out["count"] = bucket.length;
@@ -785,16 +789,12 @@ function createMockDB() {
785
789
  applied[k] = v;
786
790
  }
787
791
  assertNoExpressionHandles("update", table, Object.keys(applied), applied);
788
- const updated = idx >= 0 ? {
792
+ if (idx < 0) return null;
793
+ const updated = {
789
794
  ...rows[idx],
790
795
  ...applied
791
- } : {
792
- id,
793
- ...applied
794
796
  };
795
- if (idx >= 0) {
796
- rows[idx] = updated;
797
- }
797
+ rows[idx] = updated;
798
798
  track(tracked.updated, table, updated);
799
799
  return updated;
800
800
  },
@@ -819,6 +819,9 @@ function createMockDB() {
819
819
  async findMany(table, query, opts) {
820
820
  assertUsableFilter("findMany", table, query);
821
821
  refuseFragment("findMany", table, query);
822
+ if (opts?.with !== void 0 && Object.keys(opts.with).length > 0) {
823
+ 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.`);
824
+ }
822
825
  const rows = store.get(table) ?? [];
823
826
  let out = query ? rows.filter((row) => rowMatchesFilter("findMany", table, row, query)) : [
824
827
  ...rows
@@ -1114,7 +1117,8 @@ function fakeDatabase() {
1114
1117
  }, "query")
1115
1118
  });
1116
1119
  return {
1117
- db,
1120
+ db: makeTypedSurface(db),
1121
+ raw: db,
1118
1122
  queries,
1119
1123
  seed: /* @__PURE__ */ __name((table, rows) => mock.seed(table, rows), "seed"),
1120
1124
  inserted: /* @__PURE__ */ __name((table) => mock.inserted(table), "inserted"),
@@ -1123,11 +1127,45 @@ function fakeDatabase() {
1123
1127
  };
1124
1128
  }
1125
1129
  __name(fakeDatabase, "fakeDatabase");
1130
+
1131
+ // src/test/with-services.ts
1132
+ function absent(name) {
1133
+ return new Proxy({}, {
1134
+ get(_target, prop) {
1135
+ throw new Error(`${String(name)}.${String(prop)} was read, but this test did not provide ${String(name)}. Pass it: withServices({ ${String(name)}: \u2026 }, () => \u2026)`);
1136
+ }
1137
+ });
1138
+ }
1139
+ __name(absent, "absent");
1140
+ function pick(services, key) {
1141
+ return services[key] ?? absent(key);
1142
+ }
1143
+ __name(pick, "pick");
1144
+ function withServices(services, fn) {
1145
+ const filled = {
1146
+ Database: pick(services, "Database"),
1147
+ Auth: pick(services, "Auth"),
1148
+ Secrets: pick(services, "Secrets"),
1149
+ Documents: pick(services, "Documents"),
1150
+ Storage: pick(services, "Storage"),
1151
+ Cache: pick(services, "Cache"),
1152
+ Log: pick(services, "Log"),
1153
+ Notifications: pick(services, "Notifications"),
1154
+ Flags: pick(services, "Flags"),
1155
+ Realtime: pick(services, "Realtime")
1156
+ };
1157
+ return __requestALS.run({
1158
+ runtime: filled,
1159
+ userId: null
1160
+ }, fn);
1161
+ }
1162
+ __name(withServices, "withServices");
1126
1163
  export {
1127
1164
  TestApiError,
1128
1165
  api,
1129
1166
  createTestApi,
1130
1167
  fakeDatabase,
1131
- isolated
1168
+ isolated,
1169
+ withServices
1132
1170
  };
1133
1171
  //# sourceMappingURL=index.js.map