@shivaedev/sql 0.27.0 → 0.29.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 (54) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/README.md +25 -10
  3. package/dist/adapters/makePostgresClient.d.ts.map +1 -1
  4. package/dist/adapters/makePostgresClient.js +8 -1
  5. package/dist/adapters/makePostgresClient.js.map +1 -1
  6. package/dist/latestPerGroup.d.ts +1 -1
  7. package/dist/latestPerGroup.d.ts.map +1 -1
  8. package/dist/latestPerGroup.js +37 -13
  9. package/dist/latestPerGroup.js.map +1 -1
  10. package/dist/listenForChanges.d.ts.map +1 -1
  11. package/dist/listenForChanges.js +11 -5
  12. package/dist/listenForChanges.js.map +1 -1
  13. package/dist/pinnedGroups.d.ts +9 -1
  14. package/dist/pinnedGroups.d.ts.map +1 -1
  15. package/dist/pinnedGroups.js +2 -2
  16. package/dist/pinnedGroups.js.map +1 -1
  17. package/dist/postgresChanges.d.ts +1 -0
  18. package/dist/postgresChanges.d.ts.map +1 -1
  19. package/dist/postgresChanges.js.map +1 -1
  20. package/dist/postgresLayer.d.ts +3 -0
  21. package/dist/postgresLayer.d.ts.map +1 -1
  22. package/dist/postgresLayer.js.map +1 -1
  23. package/dist/repository.d.ts +5 -9
  24. package/dist/repository.d.ts.map +1 -1
  25. package/dist/repository.js +2 -0
  26. package/dist/repository.js.map +1 -1
  27. package/dist/repositoryCollection.d.ts +28 -0
  28. package/dist/repositoryCollection.d.ts.map +1 -0
  29. package/dist/repositoryCollection.js +21 -0
  30. package/dist/repositoryCollection.js.map +1 -0
  31. package/dist/testing.d.ts +2 -1
  32. package/dist/testing.d.ts.map +1 -1
  33. package/dist/testing.js +1 -1
  34. package/dist/testing.js.map +1 -1
  35. package/dist/tupleIn.d.ts +4 -2
  36. package/dist/tupleIn.d.ts.map +1 -1
  37. package/dist/tupleIn.js +13 -5
  38. package/dist/tupleIn.js.map +1 -1
  39. package/dist/tupleUnnest.d.ts +5 -0
  40. package/dist/tupleUnnest.d.ts.map +1 -0
  41. package/dist/tupleUnnest.js +26 -0
  42. package/dist/tupleUnnest.js.map +1 -0
  43. package/package.json +4 -4
  44. package/src/adapters/makePostgresClient.ts +9 -1
  45. package/src/latestPerGroup.ts +41 -14
  46. package/src/listenForChanges.ts +13 -10
  47. package/src/pinnedGroups.ts +5 -3
  48. package/src/postgresChanges.ts +1 -0
  49. package/src/postgresLayer.ts +3 -0
  50. package/src/repository.ts +11 -11
  51. package/src/repositoryCollection.ts +46 -0
  52. package/src/testing.ts +7 -3
  53. package/src/tupleIn.ts +40 -7
  54. package/src/tupleUnnest.ts +25 -0
@@ -5,12 +5,14 @@ import { makeCompositeCrud } from "#compositeRepository.ts";
5
5
  import { makeConflictWrites } from "#conflictWrites.ts";
6
6
  import { makeInsertFromSelect } from "#insertFromSelect.ts";
7
7
  import { repositoryChanges } from "#repositoryChanges.ts";
8
+ import { validateRepositoryCollection } from "#repositoryCollection.ts";
8
9
  import { makeQueries } from "#repositoryQueries.ts";
9
10
  import { makeVersionedWrites } from "#versionedRepository.ts";
10
11
  export class OptimisticConcurrencyError extends Data.TaggedError("OptimisticConcurrencyError") {
11
12
  }
12
13
  export function makeRepository(model, options) {
13
14
  return Effect.fnUntraced(function* () {
15
+ yield* validateRepositoryCollection(model.fields, options);
14
16
  const sql = yield* SqlClient.SqlClient;
15
17
  const composite = typeof options.idColumn === "object";
16
18
  const idColumns = (composite ? options.idColumn : [options.idColumn]).map(String);
@@ -1 +1 @@
1
- {"version":3,"file":"repository.js","sourceRoot":"","sources":["../src/repository.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAe,MAAM,QAAQ,CAAC;AAEnD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAoB,cAAc,EAAE,MAAM,iBAAiB,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAoC,MAAM,yBAAyB,CAAC;AAC9F,OAAO,EAA6C,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACnG,OAAO,EAAE,oBAAoB,EAAqB,MAAM,sBAAsB,CAAC;AAC/E,OAAO,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAE1D,OAAO,EAAE,WAAW,EAAgB,MAAM,uBAAuB,CAAC;AAClE,OAAO,EAAiB,mBAAmB,EAA4C,MAAM,yBAAyB,CAAC;AAwBvH,MAAM,OAAO,0BAA2B,SAAQ,IAAI,CAAC,WAAW,CAAC,4BAA4B,CAK3F;CAAG;AAsEL,MAAM,UAAU,cAAc,CAC7B,KAAa,EACb,OAIC;IAED,OAAO,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC;QACjC,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC;QACvC,MAAM,SAAS,GAAG,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ,CAAC;QACvD,MAAM,SAAS,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAClF,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,iBAAiB,CAAC,GAAG,EAAE,OAAO,CAAC,SAAS,EAAE,SAAS,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;QAC1F,MAAM,IAAI,GAAG,iBAAiB,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,OAAO,CAAC,CAAC;QACxF,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,EAAE,GAAG,EAAE,OAAO,CAAC,CAAC;QACjD,OAAO,OAAO,CAAC,aAAa,KAAK,SAAS;YACzC,CAAC,CAAC;gBACA,GAAG,IAAI;gBACP,gBAAgB,EAAE,oBAAoB,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,EAAE,kBAAkB,CAAC;gBAC/F,UAAU,EAAE,cAAc,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,EAAE,YAAY,CAAC;gBAC7E,GAAG,CAAC,OAAO,CAAC,eAAe,KAAK,SAAS;oBACxC,CAAC,CAAC,EAAE;oBACJ,CAAC,CAAC,kBAAkB,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,eAAe,EAAE,OAAO,CAAC,eAAe,EAAE,SAAS,EAAE,CAAC,CAAC;gBAChH,GAAG,OAAO;aACV;YACF,CAAC,CAAC;gBACA,GAAG,IAAI;gBACP,GAAG,mBAAmB,CAAC,KAAK,EAAE,GAAG,EAAE;oBAClC,GAAG,OAAO;oBACV,OAAO;oBACP,QAAQ,EAAE,CAAC,EAAE,EAAE,eAAe,EAAE,MAAM,EAAE,EAAE,CAAC,IAAI,0BAA0B,CAAC,EAAE,eAAe,EAAE,EAAE,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC;oBACxI,SAAS;oBACT,aAAa,EAAE,OAAO,CAAC,aAAa;iBACpC,CAAC;gBACF,GAAG,OAAO;aACV,CAAC;IACL,CAAC,CAAC,EAAE,CAAC;AACN,CAAC"}
1
+ {"version":3,"file":"repository.js","sourceRoot":"","sources":["../src/repository.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ import { Effect, type Schema } from "effect";
2
+ export interface RepositoryCollection {
3
+ readonly bucket: Readonly<Record<string, unknown>>;
4
+ readonly name: string;
5
+ }
6
+ export interface RepositoryOptions<TId> {
7
+ readonly collection?: RepositoryCollection;
8
+ readonly idColumn: TId;
9
+ readonly spanPrefix: string;
10
+ readonly tableName: string;
11
+ }
12
+ declare const BucketRegistrationError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
13
+ readonly _tag: "BucketRegistrationError";
14
+ } & Readonly<A>;
15
+ export declare class BucketRegistrationError extends BucketRegistrationError_base<{
16
+ readonly tableName: string;
17
+ readonly collectionName: string | undefined;
18
+ readonly modelFields: readonly string[];
19
+ readonly collectionFields: readonly string[];
20
+ }> {
21
+ get message(): string;
22
+ }
23
+ export declare const validateRepositoryCollection: (fields: Schema.Struct.Fields, options: {
24
+ readonly tableName: string;
25
+ readonly collection?: RepositoryCollection;
26
+ }) => Effect.Effect<undefined, BucketRegistrationError, never>;
27
+ export {};
28
+ //# sourceMappingURL=repositoryCollection.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repositoryCollection.d.ts","sourceRoot":"","sources":["../src/repositoryCollection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,MAAM,EAAS,KAAK,MAAM,EAAE,MAAM,QAAQ,CAAC;AAG1D,MAAM,WAAW,oBAAoB;IACpC,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACnD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,iBAAiB,CAAC,GAAG;IACrC,QAAQ,CAAC,UAAU,CAAC,EAAE,oBAAoB,CAAC;IAC3C,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC;IACvB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC3B;;;;AAED,qBAAa,uBAAwB,SAAQ,6BAA4C;IACxF,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,WAAW,EAAE,SAAS,MAAM,EAAE,CAAC;IACxC,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,CAAC;CAC7C,CAAC;IACD,IAAa,OAAO,WAEnB;CACD;AAED,eAAO,MAAM,4BAA4B;wBAET,MAAM;0BAAwB,oBAAoB;8DAiBhF,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { Data, Effect, Order } from "effect";
2
+ import { declaredBuckets } from "@shivaedev/changes/buckets.ts";
3
+ export class BucketRegistrationError extends Data.TaggedError("BucketRegistrationError") {
4
+ get message() {
5
+ return `Bucket registration for ${this.tableName}: model fields ${JSON.stringify(this.modelFields)} disagree with collection ${this.collectionName ?? "(missing)"} fields ${JSON.stringify(this.collectionFields)}`;
6
+ }
7
+ }
8
+ export const validateRepositoryCollection = Effect.fnUntraced(function* (fields, options) {
9
+ const modelFields = declaredBuckets(fields)
10
+ .map(({ field }) => field)
11
+ .sort(Order.String);
12
+ const collectionFields = Object.keys(options.collection?.bucket ?? {}).sort(Order.String);
13
+ if ((options.collection === undefined && modelFields.length === 0)
14
+ || (options.collection?.name === options.tableName
15
+ && modelFields.length === collectionFields.length
16
+ && modelFields.every((field, index) => field === collectionFields[index]))) {
17
+ return;
18
+ }
19
+ return yield* Effect.fail(new BucketRegistrationError({ collectionFields, collectionName: options.collection?.name, modelFields, tableName: options.tableName }));
20
+ });
21
+ //# sourceMappingURL=repositoryCollection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repositoryCollection.js","sourceRoot":"","sources":["../src/repositoryCollection.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAe,MAAM,QAAQ,CAAC;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAchE,MAAM,OAAO,uBAAwB,SAAQ,IAAI,CAAC,WAAW,CAAC,yBAAyB,CAKrF;IACD,IAAa,OAAO;QACnB,OAAO,2BAA2B,IAAI,CAAC,SAAS,kBAAkB,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,WAAW,CAAC,6BAA6B,IAAI,CAAC,cAAc,IAAI,WAAW,WAAW,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC;IACrN,CAAC;CACD;AAED,MAAM,CAAC,MAAM,4BAA4B,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EACtE,MAA4B,EAC5B,OAAmF;IAEnF,MAAM,WAAW,GAAG,eAAe,CAAC,MAAM,CAAC;SACzC,GAAG,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,KAAK,CAAC;SACzB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IACrB,MAAM,gBAAgB,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;IAC1F,IACC,CAAC,OAAO,CAAC,UAAU,KAAK,SAAS,IAAI,WAAW,CAAC,MAAM,KAAK,CAAC,CAAC;WAC3D,CAAC,OAAO,CAAC,UAAU,EAAE,IAAI,KAAK,OAAO,CAAC,SAAS;eAC9C,WAAW,CAAC,MAAM,KAAK,gBAAgB,CAAC,MAAM;eAC9C,WAAW,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,EAC1E,CAAC;QACF,OAAO;IACR,CAAC;IACD,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CACxB,IAAI,uBAAuB,CAAC,EAAE,gBAAgB,EAAE,cAAc,EAAE,OAAO,CAAC,UAAU,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CACtI,CAAC;AACH,CAAC,CAAC,CAAC"}
package/dist/testing.d.ts CHANGED
@@ -21,6 +21,7 @@ export declare class PublishedKeysMismatch extends PublishedKeysMismatch_base<{
21
21
  }> {
22
22
  get message(): string;
23
23
  }
24
- export declare function expectPublished(keys: readonly unknown[]): <A, E, R>(body: Effect.Effect<A, E, R>) => Effect.Effect<A, E | PublishedKeysMismatch, Exclude<R, never>>;
24
+ export declare function expectPublished(keys: readonly unknown[]): <A, E, R>(body: Effect.Effect<A, E, R>) => Effect.Effect<A, E | PublishedKeysMismatch, R>;
25
+ export declare function expectPublished<A>(keys: (result: A) => readonly unknown[]): <E, R>(body: Effect.Effect<A, E, R>) => Effect.Effect<A, E | PublishedKeysMismatch, R>;
25
26
  export {};
26
27
  //# sourceMappingURL=testing.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,OAAO,EAAoB,MAAM,4BAA4B,CAAC;;;;AAI5F,qBAAa,iBAAkB,SAAQ,uBAAsC;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC;CAAG;AAEvH,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,OAAO,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC;CAC5E;AAED,eAAO,MAAM,kBAAkB,GAAsD,CAAC,EAAE,CAAC,EAAE,CAAC,4UAuD1F,CAAC;;;;AAEH,qBAAa,qBAAsB,SAAQ,2BAA0C;IACpF,QAAQ,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAC;CACpC,CAAC;IACD,IAAa,OAAO,WAEnB;CACD;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,IACA,CAAC,EAAE,CAAC,EAAE,CAAC,kGAmB9D"}
1
+ {"version":3,"file":"testing.d.ts","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,OAAO,EAAoB,MAAM,4BAA4B,CAAC;;;;AAI5F,qBAAa,iBAAkB,SAAQ,uBAAsC;IAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;CAAE,CAAC;CAAG;AAEvH,MAAM,WAAW,cAAc;IAC9B,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,OAAO,GAAG,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC;CAC5E;AAED,eAAO,MAAM,kBAAkB,GAAsD,CAAC,EAAE,CAAC,EAAE,CAAC,4UAuD1F,CAAC;;;;AAEH,qBAAa,qBAAsB,SAAQ,2BAA0C;IACpF,QAAQ,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAC;IACtC,QAAQ,CAAC,MAAM,EAAE,SAAS,OAAO,EAAE,CAAC;CACpC,CAAC;IACD,IAAa,OAAO,WAEnB;CACD;AAED,wBAAgB,eAAe,CAAC,IAAI,EAAE,SAAS,OAAO,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,qBAAqB,EAAE,CAAC,CAAC,CAAC;AACrJ,wBAAgB,eAAe,CAAC,CAAC,EAChC,IAAI,EAAE,CAAC,MAAM,EAAE,CAAC,KAAK,SAAS,OAAO,EAAE,GACrC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,GAAG,qBAAqB,EAAE,CAAC,CAAC,CAAC"}
package/dist/testing.js CHANGED
@@ -65,7 +65,7 @@ export function expectPublished(keys) {
65
65
  }
66
66
  return observer(event);
67
67
  }));
68
- const expected = new Set(keys);
68
+ const expected = new Set(typeof keys === "function" ? keys(result) : keys);
69
69
  if (actual.size !== expected.size || [...expected].some((key) => !actual.has(key))) {
70
70
  return yield* Effect.fail(new PublishedKeysMismatch({ actual: [...actual], expected: [...expected] }));
71
71
  }
@@ -1 +1 @@
1
- {"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"testing.js","sourceRoot":"","sources":["../src/testing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAgC,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAC5F,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AAExD,MAAM,OAAO,iBAAkB,SAAQ,IAAI,CAAC,WAAW,CAAC,mBAAmB,CAAyC;CAAG;AAMvH,MAAM,CAAC,MAAM,kBAAkB,GAAG,MAAM,CAAC,EAAE,CAAC,4BAA4B,CAAC,CAAC,QAAQ,CAAC,EAClF,IAA4B,EAC5B,OAAwB;IAExB,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC;IACvC,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC;IACvC,MAAM,KAAK,GAAG,IAAI,GAAG,EAAW,CAAC;IACjC,MAAM,YAAY,GAAG,IAAI,GAAG,EAA+C,CAAC;IAC5E,MAAM,KAAK,GAAG,KAAK,CAAC,CAAC,kBAAkB,CAAC;IACxC,MAAM,OAAO,GAAG,GAAG,CAAA,yGAAyG,CAAC,IAAI,CAChI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAC1I,CAAC;IACF,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACnC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QAC/E,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC;QAC3B,MAAM,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE;YAC/C,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;YACxD,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;gBAChB,OAAO,KAAK,CAAC;YACd,CAAC;YACD,MAAM,SAAS,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;YAChD,MAAM,QAAQ,GAAG,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YACpD,MAAM,QAAQ,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,EAAE,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC,CAAC;YAC7E,OAAO,CAAC,KAAK,GAAG,CAAC,SAAS,EAAE,IAAI,IAAI,CAAC,CAAC,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QAC7G,CAAC,CAAC,CAAC;QACH,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxB,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,iBAAiB,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC;QACxG,CAAC;QACD,OAAO,MAAM,CAAC;IACf,CAAC,CAAC,CAAC;IACH,OAAO,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,CACzB,QAAQ,CAAC,EAAE,UAAU,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,EAAE,CAAC,EAC1C,MAAM,CAAC,cAAc,CAAC,kBAAkB,EAAE;QACzC,OAAO,EAAE,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,CAC9B,MAAM,CAAC,OAAO,CACb,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;YAChB,MAAM,KAAK,GAAG,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,GAAG,EAAU,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;YAC9E,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;gBACxB,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACrB,CAAC;YACD,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC;YACnB,YAAY,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAChC,CAAC,CAAC,EACF,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,IAAI,CAAC,CAChC;KACF,CAAC,EACF,MAAM,CAAC,cAAc,CAAC,cAAc,EAAE,CAAC,KAAK,EAAE,EAAE;QAC/C,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YAC/B,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;gBACjC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAChB,CAAC;QACF,CAAC;QACD,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC;IACxB,CAAC,CAAC,CACF,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,MAAM,OAAO,qBAAsB,SAAQ,IAAI,CAAC,WAAW,CAAC,uBAAuB,CAGjF;IACD,IAAa,OAAO;QACnB,OAAO,2BAA2B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,2BAA2B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC;IACzH,CAAC;CACD;AAMD,MAAM,UAAU,eAAe,CAAI,IAA8D;IAChG,OAAO,MAAM,CAAC,EAAE,CAAC,yBAAyB,CAAC,CAAC,QAAQ,CAAC,EAAQ,IAA4B;QACxF,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,cAAc,CAAC;QACvC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAW,CAAC;QAClC,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAC9B,MAAM,CAAC,cAAc,CAAC,cAAc,EAAE,CAAC,KAAK,EAAE,EAAE;YAC/C,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW,EAAE,CAAC;gBAChC,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;oBACjC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBACjB,CAAC;YACF,CAAC;YACD,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC,CAAC,CACF,CAAC;QACF,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,OAAO,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC3E,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,IAAI,IAAI,CAAC,GAAG,QAAQ,CAAC,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;YACpF,OAAO,KAAK,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,qBAAqB,CAAC,EAAE,MAAM,EAAE,CAAC,GAAG,MAAM,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;QACxG,CAAC;QACD,OAAO,MAAM,CAAC;IACf,CAAC,CAAC,CAAC;AACJ,CAAC"}
package/dist/tupleIn.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Effect, Schema } from "effect";
2
2
  import type { Model } from "effect/schema";
3
- import type { SqlClient } from "effect/sql";
3
+ import { SqlClient } from "effect/sql";
4
+ export type TupleColumnType = "bool" | "bpchar" | "bytea" | "cidr" | "date" | "float4" | "float8" | "inet" | "int2" | "int4" | "int8" | "interval" | "jsonb" | "name" | "numeric" | "oid" | "regclass" | "text" | "time" | "timestamp" | "timestamptz" | "timetz" | "uuid" | "varchar";
4
5
  export declare const tupleFilter: unique symbol;
5
6
  type Row<TModel extends Model.Any> = Schema.Struct.Type<TModel["fields"]>;
6
7
  type Key<TModel extends Model.Any> = Extract<keyof Row<TModel>, string>;
@@ -9,11 +10,12 @@ export interface TupleFilter<TModel extends Model.Any> {
9
10
  readonly model: TModel;
10
11
  readonly fields: readonly Key<TModel>[];
11
12
  readonly values: readonly (readonly unknown[])[];
13
+ readonly types: readonly TupleColumnType[];
12
14
  };
13
15
  }
14
16
  export declare function tupleIn<TModel extends Model.Any, const TFields extends readonly [Key<TModel>, Key<TModel>, ...Key<TModel>[]]>(model: TModel, fields: TFields, values: readonly {
15
17
  readonly [TIndex in keyof TFields]: Row<TModel>[TFields[TIndex]];
16
- }[]): TupleFilter<TModel>;
18
+ }[], types: Readonly<Record<TFields[number], TupleColumnType>>): TupleFilter<TModel>;
17
19
  export declare function tuplePredicate(input: Partial<TupleFilter<Model.Any>>, sql: SqlClient.SqlClient, fieldAt: (key: string) => Effect.Effect<Schema.Constraint, Schema.SchemaError>): Effect.Effect<undefined, never, never> | Effect.Effect<import("effect/sql/Statement").Fragment, Schema.SchemaError, unknown>;
18
20
  export {};
19
21
  //# sourceMappingURL=tupleIn.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tupleIn.d.ts","sourceRoot":"","sources":["../src/tupleIn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,eAAO,MAAM,WAAW,eAAwB,CAAC;AACjD,KAAK,GAAG,CAAC,MAAM,SAAS,KAAK,CAAC,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC1E,KAAK,GAAG,CAAC,MAAM,SAAS,KAAK,CAAC,GAAG,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;AAExE,MAAM,WAAW,WAAW,CAAC,MAAM,SAAS,KAAK,CAAC,GAAG;IACpD,QAAQ,CAAC,CAAC,WAAW,CAAC,EAAE;QACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QACxC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,SAAS,OAAO,EAAE,CAAC,EAAE,CAAC;KACjD,CAAC;CACF;AAED,wBAAgB,OAAO,CAAC,MAAM,SAAS,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,OAAO,SAAS,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAC5H,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,SAAS;IAAE,QAAQ,EAAE,MAAM,IAAI,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;CAAE,EAAE,GACrF,WAAW,CAAC,MAAM,CAAC,CAErB;AAED,wBAAgB,cAAc,CAC7B,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EACtC,GAAG,EAAE,SAAS,CAAC,SAAS,EACxB,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,WAAW,CAAC,gIAgB9E"}
1
+ {"version":3,"file":"tupleIn.d.ts","sourceRoot":"","sources":["../src/tupleIn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGvC,MAAM,MAAM,eAAe,GACxB,MAAM,GACN,QAAQ,GACR,OAAO,GACP,MAAM,GACN,MAAM,GACN,QAAQ,GACR,QAAQ,GACR,MAAM,GACN,MAAM,GACN,MAAM,GACN,MAAM,GACN,UAAU,GACV,OAAO,GACP,MAAM,GACN,SAAS,GACT,KAAK,GACL,UAAU,GACV,MAAM,GACN,MAAM,GACN,WAAW,GACX,aAAa,GACb,QAAQ,GACR,MAAM,GACN,SAAS,CAAC;AAEb,eAAO,MAAM,WAAW,eAAwB,CAAC;AACjD,KAAK,GAAG,CAAC,MAAM,SAAS,KAAK,CAAC,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC1E,KAAK,GAAG,CAAC,MAAM,SAAS,KAAK,CAAC,GAAG,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,CAAC;AAExE,MAAM,WAAW,WAAW,CAAC,MAAM,SAAS,KAAK,CAAC,GAAG;IACpD,QAAQ,CAAC,CAAC,WAAW,CAAC,EAAE;QACvB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QACvB,QAAQ,CAAC,MAAM,EAAE,SAAS,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QACxC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,SAAS,OAAO,EAAE,CAAC,EAAE,CAAC;QACjD,QAAQ,CAAC,KAAK,EAAE,SAAS,eAAe,EAAE,CAAC;KAC3C,CAAC;CACF;AAED,wBAAgB,OAAO,CAAC,MAAM,SAAS,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,OAAO,SAAS,SAAS,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAC5H,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,OAAO,EACf,MAAM,EAAE,SAAS;IAAE,QAAQ,EAAE,MAAM,IAAI,MAAM,OAAO,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;CAAE,EAAE,EACvF,KAAK,EAAE,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,eAAe,CAAC,CAAC,GACvD,WAAW,CAAC,MAAM,CAAC,CAErB;AAED,wBAAgB,cAAc,CAC7B,KAAK,EAAE,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EACtC,GAAG,EAAE,SAAS,CAAC,SAAS,EACxB,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,MAAM,CAAC,WAAW,CAAC,gIAoB9E"}
package/dist/tupleIn.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import { Effect, Schema } from "effect";
2
+ import { SqlClient } from "effect/sql";
3
+ import { tupleUnnest } from "#tupleUnnest.ts";
2
4
  export const tupleFilter = Symbol("sql/tupleIn");
3
- export function tupleIn(model, fields, values) {
4
- return { [tupleFilter]: { fields, model, values } };
5
+ export function tupleIn(model, fields, values, types) {
6
+ return { [tupleFilter]: { fields, model, types: fields.map((field) => types[field]), values } };
5
7
  }
6
8
  export function tuplePredicate(input, sql, fieldAt) {
7
9
  const tuple = input[tupleFilter];
@@ -12,9 +14,15 @@ export function tuplePredicate(input, sql, fieldAt) {
12
14
  const fields = yield* Effect.forEach(tuple.fields, fieldAt);
13
15
  const encode = Schema.encodeUnknownEffect(Schema.Tuple(fields));
14
16
  const columns = sql.csv(tuple.fields.map((field) => sql `${sql(field)}`));
15
- // Row comparisons retain column type inference for string-encoded numerics without PostgreSQL's deeply nested row-IN rewrite.
16
- const rows = yield* Effect.forEach(tuple.values, (value) => Effect.map(encode(value), (encoded) => sql `(${columns}) = (${sql.csv(encoded.map((entry) => sql `${entry}`))})`));
17
- return rows.length === 0 ? sql `1 = 0` : sql.or(rows);
17
+ const rows = yield* Effect.forEach(tuple.values, (value) => encode(value));
18
+ if (rows.length === 0) {
19
+ return sql `1 = 0`;
20
+ }
21
+ if (sql.onDialectOrElse({ orElse: () => false, pg: () => true })) {
22
+ const relation = yield* tupleUnnest(tuple.types, rows).pipe(Effect.provideService(SqlClient.SqlClient, sql));
23
+ return sql `(${columns}) in (select * from ${relation})`;
24
+ }
25
+ return sql.or(rows.map((row) => sql `(${columns}) = (${sql.csv(row.map((entry) => sql `${entry}`))})`));
18
26
  });
19
27
  }
20
28
  //# sourceMappingURL=tupleIn.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"tupleIn.js","sourceRoot":"","sources":["../src/tupleIn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAIxC,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC;AAYjD,MAAM,UAAU,OAAO,CACtB,KAAa,EACb,MAAe,EACf,MAAuF;IAEvF,OAAO,EAAE,CAAC,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,cAAc,CAC7B,KAAsC,EACtC,GAAwB,EACxB,OAA8E;IAE9E,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC;IACjC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC1B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC5D,MAAM,MAAM,GAAG,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;QAChE,MAAM,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAA,GAAG,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QACzE,8HAA8H;QAC9H,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAC1D,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,CAAC,GAAG,CAAA,IAAI,OAAO,QAAQ,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAA,GAAG,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,CAC/G,CAAC;QACF,OAAO,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAA,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"tupleIn.js","sourceRoot":"","sources":["../src/tupleIn.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAExC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAC;AA4B9C,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC,aAAa,CAAC,CAAC;AAajD,MAAM,UAAU,OAAO,CACtB,KAAa,EACb,MAAe,EACf,MAAuF,EACvF,KAAyD;IAEzD,OAAO,EAAE,CAAC,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC,KAAsB,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,CAAC;AAClH,CAAC;AAED,MAAM,UAAU,cAAc,CAC7B,KAAsC,EACtC,GAAwB,EACxB,OAA8E;IAE9E,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC;IACjC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACzB,OAAO,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAClC,CAAC;IACD,OAAO,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAC1B,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC5D,MAAM,MAAM,GAAG,MAAM,CAAC,mBAAmB,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC;QAChE,MAAM,OAAO,GAAG,GAAG,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAA,GAAG,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC;QACzE,MAAM,IAAI,GAAoC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAC5G,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvB,OAAO,GAAG,CAAA,OAAO,CAAC;QACnB,CAAC;QACD,IAAI,GAAG,CAAC,eAAe,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,KAAK,EAAE,EAAE,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,CAAC;YAClE,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,WAAW,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,cAAc,CAAC,SAAS,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,CAAC;YAC7G,OAAO,GAAG,CAAA,IAAI,OAAO,uBAAuB,QAAQ,GAAG,CAAC;QACzD,CAAC;QACD,OAAO,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAA,IAAI,OAAO,QAAQ,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAA,GAAG,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACvG,CAAC,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,5 @@
1
+ import { Effect } from "effect";
2
+ import { SqlClient } from "effect/sql";
3
+ import type { TupleColumnType } from "#tupleIn.ts";
4
+ export declare const tupleUnnest: (types: readonly TupleColumnType[], rows: readonly (readonly unknown[])[]) => Effect.Effect<import("effect/sql/Statement").Statement<import("effect/sql/SqlConnection").Row>, never, SqlClient.SqlClient>;
5
+ //# sourceMappingURL=tupleUnnest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tupleUnnest.d.ts","sourceRoot":"","sources":["../src/tupleUnnest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAU,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,eAAO,MAAM,WAAW,2MAoBtB,CAAC"}
@@ -0,0 +1,26 @@
1
+ import { Effect, Result } from "effect";
2
+ import { SqlClient } from "effect/sql";
3
+ export const tupleUnnest = Effect.fnUntraced(function* (types, rows) {
4
+ const sql = yield* SqlClient.SqlClient;
5
+ const pg = yield* Effect.promise(() => import("@effect/sql-pg/PgTypes"));
6
+ const arrays = types.map((type, index) => {
7
+ const entries = rows.map((row) => row[index]);
8
+ const values = entries.map((value) => (pg.isParameter(value) ? value.value : value));
9
+ // String-encoded fields rely on PostgreSQL input casts, including numerics and dates.
10
+ const first = entries.find((value) => value !== null && value !== undefined);
11
+ let oid = pg.OID[type];
12
+ if (pg.isParameter(first)) {
13
+ oid = first.oid;
14
+ }
15
+ else if (first instanceof Date) {
16
+ oid = pg.OID.timestamptz;
17
+ }
18
+ else if (values.every((value) => value === null || typeof value === "string")) {
19
+ oid = pg.OID.text;
20
+ }
21
+ const parameter = Result.getOrThrow(pg.array(values, oid));
22
+ return sql `${parameter}::${sql.literal(type)}[]`;
23
+ });
24
+ return sql `unnest(${sql.csv(arrays)})`;
25
+ });
26
+ //# sourceMappingURL=tupleUnnest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tupleUnnest.js","sourceRoot":"","sources":["../src/tupleUnnest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGvC,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,KAAiC,EAAE,IAAqC;IAC/H,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,SAAS,CAAC,SAAS,CAAC;IACvC,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,wBAAwB,CAAC,CAAC,CAAC;IACzE,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;QACxC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;QACrF,sFAAsF;QACtF,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,CAAC,CAAC;QAC7E,IAAI,GAAG,GAAW,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC/B,IAAI,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3B,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC;QACjB,CAAC;aAAM,IAAI,KAAK,YAAY,IAAI,EAAE,CAAC;YAClC,GAAG,GAAG,EAAE,CAAC,GAAG,CAAC,WAAW,CAAC;QAC1B,CAAC;aAAM,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,CAAC,EAAE,CAAC;YACjF,GAAG,GAAG,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC;QACnB,CAAC;QACD,MAAM,SAAS,GAAG,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,CAAC;QAC3D,OAAO,GAAG,CAAA,GAAG,SAAS,KAAK,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;IAClD,CAAC,CAAC,CAAC;IACH,OAAO,GAAG,CAAA,UAAU,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,GAAG,CAAC;AACxC,CAAC,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@shivaedev/sql",
3
- "version": "0.27.0",
3
+ "version": "0.29.0",
4
4
  "description": "Schema-derived repositories over native Effect SQL",
5
5
  "keywords": [
6
6
  "effect"
@@ -50,9 +50,9 @@
50
50
  "@effect/platform-node": "4.0.6",
51
51
  "@effect/sql-pg": "4.0.6",
52
52
  "@effect/sql-sqlite-node": "4.0.6",
53
- "@shivaedev/quality": "0.48.0",
54
- "@shivaedev/test-sql": "0.15.0",
55
- "@shivaedev/test-story": "0.26.2",
53
+ "@shivaedev/quality": "0.52.0",
54
+ "@shivaedev/test-sql": "0.15.2",
55
+ "@shivaedev/test-story": "0.31.2",
56
56
  "@types/node": "24.19.2",
57
57
  "effect": "4.0.6",
58
58
  "typescript": "7.0.2",
@@ -31,7 +31,15 @@ export const makePostgresClient = Effect.fn("Sql.makePostgresClient")(function*
31
31
  [PgClient.TypeId]: PgClient.TypeId,
32
32
  config,
33
33
  json: (value: unknown) => Statement.fragment([json(value)]),
34
- listen: (channel: string) => Effect.flatMap(pool.reserve, (connection) => connection.listen(channel)),
34
+ listen: (channel: string) =>
35
+ Effect.gen(function* () {
36
+ // TTL checkout retains expired leases until its scope closes; LISTEN must not pin those old backends for another lifetime.
37
+ if (pool.config.connectionTTL !== undefined) {
38
+ yield* pool.use(() => Effect.void);
39
+ }
40
+ const connection = yield* pool.reserve;
41
+ return yield* connection.listen(channel);
42
+ }),
35
43
  notify: (channel: string, payload: string) =>
36
44
  pool.use((connection) => connection.query("SELECT pg_notify($1, $2)", [channel, payload])).pipe(Effect.asVoid),
37
45
  }) satisfies PgClient.PgClient;
@@ -1,13 +1,36 @@
1
1
  import { Effect, Schema } from "effect";
2
2
  import type { Model } from "effect/schema";
3
- import type { SqlClient } from "effect/sql";
3
+ import { SqlClient } from "effect/sql";
4
4
  import type { Fragment } from "effect/sql/Statement";
5
5
  import { pinnedGroups } from "#pinnedGroups.ts";
6
6
  import type { LatestPerGroup } from "#repository.ts";
7
7
  import { makeFilter } from "#repositoryFilter.ts";
8
+ import { tupleUnnest } from "#tupleUnnest.ts";
9
+
10
+ function groupMasks(input: readonly (readonly unknown[])[]) {
11
+ const masks = new Map<string, (readonly unknown[])[]>();
12
+ for (const group of input) {
13
+ const mask = group.map((value) => (value === null ? "0" : "1")).join("");
14
+ const groups = masks.get(mask) ?? [];
15
+ groups.push(group);
16
+ masks.set(mask, groups);
17
+ }
18
+ return [...masks.values()];
19
+ }
8
20
 
9
21
  export function latestPerGroup(model: Model.Any, sql: SqlClient.SqlClient, tableName: string) {
10
22
  const { fieldAt, where } = makeFilter(model, sql);
23
+ function groupSource(groups: readonly (readonly unknown[])[], partitionBy: readonly string[], fromTuple: boolean) {
24
+ const partitions = sql.csv(partitionBy.map((key) => sql`${sql(key)}`));
25
+ if (fromTuple) {
26
+ const mask = partitionBy.map((key, index) => (groups[0]?.[index] === null ? sql`${sql(key)} is null` : sql`${sql(key)} is not null`));
27
+ return sql`select ${partitions} from ${sql("__effect_sql_tuple_groups")} where ${sql.and(mask)}`;
28
+ }
29
+ const values = sql.join(" union ", false)(groups.map((group) => sql`select ${sql.csv(group.map((value) => sql`${value}`))}`));
30
+ // The empty projection gives scalar pins their stored column types.
31
+ return sql`select ${partitions} from ${sql(tableName)} where false union ${values}`;
32
+ }
33
+
11
34
  return Effect.fnUntraced(function* (query: LatestPerGroup<Model.Any>, order: Fragment, rowLimit: number) {
12
35
  yield* Effect.forEach(query.partitionBy, fieldAt);
13
36
  const predicate = yield* where(query.where, true);
@@ -20,28 +43,32 @@ export function latestPerGroup(model: Model.Any, sql: SqlClient.SqlClient, table
20
43
  }
21
44
  if (pinned !== undefined && sql.onDialectOrElse({ orElse: () => false, pg: () => true })) {
22
45
  const remaining = yield* where(pinned.remaining, true);
23
- const masks = new Map<string, (readonly unknown[])[]>();
24
- for (const group of pinned.groups) {
25
- const mask = group.map((value) => (value === null ? "0" : "1")).join("");
26
- const groups = masks.get(mask) ?? [];
27
- groups.push(group);
28
- masks.set(mask, groups);
29
- }
30
- const branches = Array.from(masks.values(), (groups) => {
31
- const values = sql.join(" union ", false)(groups.map((group) => sql`select ${sql.csv(group.map((value) => sql`${value}`))}`));
46
+ const tuple = pinned.tuple;
47
+ const tupleRows = pinned.groups.map((group) => tuple?.fields.map((key) => group[query.partitionBy.indexOf(key)]) ?? group);
48
+ const tupleSource =
49
+ tuple === undefined
50
+ ? sql``
51
+ : sql`with ${sql("__effect_sql_tuple_groups")} as materialized (
52
+ select distinct * from ${yield* tupleUnnest(tuple.types, tupleRows).pipe(Effect.provideService(SqlClient.SqlClient, sql))}
53
+ as ${sql("__effect_sql_tuples")} (${sql.csv(tuple.fields.map((key) => sql`${sql(key)}`))})
54
+ ), ${sql("__effect_sql_tuple_limit")} as (select ${rowLimit}::bigint as ${sql("limit")})`;
55
+ const limit = tuple === undefined ? sql`${rowLimit}` : sql`(select ${sql("limit")} from ${sql("__effect_sql_tuple_limit")})`;
56
+
57
+ const branches = groupMasks(pinned.groups).map((groups) => {
58
+ const source = groupSource(groups, query.partitionBy, tuple !== undefined);
32
59
  const matches = query.partitionBy.map((key, index) =>
33
60
  groups[0]?.[index] === null ? sql`${sql(key)} is null` : sql`${sql(key)} = ${sql("__effect_sql_groups")}.${sql(key)}`,
34
61
  );
35
- // The empty table projection gives encoded parameters their actual PostgreSQL column types.
62
+ // Null masks keep ordinary equality available to each non-null index column.
36
63
  return sql`select ${sql("__effect_sql_latest")}.* from (
37
- select ${partitions} from ${sql(tableName)} where false union ${values}
64
+ ${source}
38
65
  ) as ${sql("__effect_sql_groups")} cross join lateral (
39
- select ${columns} from ${sql(tableName)} where ${sql.and([...matches, remaining])} ${order} limit ${rowLimit}
66
+ select ${columns} from ${sql(tableName)} where ${sql.and([...matches, remaining])} ${order} limit ${limit}
40
67
  ) as ${sql("__effect_sql_latest")}`;
41
68
  });
42
69
  const ordering = sql.csv(query.orderBy.map(({ field, direction }) => sql`${sql(field)} ${sql.literal(direction === "desc" ? "desc" : "asc")}`));
43
70
  const rows =
44
- yield* sql`select ${columns} from (${sql.join(" union all ")(branches)}) as ${sql("__effect_sql_pinned")} order by ${partitions}, ${ordering}`;
71
+ yield* sql`${tupleSource} select ${columns} from (${sql.join(" union all ")(branches)}) as ${sql("__effect_sql_pinned")} order by ${partitions}, ${ordering}`;
45
72
  return yield* Schema.decodeUnknownEffect(Schema.Array(Schema.Struct(model.fields)))(rows);
46
73
  }
47
74
  let rankColumn = "__effect_sql_rank";
@@ -1,4 +1,4 @@
1
- import { Clock, Deferred, type Duration, Effect, Queue, Random, Scope } from "effect";
1
+ import { Clock, Deferred, Duration, Effect, Queue, Random, Scope } from "effect";
2
2
  import * as Reactivity from "effect/reactivity/Reactivity";
3
3
  import type { SqlError } from "effect/sql/SqlError";
4
4
  import { decodeKeys, resyncPayload } from "./notifyPayload.ts";
@@ -30,6 +30,9 @@ export const listenForChanges = Effect.fn("PostgresChanges.listenForChanges")(fu
30
30
  Effect.gen(function* () {
31
31
  const scope = yield* listenerScope(timeout);
32
32
  const notifications = yield* (options.listenerClient ?? client).listen(channel).pipe(Scope.provide(scope), deadline);
33
+ const interval = Duration.toMillis(Duration.fromInputUnsafe(options.heartbeatInterval ?? "5 seconds"));
34
+ const lifetime = Duration.toMillis(Duration.fromInputUnsafe(options.connectionTTL ?? Duration.infinity));
35
+ const renewAt = (yield* Clock.currentTimeMillis) + (lifetime === 0 ? interval : lifetime);
33
36
  const token = JSON.stringify({ heartbeat: `${yield* Random.nextInt}:${yield* Random.nextInt}` });
34
37
  let ack = yield* Deferred.make<void>();
35
38
  function receive(payload: string) {
@@ -64,13 +67,14 @@ export const listenForChanges = Effect.fn("PostgresChanges.listenForChanges")(fu
64
67
  lostAt = undefined;
65
68
  }
66
69
  yield* Deferred.succeed(ready, undefined);
67
- yield* Effect.forever(
68
- Effect.gen(function* () {
69
- yield* Effect.sleep(options.heartbeatInterval ?? "5 seconds");
70
- ack = yield* Deferred.make<void>();
71
- yield* Effect.andThen(send, Deferred.await(ack)).pipe(deadline);
72
- }),
73
- );
70
+ while (true) {
71
+ yield* Effect.sleep(Math.min(interval, Math.max(0, renewAt - (yield* Clock.currentTimeMillis))));
72
+ if ((yield* Clock.currentTimeMillis) >= renewAt) {
73
+ return;
74
+ }
75
+ ack = yield* Deferred.make<void>();
76
+ yield* Effect.andThen(send, Deferred.await(ack)).pipe(deadline);
77
+ }
74
78
  });
75
79
  yield* Effect.raceFirst(listening, monitor);
76
80
  }).pipe(Effect.onExit(() => resync)),
@@ -90,9 +94,8 @@ export const listenForChanges = Effect.fn("PostgresChanges.listenForChanges")(fu
90
94
  }
91
95
  lostAt = yield* Clock.currentTimeMillis;
92
96
  yield* Effect.logWarning("PostgreSQL change listener lost; resynchronizing live streams", cause);
93
- }),
97
+ }).pipe(Effect.andThen(Effect.sleep(options.retryDelay ?? "1 second"))),
94
98
  ),
95
- Effect.andThen(Effect.sleep(options.retryDelay ?? "1 second")),
96
99
  ),
97
100
  ).pipe(Effect.forkScoped);
98
101
  yield* Deferred.await(ready);
@@ -18,7 +18,7 @@ export function pinnedGroups(
18
18
  (value) => typeof value === "object" && value !== null && "in" in value && Array.isArray(value.in) && value.in.length === 0,
19
19
  )
20
20
  ) {
21
- return Effect.succeed({ groups: [], remaining: input });
21
+ return Effect.succeed({ groups: [], remaining: input, tuple: undefined });
22
22
  }
23
23
  const exactTuple = tuple !== undefined && tuple.fields.length === partitionBy.length && partitionBy.every((key) => tuple.fields.includes(key));
24
24
  let groups: readonly (readonly unknown[])[] = [[]];
@@ -40,7 +40,9 @@ export function pinnedGroups(
40
40
  const { fieldAt } = makeFilter(model, sql);
41
41
  return Effect.gen(function* () {
42
42
  const fields = yield* Effect.forEach(partitionBy, fieldAt);
43
- const groupsEncoded = yield* Effect.forEach(groups, (group) => Schema.encodeUnknownEffect(Schema.Tuple(fields))(group));
44
- return { groups: groupsEncoded, remaining };
43
+ const groupsEncoded: readonly (readonly unknown[])[] = yield* Effect.forEach(groups, (group) =>
44
+ Schema.encodeUnknownEffect(Schema.Tuple(fields))(group),
45
+ );
46
+ return { groups: groupsEncoded, remaining, tuple: exactTuple ? tuple : undefined };
45
47
  });
46
48
  }
@@ -19,6 +19,7 @@ export interface PostgresChangesOptions {
19
19
  readonly channel: string;
20
20
  readonly client: PostgresChangeClient;
21
21
  readonly connectionSetting?: string;
22
+ readonly "connectionTTL"?: Duration.Input | undefined;
22
23
  readonly heartbeatInterval?: Duration.Input;
23
24
  readonly heartbeatTimeout?: Duration.Input;
24
25
  readonly listenerClient?: Pick<PostgresChangeClient, "listen">;
@@ -10,7 +10,10 @@ import { databaseAddress } from "#databaseAddress.ts";
10
10
 
11
11
  export interface PostgresOptions extends SqlNameTransforms {
12
12
  readonly applicationName?: string | undefined;
13
+ readonly "connectionTTL"?: PgPool.Config["connectionTTL"];
14
+ readonly idleTimeout?: PgPool.Config["idleTimeout"];
13
15
  readonly maxConnections?: number | undefined;
16
+ readonly minConnections?: PgPool.Config["minConnections"];
14
17
  }
15
18
 
16
19
  export function postgresLayer(url: Redacted.Redacted<string>, options?: PostgresOptions) {
package/src/repository.ts CHANGED
@@ -6,6 +6,7 @@ import { makeCompositeCrud, type PrimaryKey, type Repository } from "#compositeR
6
6
  import { type ConflictColumns, type ConflictWrites, makeConflictWrites } from "#conflictWrites.ts";
7
7
  import { makeInsertFromSelect, type SelectWrites } from "#insertFromSelect.ts";
8
8
  import { repositoryChanges } from "#repositoryChanges.ts";
9
+ import { type BucketRegistrationError, type RepositoryOptions, validateRepositoryCollection } from "#repositoryCollection.ts";
9
10
  import type { FindManyFilter, RepositoryFilter, RepositoryListFilter } from "#repositoryFilter.ts";
10
11
  import { makeQueries, type Queries } from "#repositoryQueries.ts";
11
12
  import { type IdColumn, makeVersionedWrites, type VersionColumn, type VersionedWrites } from "#versionedRepository.ts";
@@ -39,12 +40,6 @@ export class OptimisticConcurrencyError extends Data.TaggedError("OptimisticConc
39
40
  readonly reason: "missing" | "stale";
40
41
  }> {}
41
42
 
42
- interface RepositoryOptions<TId> {
43
- readonly idColumn: TId;
44
- readonly spanPrefix: string;
45
- readonly tableName: string;
46
- }
47
-
48
43
  type VersionedRepository<TModel extends Model.Any, TId extends PrimaryKey<TModel>> = Omit<
49
44
  Repository<TModel, TId>,
50
45
  keyof VersionedWrites<TModel, TId>
@@ -67,7 +62,7 @@ export function makeRepository<
67
62
  ): Effect.Effect<
68
63
  VersionedRepository<TModel, TId> &
69
64
  Pick<ConflictWrites<TModel, never, TModel["fields"][Extract<TWhere, keyof TModel["fields"]>]["EncodingServices"]>, "insertOrIgnore">,
70
- never,
65
+ BucketRegistrationError,
71
66
  SqlClient.SqlClient
72
67
  >;
73
68
  export function makeRepository<TModel extends Model.Any, const TId extends PrimaryKey<TModel>>(
@@ -77,7 +72,7 @@ export function makeRepository<TModel extends Model.Any, const TId extends Prima
77
72
  readonly conflictColumns?: never;
78
73
  readonly conflictWhere?: never;
79
74
  },
80
- ): Effect.Effect<VersionedRepository<TModel, TId>, never, SqlClient.SqlClient>;
75
+ ): Effect.Effect<VersionedRepository<TModel, TId>, BucketRegistrationError, SqlClient.SqlClient>;
81
76
  export function makeRepository<
82
77
  TModel extends Model.Any,
83
78
  const TId extends PrimaryKey<TModel>,
@@ -100,13 +95,17 @@ export function makeRepository<
100
95
  TConflict[number] | (TId extends readonly PropertyKey[] ? TId[number] : TId),
101
96
  TModel["fields"][Extract<TWhere, keyof TModel["fields"]>]["EncodingServices"]
102
97
  >,
103
- never,
98
+ BucketRegistrationError,
104
99
  SqlClient.SqlClient
105
100
  >;
106
101
  export function makeRepository<TModel extends Model.Any, const TId extends PrimaryKey<TModel>>(
107
102
  model: TModel,
108
103
  options: RepositoryOptions<TId> & { readonly versionColumn?: never; readonly conflictColumns?: never; readonly conflictWhere?: never },
109
- ): Effect.Effect<Repository<TModel, TId> & BatchWrites<TModel> & SelectWrites<TModel> & Queries<TModel>, never, SqlClient.SqlClient>;
104
+ ): Effect.Effect<
105
+ Repository<TModel, TId> & BatchWrites<TModel> & SelectWrites<TModel> & Queries<TModel>,
106
+ BucketRegistrationError,
107
+ SqlClient.SqlClient
108
+ >;
110
109
  export function makeRepository<TModel extends Model.Any, TId extends IdColumn<TModel>>(
111
110
  model: TModel,
112
111
  options: RepositoryOptions<TId | readonly [TId, TId, ...TId[]]> & {
@@ -114,8 +113,9 @@ export function makeRepository<TModel extends Model.Any, TId extends IdColumn<TM
114
113
  readonly conflictColumns?: readonly string[];
115
114
  readonly conflictWhere?: Readonly<Record<string, unknown>>;
116
115
  },
117
- ): Effect.Effect<unknown, never, SqlClient.SqlClient> {
116
+ ): Effect.Effect<unknown, BucketRegistrationError, SqlClient.SqlClient> {
118
117
  return Effect.fnUntraced(function* () {
118
+ yield* validateRepositoryCollection(model.fields, options);
119
119
  const sql = yield* SqlClient.SqlClient;
120
120
  const composite = typeof options.idColumn === "object";
121
121
  const idColumns = (composite ? options.idColumn : [options.idColumn]).map(String);
@@ -0,0 +1,46 @@
1
+ import { Data, Effect, Order, type Schema } from "effect";
2
+ import { declaredBuckets } from "@shivaedev/changes/buckets.ts";
3
+
4
+ export interface RepositoryCollection {
5
+ readonly bucket: Readonly<Record<string, unknown>>;
6
+ readonly name: string;
7
+ }
8
+
9
+ export interface RepositoryOptions<TId> {
10
+ readonly collection?: RepositoryCollection;
11
+ readonly idColumn: TId;
12
+ readonly spanPrefix: string;
13
+ readonly tableName: string;
14
+ }
15
+
16
+ export class BucketRegistrationError extends Data.TaggedError("BucketRegistrationError")<{
17
+ readonly tableName: string;
18
+ readonly collectionName: string | undefined;
19
+ readonly modelFields: readonly string[];
20
+ readonly collectionFields: readonly string[];
21
+ }> {
22
+ override get message() {
23
+ return `Bucket registration for ${this.tableName}: model fields ${JSON.stringify(this.modelFields)} disagree with collection ${this.collectionName ?? "(missing)"} fields ${JSON.stringify(this.collectionFields)}`;
24
+ }
25
+ }
26
+
27
+ export const validateRepositoryCollection = Effect.fnUntraced(function* (
28
+ fields: Schema.Struct.Fields,
29
+ options: { readonly tableName: string; readonly collection?: RepositoryCollection },
30
+ ) {
31
+ const modelFields = declaredBuckets(fields)
32
+ .map(({ field }) => field)
33
+ .sort(Order.String);
34
+ const collectionFields = Object.keys(options.collection?.bucket ?? {}).sort(Order.String);
35
+ if (
36
+ (options.collection === undefined && modelFields.length === 0)
37
+ || (options.collection?.name === options.tableName
38
+ && modelFields.length === collectionFields.length
39
+ && modelFields.every((field, index) => field === collectionFields[index]))
40
+ ) {
41
+ return;
42
+ }
43
+ return yield* Effect.fail(
44
+ new BucketRegistrationError({ collectionFields, collectionName: options.collection?.name, modelFields, tableName: options.tableName }),
45
+ );
46
+ });