@thomasfosterau/effect-drizzle-ext 0.0.0 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +508 -2
  3. package/dist/columns.d.ts +113 -0
  4. package/dist/columns.d.ts.map +1 -0
  5. package/dist/columns.js +146 -0
  6. package/dist/columns.js.map +1 -0
  7. package/dist/constraints.d.ts +117 -0
  8. package/dist/constraints.d.ts.map +1 -0
  9. package/dist/constraints.js +93 -0
  10. package/dist/constraints.js.map +1 -0
  11. package/dist/database.d.ts +189 -0
  12. package/dist/database.d.ts.map +1 -0
  13. package/dist/database.js +51 -0
  14. package/dist/database.js.map +1 -0
  15. package/dist/derive.d.ts +97 -0
  16. package/dist/derive.d.ts.map +1 -0
  17. package/dist/derive.js +142 -0
  18. package/dist/derive.js.map +1 -0
  19. package/dist/errors.d.ts +157 -0
  20. package/dist/errors.d.ts.map +1 -0
  21. package/dist/errors.js +287 -0
  22. package/dist/errors.js.map +1 -0
  23. package/dist/index-CFhgcF4N.d.ts +418 -0
  24. package/dist/index-CFhgcF4N.d.ts.map +1 -0
  25. package/dist/index.d.ts +2 -0
  26. package/dist/index.js +1 -0
  27. package/dist/jsonapi/derive.d.ts +28 -0
  28. package/dist/jsonapi/derive.d.ts.map +1 -0
  29. package/dist/jsonapi/derive.js +28 -0
  30. package/dist/jsonapi/derive.js.map +1 -0
  31. package/dist/jsonapi/index.d.ts +189 -0
  32. package/dist/jsonapi/index.d.ts.map +1 -0
  33. package/dist/jsonapi/index.js +164 -0
  34. package/dist/jsonapi/index.js.map +1 -0
  35. package/dist/query.d.ts +111 -0
  36. package/dist/query.d.ts.map +1 -0
  37. package/dist/query.js +142 -0
  38. package/dist/query.js.map +1 -0
  39. package/dist/references.d.ts +38 -0
  40. package/dist/references.d.ts.map +1 -0
  41. package/dist/references.js +60 -0
  42. package/dist/references.js.map +1 -0
  43. package/dist/sort.d.ts +129 -0
  44. package/dist/sort.d.ts.map +1 -0
  45. package/dist/sort.js +225 -0
  46. package/dist/sort.js.map +1 -0
  47. package/dist/src-CBeuhy5b.js +478 -0
  48. package/dist/src-CBeuhy5b.js.map +1 -0
  49. package/dist/update.d.ts +61 -0
  50. package/dist/update.d.ts.map +1 -0
  51. package/dist/update.js +64 -0
  52. package/dist/update.js.map +1 -0
  53. package/dist/versioned-chain.d.ts +524 -0
  54. package/dist/versioned-chain.d.ts.map +1 -0
  55. package/dist/versioned-chain.js +298 -0
  56. package/dist/versioned-chain.js.map +1 -0
  57. package/package.json +102 -4
  58. package/src/columns.ts +186 -0
  59. package/src/constraints.ts +225 -0
  60. package/src/database.ts +273 -0
  61. package/src/derivation-error.ts +27 -0
  62. package/src/derive.ts +320 -0
  63. package/src/errors.ts +466 -0
  64. package/src/index.ts +653 -0
  65. package/src/jsonapi/derive.ts +74 -0
  66. package/src/jsonapi/index.ts +355 -0
  67. package/src/query.ts +183 -0
  68. package/src/references.ts +103 -0
  69. package/src/sort.ts +368 -0
  70. package/src/tree.ts +367 -0
  71. package/src/update.ts +88 -0
  72. package/src/versioned-chain.ts +1027 -0
@@ -0,0 +1,189 @@
1
+ import { F as Tree, N as LevelSpec, c as DbTableOf$1, h as UnwrapColumn, i as DbColumn, l as FieldColumnsMetaOf, m as TableMeta, n as ColumnBounds, p as TableAnnotated$1, s as DbTable } from "../index-CFhgcF4N.js";
2
+ import * as Option from "effect/Option";
3
+ import * as Schema from "effect/Schema";
4
+ import { Resource } from "@thomasfosterau/effect-jsonapi";
5
+ /**
6
+ * Any `Resource.make` resource definition. Re-exported so a derivation layer
7
+ * can name the constraint of {@link ResourceColumnsMetaOf} without importing
8
+ * `@thomasfosterau/effect-jsonapi` directly.
9
+ */
10
+ export type AnyResource = Resource.Any;
11
+ /**
12
+ * The underlying schema of an attribute field. A `Resource.attribute`
13
+ * descriptor carries the schema `S` in its config (recoverable through the
14
+ * `Resource.Attribute` type), peeling the `resource: "optional"` `optionalKey`
15
+ * wrapper that would otherwise bury the `dbColumn` phantom; a plain annotated
16
+ * attribute (e.g. `dbColumn(schema, …)` passed directly) is its own schema.
17
+ */
18
+ type UnderlyingSchema<A> = A extends Resource.Attribute<infer S, infer _R, infer _C, infer _U, infer _Cl> ? S : A;
19
+ /**
20
+ * The db-column metadata map a resource definition projects to, keyed by the
21
+ * attributes that declare a `dbColumn` (others drop out of the map entirely).
22
+ * Recovers each attribute's underlying schema (peeling the descriptor + any
23
+ * `optionalKey` resource wrapper) and reads its phantom via
24
+ * {@link UnwrapColumn}. The resource-shaped counterpart of
25
+ * `FieldColumnsMetaOf`.
26
+ */
27
+ export type ResourceColumnsMetaOf<R extends AnyResource> = { readonly [K in keyof Resource.AttributesOf<R> as [UnwrapColumn<UnderlyingSchema<Resource.AttributesOf<R>[K]>>] extends [never] ? never : K]: UnwrapColumn<UnderlyingSchema<Resource.AttributesOf<R>[K]>>; };
28
+ /**
29
+ * Project a resource definition's per-attribute {@link DbColumn} metadata into
30
+ * plain data the derivation consumes, dropping attributes that declare no
31
+ * column. Reads the annotation via `Resource.attributeAnnotations`, which
32
+ * resolves each attribute schema's annotations — including through the
33
+ * `optionalKey` wrapper a `resource: "optional"` attribute carries, whose AST
34
+ * is the inner schema's. The type-level counterpart is
35
+ * {@link ResourceColumnsMetaOf}.
36
+ */
37
+ export declare const resourceColumns: (resource: AnyResource) => Record<string, DbColumn>;
38
+ /**
39
+ * Project a resource definition's per-attribute numeric bounds into plain data
40
+ * a `CHECK`-constraint derivation consumes: for each `dbColumn`-annotated
41
+ * attribute whose schema carries numeric-bound checks, the bounds paired with
42
+ * the column metadata. Attributes without a column, or without bounds, drop.
43
+ * The Drizzle-emitting counterpart is the `./jsonapi/derive` entry's
44
+ * `deriveCheckConstraints`.
45
+ */
46
+ export declare const resourceColumnBounds: (resource: AnyResource) => Record<string, ColumnBounds>;
47
+ /**
48
+ * The resource-projection shape of one `Resource.make` attribute, as plain
49
+ * data — what an operation factory needs to derive a row → resource mapper
50
+ * and a version-table column map from the model alone:
51
+ *
52
+ * - `date` (a `createdAt` / `updatedAt` / `deletedAt`-style `Date`) → a
53
+ * timestamp axis the mapper reads from the chain rather than a column,
54
+ * - `array` (a materialised-list attribute a flat view does not carry) → a
55
+ * placeholder the mapper fills from a join,
56
+ * - `optional` (an `optionalKey` resource projection) → the value drops when
57
+ * the column is absent rather than surfacing as a present `undefined`,
58
+ * - everything else (including a `NullOr` column read straight off a view) →
59
+ * passthrough.
60
+ *
61
+ * `column` is the column the attribute's `dbColumn` annotation names, `none`
62
+ * for an attribute that declares none.
63
+ */
64
+ export interface ResourceAttributeProjection {
65
+ readonly key: string;
66
+ /** The resource projection is an `optionalKey` (drops when absent). */
67
+ readonly optional: boolean;
68
+ /** The attribute schema accepts `null` (a `Schema.NullOr`). */
69
+ readonly nullable: boolean;
70
+ /** The attribute schema is an array (a materialised-list attribute). */
71
+ readonly array: boolean;
72
+ /** The attribute schema decodes an ISO string to a `Date` (a timestamp axis). */
73
+ readonly date: boolean;
74
+ /** The column the attribute backs, if it declares one. */
75
+ readonly column: Option.Option<string>;
76
+ }
77
+ /**
78
+ * Project a `Resource.make` definition's resource-object attributes into
79
+ * ordered plain-data {@link ResourceAttributeProjection}s (declaration
80
+ * order). Optionality is read off each field's AST (`optionalKey` marks the
81
+ * property optional); nullability, array-ness and date-ness are recovered by
82
+ * behavioural decode probes through each schema's JSON codec (the wire
83
+ * reading), so the read stays robust across schema-AST shapes (a `NullOr` of
84
+ * a transformation, a branded array, …).
85
+ *
86
+ * Input-only attributes (`resource: false`) are not on the resource object,
87
+ * and so not here. The `column` read is {@link resourceColumns}'.
88
+ */
89
+ export declare const resourceAttributeProjections: (resource: AnyResource) => ReadonlyArray<ResourceAttributeProjection>;
90
+ /**
91
+ * A resource definition carrying db-table metadata `M` in a type-level phantom
92
+ * — `TableAnnotated` from the annotation entry with its target narrowed to a
93
+ * resource.
94
+ */
95
+ export type TableAnnotated<R extends AnyResource, M extends TableMeta> = TableAnnotated$1<R, M>;
96
+ /** Recovers the db-table metadata phantom from a resource, or `never`. */
97
+ export type DbTableOf<R> = DbTableOf$1<R>;
98
+ /**
99
+ * Annotate a resource definition with the database table it backs. The
100
+ * annotation entry's `dbTable`, narrowed to `R extends AnyResource`: it
101
+ * attaches the metadata as a non-enumerable symbol property and returns the
102
+ * **same** resource object, so `Resource.make`'s assigned properties (`type`,
103
+ * `Id`, `identifier`, …) and the referential identity a registry compares by
104
+ * both survive.
105
+ */
106
+ export declare const dbTable: <R extends AnyResource, const M extends TableMeta>(resource: R, meta: M) => TableAnnotated<R, M>;
107
+ /**
108
+ * A {@link dbTable} constrained to a specific level union `Level`, requiring
109
+ * the full {@link DbTable} metadata. A consumer pins its own levels once —
110
+ * `const dbTable = dbTableFor<LeafLevel>()` — so an annotation naming a level
111
+ * the derivation has no parent binding for is a type error at the annotation
112
+ * site.
113
+ */
114
+ export declare const dbTableFor: <Level extends string>() => <R extends AnyResource, const M extends DbTable<Level>>(resource: R, meta: M) => TableAnnotated<R, M>;
115
+ /**
116
+ * Read the db-table metadata off a resource at runtime. `Option.none()` when
117
+ * the resource carries none. Typed to the {@link TableMeta} floor; a consumer
118
+ * that annotated with a {@link DbTable} recovers its `level` through the
119
+ * {@link DbTableOf} phantom.
120
+ */
121
+ export declare const readDbTable: (resource: AnyResource) => Option.Option<TableMeta>;
122
+ /** The resource an entry's `leaf` holds, when it holds one. */
123
+ type EntryResource<E> = E extends {
124
+ readonly leaf?: infer L;
125
+ } ? Exclude<L, undefined> extends AnyResource ? Exclude<L, undefined> : never : never;
126
+ /** The fields an entry's `columns` declares; `{}` when absent. */
127
+ type EntryLevelFields<E> = E extends {
128
+ readonly columns?: infer F;
129
+ } ? Exclude<F, undefined> extends Schema.Struct.Fields ? Exclude<F, undefined> : Record<never, never> : Record<never, never>;
130
+ /**
131
+ * The typed column metadata of a {@link defineResourceTree} declaration `S`:
132
+ * per level, its level columns' `DbColumn` map and its leaf resource's
133
+ * attribute columns. The phantom the table derivation reads.
134
+ */
135
+ export type ResourceTreeMeta<S> = { readonly [K in keyof S]: {
136
+ readonly level: FieldColumnsMetaOf<EntryLevelFields<S[K]>>;
137
+ readonly leaf: [EntryResource<S[K]>] extends [never] ? Record<never, never> : ResourceColumnsMetaOf<EntryResource<S[K]>>;
138
+ }; };
139
+ /**
140
+ * The constraint a resource tree declaration `S` is checked against: the
141
+ * core `CheckedTreeSpec`, with each leaf a `Resource.make` definition whose
142
+ * wire `type` is the level's own name. A leaf filed under another name, or a
143
+ * parent naming no level (or the level itself), is a type error.
144
+ */
145
+ export type CheckedResourceTreeSpec<S, Ext> = { readonly [K in keyof S]: LevelSpec<Exclude<keyof S & string, K>, AnyResource & {
146
+ readonly type: K;
147
+ }> & Ext; };
148
+ /**
149
+ * A {@link defineResourceTree} whose every entry also carries the consumer's
150
+ * extension record `Ext`, which the library never reads (pulpit's
151
+ * `RegistryEntry` flags). Pin it once — `const defineTree =
152
+ * defineResourceTreeFor<RegistryFlags>()`. Throws `DerivationError` at module
153
+ * load as `defineResourceTree` does.
154
+ */
155
+ export declare const defineResourceTreeFor: <Ext extends object>() => <const S extends CheckedResourceTreeSpec<S, Ext>>(spec: S) => Tree<S, ResourceTreeMeta<S>>;
156
+ /**
157
+ * The core entry's `defineTree` over `Resource.make` definitions: each leaf's
158
+ * `leaf` is the resource whose wire `type` is the level's name (the
159
+ * discriminator value), and its domain columns are the resource's
160
+ * `dbColumn`-annotated attributes. Every other check is the core one. Throws
161
+ * `DerivationError` at module load for a leaf whose resource `type` isn't its
162
+ * name, and for every structural fault `defineTree` names.
163
+ *
164
+ * @example
165
+ * ```ts
166
+ * import { dbColumn } from "@thomasfosterau/effect-drizzle-ext";
167
+ * import { defineResourceTree } from "@thomasfosterau/effect-drizzle-ext/jsonapi";
168
+ * import { Resource } from "@thomasfosterau/effect-jsonapi";
169
+ * import * as Schema from "effect/Schema";
170
+ *
171
+ * const Book = Resource.make("book", {
172
+ * attributes: {
173
+ * pages: dbColumn(Schema.Finite, { column: "pages", pg: "integer", notNull: false }),
174
+ * },
175
+ * });
176
+ *
177
+ * const tree = defineResourceTree({
178
+ * work: {
179
+ * columns: { title: dbColumn(Schema.String, { column: "title", pg: "text", notNull: true }) },
180
+ * },
181
+ * book: { parent: "work", leaf: Book },
182
+ * });
183
+ *
184
+ * console.log(tree.leaves); // ["book"]
185
+ * ```
186
+ */
187
+ export declare const defineResourceTree: <const S extends CheckedResourceTreeSpec<S, Record<never, never>>>(spec: S) => Tree<S, ResourceTreeMeta<S>>;
188
+ export type { DbTable, TableMeta };
189
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":["CoreTableAnnotated","CoreDbTableOf"],"sources":["../../src/jsonapi/index.ts"],"mappings":";;;;;;;;;YA2DY,cAAc,SAAS;;;;;;;;KAS9B,iBAAiB,KACpB,UAAU,SAAS,gBAAgB,SAAS,UAAU,UAAU,UAAU,OAAO,IAAI;;;;;;;;;YAU3E,sBAAsB,UAAU,2BAExC,WAAW,SAAS,aAAa,OAC/B,aAAa,iBAAiB,SAAS,aAAa,GAAG,gCAGrD,IACH,aAAa,iBAAiB,SAAS,aAAa,GAAG;;;;;;;;;;qBAY/C,kBAAe,UAAc,gBAAc,eAAe;;;;;;;;;qBAuB1D,uBAAoB,UAAc,gBAAc,eAAe;;;;;;;;;;;;;;;;;;iBA8B3D;WACN;;WAEA;;WAEA;;WAEA;;WAEA;;WAEA,QAAQ,OAAO;;;;;;;;;;;;;;qBA6Bb,+BAA4B,UAC7B,gBACT,cAAc;;;;;;YAsBL,eAAe,UAAU,aAAa,UAAU,aAAaA,iBAAmB,GAAG;;YAGnF,UAAU,KAAKC,YAAc;;;;;;;;;qBAU5B,UAAW,UAAU,mBAAmB,UAAU,WAAS,UAC5D,GAAC,MACL,MACL,eAAe,GAAG;;;;;;;;qBASR,aACV,4BACA,UAAU,mBAAmB,UAAU,QAAQ,QAAM,UAC1C,GAAC,MACL,MACL,eAAe,GAAG;;;;;;;qBASV,cAAW,UAAc,gBAAc,OAAO,OAAO;;KAM7D,cAAc,KAAK;WAAqB,aAAa;IACtD,QAAQ,sBAAsB,cAC5B,QAAQ;;KAKT,iBAAiB,KAAK;WAAqB,gBAAgB;IAC5D,QAAQ,sBAAsB,OAAO,OAAO,SAC1C,QAAQ,gBACR,uBACF;;;;;;YAOQ,iBAAiB,iBACjB,WAAW;WACV,OAAO,mBAAmB,iBAAiB,EAAE;WAC7C,OAAO,cAAc,EAAE,uBAC5B,uBACA,sBAAsB,cAAc,EAAE;;;;;;;;YAUlC,wBAAwB,GAAG,mBAC3B,WAAW,IAAI,UACvB,cAAc,YAAY,IAC1B;WAAyB,MAAM;KAE/B;;;;;;;;qBAUS,wBACV,gCACM,UAAU,wBAAwB,GAAG,MAAI,MAAQ,MAAI,KAAK,GAAG,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA0C1E,2BAA2B,UAAU,wBAAwB,GAAG,uBAC3E,MAAM,MACH,KAAK,GAAG,iBAAiB"}
@@ -0,0 +1,164 @@
1
+ import { f as readDbTable$1, i as dbTable$1, l as fieldColumns, m as treeError, p as buildTree, t as DB_COLUMN_KEY, u as numericBounds } from "../src-CBeuhy5b.js";
2
+ import * as Arr from "effect/Array";
3
+ import * as Option from "effect/Option";
4
+ import * as Predicate from "effect/Predicate";
5
+ import * as R from "effect/Record";
6
+ import * as Schema from "effect/Schema";
7
+ import * as SchemaAST from "effect/SchemaAST";
8
+ import { Resource } from "@thomasfosterau/effect-jsonapi";
9
+ // src/jsonapi/index.ts
10
+ /**
11
+ * @copyright Thomas Foster
12
+ *
13
+ * **The JSON:API resource projection of the annotation entry (no Drizzle).**
14
+ *
15
+ * The {@link ../index.ts} entry reads `dbColumn` annotations off a
16
+ * `Schema.Struct`'s fields. A consumer whose models are
17
+ * `@thomasfosterau/effect-jsonapi` `Resource.make` definitions declares the
18
+ * same annotations on its **attributes**, which sit behind an extra layer:
19
+ * `Resource.attribute` descriptors, and the `optionalKey` wrapper a
20
+ * `resource: "optional"` attribute takes. This entry is the reader for that
21
+ * shape — the resource-shaped counterpart of `fieldColumns` /
22
+ * `fieldColumnBounds`.
23
+ *
24
+ * **No Drizzle, and an optional peer.** `@thomasfosterau/effect-jsonapi` is an
25
+ * *optional* peer dependency: only this subpath and `./derive` import it, so a
26
+ * consumer with plain `Schema.Struct` models never installs it. The
27
+ * Drizzle-emitting half is `./jsonapi/derive`.
28
+ *
29
+ * The table-level annotation helpers are re-exported here with an
30
+ * `R extends AnyResource` bound. They are the same functions — the narrower
31
+ * bound exists so a consumer's resource-facing `db-annotation.ts` can import
32
+ * every name it needs from one place, and so a non-resource object cannot be
33
+ * annotated through the resource-shaped API by accident.
34
+ *
35
+ * @module jsonapi
36
+ */
37
+ /**
38
+ * Project a resource definition's per-attribute {@link DbColumn} metadata into
39
+ * plain data the derivation consumes, dropping attributes that declare no
40
+ * column. Reads the annotation via `Resource.attributeAnnotations`, which
41
+ * resolves each attribute schema's annotations — including through the
42
+ * `optionalKey` wrapper a `resource: "optional"` attribute carries, whose AST
43
+ * is the inner schema's. The type-level counterpart is
44
+ * {@link ResourceColumnsMetaOf}.
45
+ */
46
+ const resourceColumns = (resource) => R.getSomes(R.map(Resource.attributeAnnotations(resource), (annotations) => Option.map(Option.fromUndefinedOr(annotations?.[DB_COLUMN_KEY]), (meta) => meta)));
47
+ /**
48
+ * Project a resource definition's per-attribute numeric bounds into plain data
49
+ * a `CHECK`-constraint derivation consumes: for each `dbColumn`-annotated
50
+ * attribute whose schema carries numeric-bound checks, the bounds paired with
51
+ * the column metadata. Attributes without a column, or without bounds, drop.
52
+ * The Drizzle-emitting counterpart is the `./jsonapi/derive` entry's
53
+ * `deriveCheckConstraints`.
54
+ */
55
+ const resourceColumnBounds = (resource) => {
56
+ const fields = Resource.attributes(resource);
57
+ return R.getSomes(R.map(resourceColumns(resource), (column, name) => Option.map(numericBounds(fields[name]), (bounds) => ({
58
+ column,
59
+ ...bounds
60
+ }))));
61
+ };
62
+ /** An ISO timestamp a `Date`-decoding attribute schema accepts. */
63
+ const ISO_PROBE = "2024-01-02T03:04:05.000Z";
64
+ /**
65
+ * What a schema's **JSON codec** decodes `input` to — the wire reading, so an
66
+ * ISO string decodes to a `Date` for `Schema.Date` (whose own decoder accepts
67
+ * only a `Date` instance). A decoder is built per call: this is start-up
68
+ * introspection over a model, not a hot path.
69
+ */
70
+ const decodesFromJson = (schema, input) => Schema.decodeOption(Schema.toCodecJson(schema))(input);
71
+ /**
72
+ * Project a `Resource.make` definition's resource-object attributes into
73
+ * ordered plain-data {@link ResourceAttributeProjection}s (declaration
74
+ * order). Optionality is read off each field's AST (`optionalKey` marks the
75
+ * property optional); nullability, array-ness and date-ness are recovered by
76
+ * behavioural decode probes through each schema's JSON codec (the wire
77
+ * reading), so the read stays robust across schema-AST shapes (a `NullOr` of
78
+ * a transformation, a branded array, …).
79
+ *
80
+ * Input-only attributes (`resource: false`) are not on the resource object,
81
+ * and so not here. The `column` read is {@link resourceColumns}'.
82
+ */
83
+ const resourceAttributeProjections = (resource) => {
84
+ const fields = Resource.attributes(resource);
85
+ const columns = resourceColumns(resource);
86
+ return Arr.map(R.toEntries(fields), ([key, schema]) => ({
87
+ key,
88
+ optional: SchemaAST.isOptional(schema.ast),
89
+ nullable: Option.isSome(decodesFromJson(schema, null)),
90
+ array: Option.isSome(decodesFromJson(schema, [])),
91
+ date: Option.exists(decodesFromJson(schema, ISO_PROBE), Predicate.isDate),
92
+ column: Option.fromNullishOr(columns[key]?.column)
93
+ }));
94
+ };
95
+ /**
96
+ * Annotate a resource definition with the database table it backs. The
97
+ * annotation entry's `dbTable`, narrowed to `R extends AnyResource`: it
98
+ * attaches the metadata as a non-enumerable symbol property and returns the
99
+ * **same** resource object, so `Resource.make`'s assigned properties (`type`,
100
+ * `Id`, `identifier`, …) and the referential identity a registry compares by
101
+ * both survive.
102
+ */
103
+ const dbTable = (resource, meta) => dbTable$1(resource, meta);
104
+ /**
105
+ * A {@link dbTable} constrained to a specific level union `Level`, requiring
106
+ * the full {@link DbTable} metadata. A consumer pins its own levels once —
107
+ * `const dbTable = dbTableFor<LeafLevel>()` — so an annotation naming a level
108
+ * the derivation has no parent binding for is a type error at the annotation
109
+ * site.
110
+ */
111
+ const dbTableFor = () => (resource, meta) => dbTable$1(resource, meta);
112
+ /**
113
+ * Read the db-table metadata off a resource at runtime. `Option.none()` when
114
+ * the resource carries none. Typed to the {@link TableMeta} floor; a consumer
115
+ * that annotated with a {@link DbTable} recovers its `level` through the
116
+ * {@link DbTableOf} phantom.
117
+ */
118
+ const readDbTable = (resource) => readDbTable$1(resource);
119
+ /**
120
+ * A {@link defineResourceTree} whose every entry also carries the consumer's
121
+ * extension record `Ext`, which the library never reads (pulpit's
122
+ * `RegistryEntry` flags). Pin it once — `const defineTree =
123
+ * defineResourceTreeFor<RegistryFlags>()`. Throws `DerivationError` at module
124
+ * load as `defineResourceTree` does.
125
+ */
126
+ const defineResourceTreeFor = () => (spec) => buildTree(spec, {
127
+ levelColumns: fieldColumns,
128
+ leafColumns: (resource, name) => resource.type === name ? resourceColumns(resource) : treeError(`leaf "${name}" carries resource "${resource.type}"; a leaf's name is its resource's type`)
129
+ });
130
+ /**
131
+ * The core entry's `defineTree` over `Resource.make` definitions: each leaf's
132
+ * `leaf` is the resource whose wire `type` is the level's name (the
133
+ * discriminator value), and its domain columns are the resource's
134
+ * `dbColumn`-annotated attributes. Every other check is the core one. Throws
135
+ * `DerivationError` at module load for a leaf whose resource `type` isn't its
136
+ * name, and for every structural fault `defineTree` names.
137
+ *
138
+ * @example
139
+ * ```ts
140
+ * import { dbColumn } from "@thomasfosterau/effect-drizzle-ext";
141
+ * import { defineResourceTree } from "@thomasfosterau/effect-drizzle-ext/jsonapi";
142
+ * import { Resource } from "@thomasfosterau/effect-jsonapi";
143
+ * import * as Schema from "effect/Schema";
144
+ *
145
+ * const Book = Resource.make("book", {
146
+ * attributes: {
147
+ * pages: dbColumn(Schema.Finite, { column: "pages", pg: "integer", notNull: false }),
148
+ * },
149
+ * });
150
+ *
151
+ * const tree = defineResourceTree({
152
+ * work: {
153
+ * columns: { title: dbColumn(Schema.String, { column: "title", pg: "text", notNull: true }) },
154
+ * },
155
+ * book: { parent: "work", leaf: Book },
156
+ * });
157
+ *
158
+ * console.log(tree.leaves); // ["book"]
159
+ * ```
160
+ */
161
+ const defineResourceTree = defineResourceTreeFor();
162
+ export { dbTable, dbTableFor, defineResourceTree, defineResourceTreeFor, readDbTable, resourceAttributeProjections, resourceColumnBounds, resourceColumns };
163
+
164
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["coreDbTable","coreReadDbTable"],"sources":["../../src/jsonapi/index.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkGA,MAAa,mBAAmB,aAI9B,EAAE,SACA,EAAE,IAAI,SAAS,qBAAqB,QAAQ,IAAI,gBAC9C,OAAO,IACL,OAAO,gBAAgB,cAAc,cAAc,IAGlD,SAAS,IACZ,CACF,CACF;;;;;;;;;AAUF,MAAa,wBAAwB,aAAwD;CAG3F,MAAM,SAAS,SAAS,WAAW,QAAQ;CAC3C,OAAO,EAAE,SACP,EAAE,IAAI,gBAAgB,QAAQ,IAAI,QAAQ,SACxC,OAAO,IAAI,cAAc,OAAO,KAAK,IAAI,YAA0B;EAAE;EAAQ,GAAG;CAAO,EAAE,CAC3F,CACF;AACF;;AAoCA,MAAM,YAAY;;;;;;;AAQlB,MAAM,mBAAmB,QAAoB,UAG3C,OAAO,aAAa,OAAO,YAAY,MAA+B,CAAC,CAAC,CAAC,KAAK;;;;;;;;;;;;;AAchF,MAAa,gCACX,aAC+C;CAG/C,MAAM,SAAS,SAAS,WAAW,QAAQ;CAC3C,MAAM,UAAU,gBAAgB,QAAQ;CACxC,OAAO,IAAI,IAAI,EAAE,UAAU,MAAM,IAAI,CAAC,KAAK,aAAa;EACtD;EACA,UAAU,UAAU,WAAW,OAAO,GAAG;EACzC,UAAU,OAAO,OAAO,gBAAgB,QAAQ,IAAI,CAAC;EACrD,OAAO,OAAO,OAAO,gBAAgB,QAAQ,CAAC,CAAC,CAAC;EAChD,MAAM,OAAO,OAAO,gBAAgB,QAAQ,SAAS,GAAG,UAAU,MAAM;EACxE,QAAQ,OAAO,cAAc,QAAQ,IAAI,EAAE,MAAM;CACnD,EAAE;AACJ;;;;;;;;;AAsBA,MAAa,WACX,UACA,SACyBA,UAAY,UAAU,IAAI;;;;;;;;AASrD,MAAa,oBAGT,UACA,SAEAA,UAAY,UAAU,IAAI;;;;;;;AAQ9B,MAAa,eAAe,aAC1BC,cAAgB,QAAQ;;;;;;;;AAqD1B,MAAa,+BAEuC,SAChD,UAA+C,MAAM;CACnD,cAAc;CACd,cAAc,UAAU,SACtB,SAAS,SAAS,OACd,gBAAgB,QAAQ,IACxB,UACE,SAAS,KAAK,sBAAsB,SAAS,KAAK,wCACpD;AACR,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCL,MAAa,qBAEuB,sBAA4C"}
@@ -0,0 +1,111 @@
1
+ import * as Schema from "effect/Schema";
2
+ import { SQL, SQLWrapper } from "drizzle-orm";
3
+ import * as Effect from "effect/Effect";
4
+ import { EffectDrizzleQueryError } from "drizzle-orm/effect-core";
5
+ /**
6
+ * The alias the unpaged total rides back on. Prefixed so it cannot collide
7
+ * with a projected column, and so a clash surfaces as a visible key rather
8
+ * than silently overwriting an attribute.
9
+ */
10
+ export declare const TOTAL_COUNT_ALIAS = "__totalCount";
11
+ /**
12
+ * The selection entry adding the unpaged total to a list query. Spread into
13
+ * a `.select({ … })` map alongside the columns the read already projects.
14
+ *
15
+ * `COUNT(*) OVER ()` is evaluated after `WHERE` and before `LIMIT`/`OFFSET`,
16
+ * so it rides back on every row of the page the query was already
17
+ * fetching — one round trip instead of a separate sequential `count(*)`
18
+ * query for data the first one can already carry.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * import { totalCountColumn } from "@thomasfosterau/effect-drizzle-ext/query";
23
+ * import { eq } from "drizzle-orm";
24
+ * import { integer, pgTable, QueryBuilder, text } from "drizzle-orm/pg-core";
25
+ *
26
+ * const article = pgTable("article", { id: integer().primaryKey(), title: text().notNull() });
27
+ *
28
+ * const page = new QueryBuilder()
29
+ * .select({ id: article.id, title: article.title, ...totalCountColumn })
30
+ * .from(article)
31
+ * .where(eq(article.title, "Hello"))
32
+ * .limit(20);
33
+ *
34
+ * console.log(page.toSQL().sql);
35
+ * // select "id", "title", (count(*) over ())::int from "article" where … limit $2
36
+ * ```
37
+ */
38
+ export declare const totalCountColumn: {
39
+ readonly __totalCount: SQL<number>;
40
+ };
41
+ /** One page of a list read: the window's rows plus the unpaged total. */
42
+ export interface Page<TResource> {
43
+ readonly items: readonly TResource[];
44
+ readonly totalCount: number;
45
+ }
46
+ /**
47
+ * Build a {@link Page} from rows selected with {@link totalCountColumn},
48
+ * mapping each row to its resource.
49
+ */
50
+ export declare const pageOf: <TRow, TResource>(rows: readonly TRow[], toResource: (row: TRow) => TResource) => Page<TResource>;
51
+ /**
52
+ * A {@link Page} over resources already mapped — for a list whose rows go
53
+ * through several transformation steps before becoming resources, where
54
+ * {@link pageOf}'s single mapper doesn't fit.
55
+ */
56
+ export declare const pageWithTotal: <TResource>(items: readonly TResource[], rows: readonly unknown[]) => Page<TResource>;
57
+ /**
58
+ * Combines a set of optional SQL filters into one `AND`-joined condition.
59
+ * Filters an operation didn't build (an optional query parameter left
60
+ * unset) are `undefined`; `and()` drops them, so passing every filter
61
+ * unconditionally is safe. With no defined filter at all, the result is
62
+ * `undefined` — the caller omits `.where(...)` entirely rather than
63
+ * applying a vacuous `WHERE true`.
64
+ */
65
+ export declare const combineFilters: (...filters: ReadonlyArray<SQL | undefined>) => SQL | undefined;
66
+ /**
67
+ * Builds a `limit` / `offset` input schema bounded by `options`, plus the
68
+ * two field schemas standalone (for a caller that validates them outside a
69
+ * struct, e.g. against a query-string parameter one at a time).
70
+ *
71
+ * `limit` accepts an integer in `(0, maxLimit]`; `offset` accepts an
72
+ * integer `>= 0`. Both keys are optional on input and decode to
73
+ * `defaultLimit` / `0` respectively when absent, via
74
+ * {@link Schema.withDecodingDefaultKey} — so `Input`'s decoded `Type` always
75
+ * carries both fields, while its `Encoded` (request) side may omit either.
76
+ */
77
+ export declare const boundedPagination: (options: {
78
+ readonly maxLimit: number;
79
+ readonly defaultLimit: number;
80
+ }) => {
81
+ Limit: Schema.Int;
82
+ Offset: Schema.Natural;
83
+ Input: Schema.Struct<{
84
+ readonly limit: Schema.withDecodingDefaultKey<Schema.Int, never>;
85
+ readonly offset: Schema.withDecodingDefaultKey<Schema.Natural, never>;
86
+ }>;
87
+ };
88
+ /** The shape {@link boundedPagination} returns. */
89
+ export type BoundedPagination = ReturnType<typeof boundedPagination>;
90
+ /**
91
+ * Rows-normalising wrapper for raw-SQL reads (`db.execute<T>`).
92
+ *
93
+ * drizzle-orm's effect-postgres driver types `db.execute<T>` as
94
+ * `readonly T[]`, but depending on the SQL client bound underneath, the
95
+ * query can resolve in "raw" mode with no row mapper, handing back the
96
+ * full driver result object (`{ rows, rowCount, … }`) instead of a bare
97
+ * array — so `.map` on the yielded value throws `rows.map is not a
98
+ * function`. Every raw-SQL read should go through this helper instead of
99
+ * `db.execute` directly: it accepts both shapes — array in → array out —
100
+ * so which shape the underlying driver actually returns changes nothing
101
+ * for call sites. Write-only executes (INSERT/DDL) that never read rows
102
+ * may keep calling `db.execute` directly.
103
+ *
104
+ * `db` is typed to the narrowest shape this needs (`execute` alone),
105
+ * widened so both a real Effect-Drizzle database and a stub client in a
106
+ * test satisfy it without a cast.
107
+ */
108
+ export declare const executeRows: <T extends Record<string, unknown>>(db: {
109
+ readonly execute: (query: SQLWrapper | string) => Effect.Effect<unknown, EffectDrizzleQueryError>;
110
+ }, query: SQLWrapper | string) => Effect.Effect<readonly T[], EffectDrizzleQueryError>;
111
+ //# sourceMappingURL=query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.d.ts","names":[],"sources":["../src/query.ts"],"mappings":";;;;;;;;;qBAuBa;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBA6BA;;;;iBAKI,KAAK;WACX,gBAAgB;WAChB;;;;;;qBA0BE,SAAU,MAAM,WAAS,eACrB,QAAM,aACR,KAAK,SAAS,cAC1B,KAAK;;;;;;qBAUK,gBAAiB,WAAS,gBACrB,aAAW,6BAE1B,KAAK;;;;;;;;;qBAcK,oBAAc,SAAgB,cAAc,qBAAmB;;;;;;;;;;;;qBAe/D,oBAAiB;WACnB;WACA;;;;;;;;;;YAeC,oBAAoB,kBAAkB;;;;;;;;;;;;;;;;;;;qBAoBrC,cAAe,UAAU,yBAAuB;WAEhD,UACP,OAAO,wBACJ,OAAO,gBAAgB;GAC7B,OACM,wBACN,OAAO,gBAAgB,KAAK"}
package/dist/query.js ADDED
@@ -0,0 +1,142 @@
1
+ import * as Predicate from "effect/Predicate";
2
+ import * as Schema from "effect/Schema";
3
+ import { and, sql } from "drizzle-orm";
4
+ import * as Effect from "effect/Effect";
5
+ // src/query.ts
6
+ /**
7
+ * @copyright Thomas Foster
8
+ *
9
+ * List-read helpers: the unpaged-total window column and the {@link Page}
10
+ * shape every list read returns, a variadic AND filter combinator, a bounded
11
+ * limit/offset input schema, and the rows-normalising wrapper over a raw-SQL
12
+ * `execute` that papers over a driver mismatch between typed and runtime
13
+ * `db.execute` shapes.
14
+ *
15
+ * @module query
16
+ */
17
+ /**
18
+ * The alias the unpaged total rides back on. Prefixed so it cannot collide
19
+ * with a projected column, and so a clash surfaces as a visible key rather
20
+ * than silently overwriting an attribute.
21
+ */
22
+ const TOTAL_COUNT_ALIAS = "__totalCount";
23
+ /**
24
+ * The selection entry adding the unpaged total to a list query. Spread into
25
+ * a `.select({ … })` map alongside the columns the read already projects.
26
+ *
27
+ * `COUNT(*) OVER ()` is evaluated after `WHERE` and before `LIMIT`/`OFFSET`,
28
+ * so it rides back on every row of the page the query was already
29
+ * fetching — one round trip instead of a separate sequential `count(*)`
30
+ * query for data the first one can already carry.
31
+ *
32
+ * @example
33
+ * ```ts
34
+ * import { totalCountColumn } from "@thomasfosterau/effect-drizzle-ext/query";
35
+ * import { eq } from "drizzle-orm";
36
+ * import { integer, pgTable, QueryBuilder, text } from "drizzle-orm/pg-core";
37
+ *
38
+ * const article = pgTable("article", { id: integer().primaryKey(), title: text().notNull() });
39
+ *
40
+ * const page = new QueryBuilder()
41
+ * .select({ id: article.id, title: article.title, ...totalCountColumn })
42
+ * .from(article)
43
+ * .where(eq(article.title, "Hello"))
44
+ * .limit(20);
45
+ *
46
+ * console.log(page.toSQL().sql);
47
+ * // select "id", "title", (count(*) over ())::int from "article" where … limit $2
48
+ * ```
49
+ */
50
+ const totalCountColumn = { [TOTAL_COUNT_ALIAS]: sql`(count(*) over ())::int` };
51
+ /**
52
+ * Reads the total off the first row — every row carries the same value,
53
+ * since the window is evaluated over the whole matching set. Falls back to
54
+ * `0` for an empty page: a window function over zero rows returns nothing,
55
+ * so **an empty page reports `totalCount: 0`** — exact when nothing
56
+ * matches, and wrong only when a caller paged past the end of a non-empty
57
+ * collection. That case is self-describing (`items: []` says it) and
58
+ * self-correcting (paging back gives the true total); the alternative is a
59
+ * second query on every list read. The alias is added by
60
+ * {@link totalCountColumn} outside the row's declared type, so it is
61
+ * recovered by an `in` narrowing rather than a cast.
62
+ */
63
+ const totalFrom = (rows) => {
64
+ const first = rows[0];
65
+ return Predicate.isObjectOrArray(first) && "__totalCount" in first ? Number(first[TOTAL_COUNT_ALIAS]) : 0;
66
+ };
67
+ /**
68
+ * Build a {@link Page} from rows selected with {@link totalCountColumn},
69
+ * mapping each row to its resource.
70
+ */
71
+ const pageOf = (rows, toResource) => ({
72
+ items: rows.map(toResource),
73
+ totalCount: totalFrom(rows)
74
+ });
75
+ /**
76
+ * A {@link Page} over resources already mapped — for a list whose rows go
77
+ * through several transformation steps before becoming resources, where
78
+ * {@link pageOf}'s single mapper doesn't fit.
79
+ */
80
+ const pageWithTotal = (items, rows) => ({
81
+ items,
82
+ totalCount: totalFrom(rows)
83
+ });
84
+ /**
85
+ * Combines a set of optional SQL filters into one `AND`-joined condition.
86
+ * Filters an operation didn't build (an optional query parameter left
87
+ * unset) are `undefined`; `and()` drops them, so passing every filter
88
+ * unconditionally is safe. With no defined filter at all, the result is
89
+ * `undefined` — the caller omits `.where(...)` entirely rather than
90
+ * applying a vacuous `WHERE true`.
91
+ */
92
+ const combineFilters = (...filters) => and(...filters);
93
+ /**
94
+ * Builds a `limit` / `offset` input schema bounded by `options`, plus the
95
+ * two field schemas standalone (for a caller that validates them outside a
96
+ * struct, e.g. against a query-string parameter one at a time).
97
+ *
98
+ * `limit` accepts an integer in `(0, maxLimit]`; `offset` accepts an
99
+ * integer `>= 0`. Both keys are optional on input and decode to
100
+ * `defaultLimit` / `0` respectively when absent, via
101
+ * {@link Schema.withDecodingDefaultKey} — so `Input`'s decoded `Type` always
102
+ * carries both fields, while its `Encoded` (request) side may omit either.
103
+ */
104
+ const boundedPagination = (options) => {
105
+ const { maxLimit, defaultLimit } = options;
106
+ const Limit = Schema.Int.pipe(Schema.check(Schema.isBetween({
107
+ minimum: 0,
108
+ maximum: maxLimit,
109
+ exclusiveMinimum: true
110
+ })));
111
+ const Offset = Schema.Natural;
112
+ return {
113
+ Limit,
114
+ Offset,
115
+ Input: Schema.Struct({
116
+ limit: Limit.pipe(Schema.withDecodingDefaultKey(Effect.succeed(defaultLimit))),
117
+ offset: Offset.pipe(Schema.withDecodingDefaultKey(Effect.succeed(0)))
118
+ })
119
+ };
120
+ };
121
+ /**
122
+ * Rows-normalising wrapper for raw-SQL reads (`db.execute<T>`).
123
+ *
124
+ * drizzle-orm's effect-postgres driver types `db.execute<T>` as
125
+ * `readonly T[]`, but depending on the SQL client bound underneath, the
126
+ * query can resolve in "raw" mode with no row mapper, handing back the
127
+ * full driver result object (`{ rows, rowCount, … }`) instead of a bare
128
+ * array — so `.map` on the yielded value throws `rows.map is not a
129
+ * function`. Every raw-SQL read should go through this helper instead of
130
+ * `db.execute` directly: it accepts both shapes — array in → array out —
131
+ * so which shape the underlying driver actually returns changes nothing
132
+ * for call sites. Write-only executes (INSERT/DDL) that never read rows
133
+ * may keep calling `db.execute` directly.
134
+ *
135
+ * `db` is typed to the narrowest shape this needs (`execute` alone),
136
+ * widened so both a real Effect-Drizzle database and a stub client in a
137
+ * test satisfy it without a cast.
138
+ */
139
+ const executeRows = (db, query) => Effect.map(db.execute(query), (result) => Array.isArray(result) ? result : result.rows ?? []);
140
+ export { TOTAL_COUNT_ALIAS, boundedPagination, combineFilters, executeRows, pageOf, pageWithTotal, totalCountColumn };
141
+
142
+ //# sourceMappingURL=query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.js","names":[],"sources":["../src/query.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAuBA,MAAa,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BjC,MAAa,mBAAmB,GAC7B,oBAAoB,GAAW,0BAClC;;;;;;;;;;;;;AAoBA,MAAM,aAAa,SAAqC;CACtD,MAAM,QAAQ,KAAK;CACnB,OAAO,UAAU,gBAAgB,KAAK,KAAA,kBAA0B,QAC5D,OAAO,MAAM,kBAAkB,IAC/B;AACN;;;;;AAMA,MAAa,UACX,MACA,gBACqB;CACrB,OAAO,KAAK,IAAI,UAAU;CAC1B,YAAY,UAAU,IAAI;AAC5B;;;;;;AAOA,MAAa,iBACX,OACA,UACqB;CACrB;CACA,YAAY,UAAU,IAAI;AAC5B;;;;;;;;;AAWA,MAAa,kBAAkB,GAAG,YAChC,IAAI,GAAG,OAAO;;;;;;;;;;;;AAchB,MAAa,qBAAqB,YAG5B;CACJ,MAAM,EAAE,UAAU,iBAAiB;CACnC,MAAM,QAAQ,OAAO,IAAI,KACvB,OAAO,MAAM,OAAO,UAAU;EAAE,SAAS;EAAG,SAAS;EAAU,kBAAkB;CAAK,CAAC,CAAC,CAC1F;CACA,MAAM,SAAS,OAAO;CAKtB,OAAO;EAAE;EAAO;EAAQ,OAJV,OAAO,OAAO;GAC1B,OAAO,MAAM,KAAK,OAAO,uBAAuB,OAAO,QAAQ,YAAY,CAAC,CAAC;GAC7E,QAAQ,OAAO,KAAK,OAAO,uBAAuB,OAAO,QAAQ,CAAC,CAAC,CAAC;EACtE,CAC4B;CAAE;AAChC;;;;;;;;;;;;;;;;;;;AAuBA,MAAa,eACX,IAKA,UAEA,OAAO,IAAI,GAAG,QAAQ,KAAK,IAAI,WAI7B,MAAM,QAAQ,MAAM,IACf,SACC,OAAmC,QAAQ,CAAC,CACpD"}
@@ -0,0 +1,38 @@
1
+ import { o as DbColumnReference } from "./index-CFhgcF4N.js";
2
+ import { AnyPgColumn, PgTable } from "drizzle-orm/pg-core";
3
+ /**
4
+ * A name-keyed registry of FK-reference targets, plus the lazy resolver the
5
+ * derivation's `resolveReference` hook expects.
6
+ *
7
+ * The invariant is the laziness: `resolve` never touches the registry, so a
8
+ * reference may be resolved before its target table module has loaded;
9
+ * **forcing** the returned thunk is what reads the registry, and that happens
10
+ * only when Drizzle materialises the foreign key.
11
+ */
12
+ export interface ReferenceRegistry {
13
+ /**
14
+ * Register a table as a possible FK-reference target, keyed by its SQL table
15
+ * name (matching a `references.table` annotation). Idempotent; call it once,
16
+ * right after the target table is defined, in the table module that owns it.
17
+ */
18
+ readonly register: (table: PgTable) => void;
19
+ /**
20
+ * Resolve a `dbColumn` `references` annotation to the target Drizzle column,
21
+ * as the lazy thunk `.references()` consumes. Wire it into the derivation's
22
+ * {@link DeriveOptions.resolveReference}; it is invoked only when a derived
23
+ * column declares a reference, so leaves with no FK annotations never touch
24
+ * the registry.
25
+ *
26
+ * Forcing the thunk against an unregistered table, or against a table with
27
+ * no such column, throws {@link DerivationError} — a schema-definition
28
+ * programmer error surfaced the first time Drizzle forces the thunk, not a
29
+ * runtime condition.
30
+ */
31
+ readonly resolve: (reference: DbColumnReference) => () => AnyPgColumn;
32
+ }
33
+ /**
34
+ * Build a fresh {@link ReferenceRegistry}. Each call owns its own name → table
35
+ * map, so independent schemas never resolve into each other.
36
+ */
37
+ export declare const makeReferenceRegistry: () => ReferenceRegistry;
38
+ //# sourceMappingURL=references.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"references.d.ts","names":[],"sources":["../src/references.ts"],"mappings":";;;;;;;;;;;iBAgDiB;;;;;;WAMN,WAAW,OAAO;;;;;;;;;;;;;WAalB,UAAU,WAAW,4BAA4B;;;;;;qBAO/C,6BAA4B"}