prisma-effect-kysely 5.9.0 → 6.0.0-next.1

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 (44) hide show
  1. package/CHANGELOG.md +33 -141
  2. package/README.md +76 -43
  3. package/dist/effect/enum.d.ts +2 -2
  4. package/dist/effect/enum.d.ts.map +1 -1
  5. package/dist/effect/enum.js +9 -6
  6. package/dist/effect/enum.js.map +1 -1
  7. package/dist/effect/generator.d.ts +2 -1
  8. package/dist/effect/generator.d.ts.map +1 -1
  9. package/dist/effect/generator.js +20 -10
  10. package/dist/effect/generator.js.map +1 -1
  11. package/dist/effect/join-table.d.ts +5 -3
  12. package/dist/effect/join-table.d.ts.map +1 -1
  13. package/dist/effect/join-table.js +13 -8
  14. package/dist/effect/join-table.js.map +1 -1
  15. package/dist/effect/type.d.ts.map +1 -1
  16. package/dist/effect/type.js +24 -2
  17. package/dist/effect/type.js.map +1 -1
  18. package/dist/error/index.d.ts +55 -0
  19. package/dist/error/index.d.ts.map +1 -0
  20. package/dist/error/index.js +38 -0
  21. package/dist/error/index.js.map +1 -0
  22. package/dist/generator/config.d.ts +6 -6
  23. package/dist/generator/config.js +1 -1
  24. package/dist/generator/config.js.map +1 -1
  25. package/dist/generator/contract-scaffolder.d.ts +5 -5
  26. package/dist/generator/contract-scaffolder.js +2 -2
  27. package/dist/generator/contract-scaffolder.js.map +1 -1
  28. package/dist/kysely/helpers.d.ts +29 -39
  29. package/dist/kysely/helpers.d.ts.map +1 -1
  30. package/dist/kysely/helpers.js +183 -169
  31. package/dist/kysely/helpers.js.map +1 -1
  32. package/dist/kysely/type.d.ts +20 -27
  33. package/dist/kysely/type.d.ts.map +1 -1
  34. package/dist/kysely/type.js +21 -36
  35. package/dist/kysely/type.js.map +1 -1
  36. package/dist/runtime/index.d.ts +10 -0
  37. package/dist/runtime/index.d.ts.map +1 -0
  38. package/dist/runtime/index.js +10 -0
  39. package/dist/runtime/index.js.map +1 -0
  40. package/dist/utils/type-mappings.d.ts +8 -13
  41. package/dist/utils/type-mappings.d.ts.map +1 -1
  42. package/dist/utils/type-mappings.js +7 -12
  43. package/dist/utils/type-mappings.js.map +1 -1
  44. package/package.json +12 -8
package/CHANGELOG.md CHANGED
@@ -1,152 +1,44 @@
1
1
  # Changelog
2
2
 
3
- ## 5.9.0
3
+ ## 6.0.0-next.1
4
4
 
5
- ### Minor Changes
6
-
7
- - 5f07c8f: fix: regular tables in DB interface use `Schema.Schema.Type`, join tables use `Schema.Schema.Encoded`
8
-
9
- 5.7.0 flipped the DB interface to `Schema.Schema.Encoded` for **all** tables to fix the join-table column-name bug (`_product_tags.product_id` was decoded-name; SQL needs `A`). That was the right fix for join tables but accidentally **stripped branded IDs** for regular tables — `Schema.Schema.Encoded<typeof X>` strips `Schema.brand(...)` because brands live on the Type side.
10
-
11
- Concrete consequence: every Kysely consumer queried `result.seller_id: string` instead of `result.seller_id: string & Brand<"SellerId">`. Branded ID type safety silently disabled across the entire monorepo.
12
-
13
- Fix: the two table categories need different treatment.
14
- - **Regular tables**: `Schema.Schema.Type<typeof X>` — preserves branded IDs (`string & Brand<"SellerId">`) and the `ColumnType<S, I, U>` `__select__`/`__insert__`/`__update__` phantoms. Type === Encoded for column names anyway because regular tables don't use `Schema.fromKey`.
15
- - **Join tables**: `Schema.Schema.Encoded<typeof X>` — only join tables use `Schema.fromKey('A')` to remap DB columns `A`/`B` to semantic names. Type would expose the decoded names that Kysely passes to SQL verbatim → "column does not exist". Encoded preserves real column names.
16
-
17
- Effectively: pick the side that matches the _intended consumer view_. For non-`fromKey` tables, that's the Type side (richer info, brand info preserved). For `fromKey` tables, that's the Encoded side (matches DB).
18
-
19
- **Migration**: regular-table consumers regain `Brand<...>` IDs immediately. Join-table consumers (`_product_tags.A`/`B` queries) unchanged from 5.7.0 — those still expose real DB column names.
20
-
21
- ## 5.8.0
22
-
23
- ### Minor Changes
5
+ ### Patch Changes
24
6
 
25
- - ac42853: fix: DateTime maps back to `Schema.DateFromSelf` (Date Date) Prisma+Kysely canonical
26
-
27
- Reverts the 5.6.0 change that mapped `DateTime` to `DateFromInput` (a
28
- `Schema.Union(DateFromSelf, Date)` with `Encoded = Date | string`).
29
-
30
- **Why revert**: the dual-input Union pushed the boundary problem onto
31
- DA-layer consumers. Kysely's pg driver returns native `Date` instances,
32
- but `Selectable<X>.created_at` typed as `Date | string` forced every DA
33
- mapper that copies `result.created_at` into a contract type to either
34
- narrow manually (no cast-free path) or wrap the read in
35
- `Schema.decode(Selectable(X))` (heavy refactor across hundreds of sites).
36
-
37
- **Why DateFromSelf is correct**:
38
- - **Prisma docs**: _"Prisma Client returns all DateTime values as native
39
- JavaScript Date objects. ... DateTime values must be passed as Date
40
- objects, not strings, to avoid runtime errors."_
41
- - **Kysely docs**: idiomatic DateTime column is
42
- `created_at: ColumnType<Date, string | undefined, never>` — SELECT
43
- yields `Date`. _"TypeScript is a compile-time concept and cannot
44
- alter runtime JavaScript types. If your TypeScript definition for a
45
- column differs from the database's actual return type, the runtime
46
- type will not change automatically."_
47
- - **Effect Schema docs (Doc 10944)**: _"schemas should be defined such
48
- that encode + decode return the original value"_ — one Type, one
49
- Encoded per schema. The dual-boundary problem (DA Date ↔ Date vs
50
- RPC string ↔ Date) is solved by **two schemas** (one per boundary),
51
- not one Union. Doc 4312 (`@effect/sql/Model.Class`) shows this
52
- canonical variant pattern (`select`/`insert`/`update` vs
53
- `json`/`jsonCreate`/`jsonUpdate`).
54
-
55
- **For RPC/HTTP wire boundaries**: define a contract-layer schema that
56
- overrides date columns with `Schema.Date` (Encoded = string) before the
57
- RPC framework calls `Schema.decode`. This is the same pattern as
58
- `@effect/sql`'s `json` variants — one schema per boundary.
59
-
60
- **`DateFromInput` is still exported** from the package for consumers that
61
- specifically want the dual-input behavior at a single call site. The
62
- codegen just no longer auto-emits it for every DateTime column.
63
-
64
- **The Schema.Schema.Encoded fix in 5.7.0 stays** — that's still correct
65
- for join-table column exposure (`_product_tags.A`/`B`).
66
-
67
- **Migration**: most consumers benefit immediately (DA mappers stop
68
- seeing `Date | string`). For RPC contracts that previously didn't have
69
- a Date override (because they relied on `DateFromInput`), re-add a
70
- `Schema.extend` with `Schema.Date` overrides for date columns to keep
71
- wire decode working.
72
-
73
- ## 5.7.0
7
+ - 5722a22: Fix Insertable/Updateable semantics surfaced by validation of the Effect 4 beta line:
8
+ - **`Insertable` now accepts an explicit `null` for nullable columns.** A
9
+ `Schema.NullOr(T)` field is optional on insert and retains `null` in its type,
10
+ so `{ col: null }` (set the column to NULL) decodes successfully — matching SQL
11
+ and Kysely's `Insertable`, which permit omit / value / explicit null. Previously
12
+ `null` was stripped and an explicit `null` was rejected at decode.
13
+ - **Implicit many-to-many join-table FK columns are now insertable.** They emit
14
+ `columnType(Id, Id, Never)` instead of `columnType(Id, Never, Never)`: the
15
+ foreign keys are provided on INSERT (you supply both keys when linking a row)
16
+ and read-only on UPDATE (a composite-PK join row is inserted/deleted, not
17
+ updated). Previously `Insertable<JoinTable>` resolved to an empty `{}`, making
18
+ join rows impossible to insert through the generated types.
19
+ - **Internal robustness:** Generated-field detection is gated on the `GeneratedId`
20
+ annotation rather than a bare `.from` property, so a `Schema.encodeKeys(...)`
21
+ transform nested as a struct field (which also exposes `.from`) is no longer
22
+ misclassified as a generated field.
23
+
24
+ ## 6.0.0-next.0
74
25
 
75
- ### Minor Changes
26
+ ### Major Changes
76
27
 
77
- - d272b99: fix: DB interface uses `Schema.Schema.Encoded` so Kysely sees real DB columns
78
-
79
- The generated `interface DB` previously emitted
80
- `<table>: Schema.Schema.Type<typeof X>`. For tables using `Schema.fromKey`
81
- (Prisma implicit M:N join tables, where TS field `product_id` maps to DB
82
- column `A`), the Type side has the **decoded** names. Kysely uses the TS
83
- interface as the SQL contract — it does not run the Effect schema decoder.
84
- So queries like `db.selectFrom('_product_tags').where('product_id', ...)`
85
- generated `WHERE product_id = ...` and Postgres rejected with
86
- `column _product_tags.product_id does not exist`.
87
-
88
- Fix: emit `Schema.Schema.Encoded<typeof X>` for every DB interface entry.
89
- Encoded is the on-the-wire / on-disk shape that matches Postgres. For
90
- regular tables `Type === Encoded`, no behavior change. For join tables,
91
- Kysely now sees `A`/`B` and emits valid SQL. Application code that wants
92
- the semantic field names runs the row through `Schema.decode(X)`.
93
-
94
- `ColumnType<S, I, U>` brand preserves `__select__`/`__insert__`/`__update__`
95
- phantoms on both sides, so `Insertable<X>`/`Updateable<X>` inference is
96
- unchanged.
97
-
98
- Adds `db-interface-sql-contract.test.ts` with three regression checks:
99
- 1. String-grep — every DB entry uses Encoded, none use Type.
100
- 2. Encoded-side preserves real Postgres column names for implicit M:N.
101
- 3. Kysely SQL compile — emitted SQL references the real `"A"` column,
102
- not the `product_id` decoded name. This catches the original bug
103
- structurally without needing a live database.
104
-
105
- **Migration**: most consumers need no changes. If a consumer overrode
106
- the generated DB interface entry to expose `A`/`B` directly (workaround
107
- for this bug), the override can now be removed and the generator will
108
- do the right thing.
109
-
110
- ## 5.6.0
28
+ - fde013c: Migrate to Effect 4 (beta).
111
29
 
112
- ### Minor Changes
30
+ **Breaking:** the `effect` peer dependency is now `^4.0.0-beta` (Effect 3 is no longer supported). Consumers must upgrade to `effect@^4.0.0-beta`.
113
31
 
114
- - bf7d87c: feat: DateTime columns now map to `DateFromInput` (dual-boundary Date schema)
115
-
116
- `DateTime` columns previously mapped to `Schema.DateFromSelf`
117
- (`Encoded = Date`), which broke RPC/HTTP wire decode where JSON-parsed
118
- input is a string. Now maps to a new exported `DateFromInput` schema:
119
- - **Type** = `Date` (runtime unchanged)
120
- - **Encoded** = `Date | string` (was `Date`)
121
-
122
- Defined as `Schema.Union(Schema.DateFromSelf, Schema.Date)`, so decode
123
- accepts native `Date` instances (Kysely DA layer pg driver returns Date)
124
- AND ISO strings (RPC/HTTP wire layer — JSON.parse output). One primitive
125
- serves both consumer boundaries; consumers no longer need parallel
126
- schemas or `Schema.extend` overrides for date columns.
127
-
128
- **Why minor (not major)**: existing public API behavior is preserved.
129
- `Selectable<T>` / `Insertable<T>` / `Updateable<T>` Type sides unchanged.
130
- Decode accepts MORE inputs (Date AND string), not fewer. Encode picks
131
- the first union member (`DateFromSelf`, identity) so Kysely-bound
132
- encode still produces Date instances — existing call sites keep working.
133
-
134
- **Why the change**: `DateFromSelf` optimized for the in-memory Kysely
135
- boundary; `Schema.Date` optimizes for the JSON wire boundary. Modern
136
- apps cross both with the same generated schemas. Picking either single
137
- primitive forced consumers to patch around it at one boundary.
138
- `DateFromInput` accepts both encoded shapes natively. Mirrors the
139
- `JsonValue` dual-boundary discipline already in this package
140
- (`Schema<JsonValue, JsonValue>` is wire-safe by construction).
141
-
142
- **Migration**: no code changes for typical consumers. If you imported
143
- `Schema.DateFromSelf` directly from generated `types.ts` in a way that
144
- depended on the literal symbol, switch to `DateFromInput` imported from
145
- `prisma-effect-kysely`.
146
-
147
- **Internal**: consolidated duplicated `PRISMA_TO_EFFECT_SCHEMA` /
148
- `PRISMA_SCALAR_MAP` constants. `src/effect/type.ts` now imports the
149
- canonical map from `src/utils/type-mappings.ts`.
32
+ What changed:
33
+ - **Runtime helpers** (`Selectable` / `Insertable` / `Updateable`) were reimplemented on Effect 4's public `Schema.Struct.fields` API instead of the removed `effect/SchemaAST` internals (Effect 4 reworked `SchemaAST`: `PropertySignature` is now 2-arg, structs are `Objects` nodes, `isTypeLiteral` is gone). Public signatures and the derived `Selectable<T>`/`Insertable<T>`/`Updateable<T>` types are unchanged.
34
+ - **Generated output** now emits Effect-4 schema source:
35
+ - `DateTime` → `Schema.Date` (still native `Date` on both sides — Effect 4's `Schema.Date` no longer coerces to string, replacing Effect 3's `Schema.DateFromSelf`).
36
+ - UUID fields → `Schema.String.check(Schema.isUUID())` (Effect 4 removed `Schema.UUID`).
37
+ - BigInt `Schema.BigInt` (native bigint encoding; replaces `Schema.BigIntFromSelf`).
38
+ - Enums `Schema.Enum(...)`; the internal native TS enum is suffixed with `Enum` when its name collides with the PascalCase const (Effect 4 forbids enum/const identifier merging).
39
+ - `@map` / implicit-M:N `A`/`B` column renames → struct-level `Schema.encodeKeys({ tsName: "db_name" })` (Effect 4 removed `Schema.propertySignature(...).pipe(Schema.fromKey(...))`).
40
+ - Scaffolded contract libraries now declare `effect: ^4.0.0-beta` as their peer dependency.
41
+ - Added a generator-output compile guard (`bun run test:emit`) that type-checks the emitted code against the installed Effect version.
150
42
 
151
43
  ## 5.5.0
152
44
 
package/README.md CHANGED
@@ -4,10 +4,34 @@ Prisma generator producing Effect Schema types with Kysely-compatible column met
4
4
 
5
5
  ## Install
6
6
 
7
+ This package's major version tracks the major version of its `effect` peer
8
+ dependency. Pick the line that matches your Effect version:
9
+
10
+ | Your Effect version | Install | npm dist-tag |
11
+ | ------------------- | ----------------------------------- | ------------ |
12
+ | Effect 3 (stable) | `bun add prisma-effect-kysely` | `latest` |
13
+ | Effect 4 (beta) | `bun add prisma-effect-kysely@next` | `next` |
14
+
7
15
  ```bash
16
+ # Effect 3 (current stable line)
8
17
  bun add prisma-effect-kysely
18
+
19
+ # Effect 4 beta — opt-in pre-release
20
+ bun add prisma-effect-kysely@next effect@beta
9
21
  ```
10
22
 
23
+ > **Effect 4 support is a pre-release.** It requires `effect@^4.0.0-beta` and is
24
+ > published under the `next` dist-tag, not `latest`. It is **tested against
25
+ > `effect@4.0.0-beta.68`**; later betas may introduce breaking Schema changes. A
26
+ > generator-output compile check (`bun run test:emit`) guards the emitted code
27
+ > against the installed Effect, but pin `effect` if you need stability during the
28
+ > beta. The `next` line will be promoted to `latest` when Effect 4 goes stable.
29
+ >
30
+ > Effect 4 and Effect 3 are not interchangeable: the generated output uses
31
+ > Effect-4-only Schema APIs (`Schema.Date`, `Schema.String.check(Schema.isUUID())`,
32
+ > `Schema.encodeKeys`, `Schema.Enum`, …). Stay on the `latest` line if you are on
33
+ > Effect 3.
34
+
11
35
  ## Setup
12
36
 
13
37
  ```prisma
@@ -26,18 +50,18 @@ npx prisma generate
26
50
  Three files: `enums.ts`, `types.ts`, `index.ts`.
27
51
 
28
52
  ```typescript
29
- import { Schema } from "effect";
30
- import { columnType, DateFromInput, generated, Selectable } from "prisma-effect-kysely";
53
+ import { Schema } from 'effect';
54
+ import { columnType, generated, Selectable } from 'prisma-effect-kysely';
31
55
 
32
56
  // Branded ID
33
- export const UserId = Schema.UUID.pipe(Schema.brand("UserId"));
57
+ export const UserId = Schema.String.check(Schema.isUUID()).pipe(Schema.brand('UserId'));
34
58
  export type UserId = typeof UserId.Type;
35
59
 
36
60
  // Model schema
37
61
  export const User = Schema.Struct({
38
- id: columnType(Schema.UUID, Schema.Never, Schema.Never),
62
+ id: columnType(UserId, Schema.Never, Schema.Never),
39
63
  email: Schema.String,
40
- createdAt: generated(DateFromInput),
64
+ createdAt: generated(Schema.Date),
41
65
  });
42
66
  export type User = typeof User;
43
67
 
@@ -73,20 +97,20 @@ Schema names are PascalCase regardless of Prisma model name (`session_preference
73
97
 
74
98
  ## Type Mappings
75
99
 
76
- | Prisma | Effect Schema |
77
- | ----------- | --------------------- |
78
- | String | `Schema.String` |
79
- | Int / Float | `Schema.Number` |
80
- | BigInt | `Schema.BigInt` |
81
- | Decimal | `Schema.String` |
82
- | Boolean | `Schema.Boolean` |
83
- | DateTime | `DateFromInput` |
84
- | Json | `JsonValue` |
85
- | Bytes | `Schema.Uint8Array` |
86
- | Enum | `Schema.Literal(...)` |
87
- | UUID | `Schema.UUID` |
88
-
89
- Arrays → `Schema.Array(t)`. Nullable → `Schema.NullOr(t)`. `DateFromInput` accepts `Date | string` on the encoded side; `JsonValue` is `Schema<JsonValue, JsonValue>`. Both are wire-safe (`JSON.parse` output decodes cleanly).
100
+ | Prisma | Effect Schema |
101
+ | ----------- | -------------------------------------- |
102
+ | String | `Schema.String` |
103
+ | Int / Float | `Schema.Number` |
104
+ | BigInt | `Schema.BigInt` |
105
+ | Decimal | `Schema.String` |
106
+ | Boolean | `Schema.Boolean` |
107
+ | DateTime | `Schema.Date` |
108
+ | Json | recursive `JsonValue` |
109
+ | Bytes | `Schema.Uint8Array` |
110
+ | Enum | `Schema.Enum(...)` |
111
+ | UUID | `Schema.String.check(Schema.isUUID())` |
112
+
113
+ Arrays → `Schema.Array(t)`. Nullable → `Schema.NullOr(t)`.
90
114
 
91
115
  ## UUID Detection
92
116
 
@@ -102,9 +126,9 @@ Use `@customType` in field docs to override Effect Schema:
102
126
 
103
127
  ```prisma
104
128
  model User {
105
- /// @customType(Schema.String.pipe(Schema.email()))
129
+ /// @customType(Schema.String.check(Schema.isMinLength(3)))
106
130
  email String @unique
107
- /// @customType(Schema.Number.pipe(Schema.positive()))
131
+ /// @customType(Schema.Number.check(Schema.isGreaterThan(0)))
108
132
  age Int
109
133
  }
110
134
  ```
@@ -113,28 +137,24 @@ Supported on all Prisma scalar types.
113
137
 
114
138
  ## Implicit M2M Join Tables
115
139
 
116
- Prisma columns `A`/`B` map to semantic snake_case fields via `Schema.fromKey`:
140
+ Prisma columns `A`/`B` map to semantic snake_case fields via `Schema.encodeKeys`:
117
141
 
118
142
  ```typescript
119
143
  export const ProductToProductTag = Schema.Struct({
120
- product_id: Schema.propertySignature(
121
- columnType(Schema.UUID, Schema.Never, Schema.Never)
122
- ).pipe(Schema.fromKey("A")),
123
- product_tag_id: Schema.propertySignature(
124
- columnType(Schema.UUID, Schema.Never, Schema.Never)
125
- ).pipe(Schema.fromKey("B")),
126
- });
144
+ product_id: columnType(ProductId, ProductId, Schema.Never),
145
+ product_tag_id: columnType(ProductTagId, ProductTagId, Schema.Never),
146
+ }).pipe(Schema.encodeKeys({ product_id: 'A', product_tag_id: 'B' }));
127
147
  ```
128
148
 
129
149
  ## Package Exports
130
150
 
131
- | Entry | Contents |
132
- | -------------------------------- | ----------------------------------------------------- |
133
- | `prisma-effect-kysely` | Type utilities + runtime helpers (default import) |
134
- | `prisma-effect-kysely/generator` | Prisma generator binary entry |
135
- | `prisma-effect-kysely/kysely` | `getSchemas`, `columnType`, `generated`, type utils |
136
- | `prisma-effect-kysely/error` | `NotFoundError`, `QueryError`, `QueryParseError`, ... |
137
- | `prisma-effect-kysely/runtime` | All runtime utilities |
151
+ | Entry | Contents |
152
+ | -------------------------------- | --------------------------------------------------- |
153
+ | `prisma-effect-kysely` | Type utilities + runtime helpers (default import) |
154
+ | `prisma-effect-kysely/generator` | Prisma generator binary entry |
155
+ | `prisma-effect-kysely/kysely` | `getSchemas`, `columnType`, `generated`, type utils |
156
+ | `prisma-effect-kysely/error` | `NotFoundError`, `QueryError`, `DatabaseError` |
157
+ | `prisma-effect-kysely/runtime` | All runtime utilities |
138
158
 
139
159
  ## Development
140
160
 
@@ -148,15 +168,28 @@ bun run prepublishOnly # lint + typecheck + test + build
148
168
 
149
169
  ## Releasing
150
170
 
151
- Uses [Changesets](https://github.com/changesets/changesets).
171
+ Uses [Changesets](https://github.com/changesets/changesets). Two lines run in
172
+ parallel:
152
173
 
153
- ```bash
154
- bun changeset # add changeset
155
- git add .changeset/ && git commit -m "docs: changeset"
156
- git push
157
- ```
174
+ - **Stable (`latest`)** — from `main`. Normal flow:
175
+
176
+ ```bash
177
+ bun changeset # add changeset
178
+ git add .changeset/ && git commit -m "docs: changeset"
179
+ git push # CI opens a "Version Packages" PR; merging publishes
180
+ ```
181
+
182
+ - **Pre-release (`next`)** — from the `release/next` branch, which carries
183
+ `.changeset/pre.json` (changesets pre mode, tag `next`). Pushing there versions
184
+ as `X.Y.Z-next.N` and `changeset publish` auto-routes those to the `next`
185
+ dist-tag (never `latest`). This is where Effect 4 support lives until Effect 4
186
+ is stable. When it stabilizes: run `changeset pre exit` on `release/next`,
187
+ merge into `main`, and the next release promotes it to `latest`.
158
188
 
159
- CI opens a "Version Packages" PR. Merging it publishes to npm, tags, and creates a GitHub release. Requires `NPM_TOKEN` repo secret.
189
+ The CI workflow (`.github/workflows/release.yml`) triggers on both branches and
190
+ uses the changesets action's `version` + `publish` inputs; `changeset publish`
191
+ selects the dist-tag from pre mode. Requires the `NPM_TOKEN` repo secret. Do not
192
+ enter pre mode on `main` — it blocks stable releases until you exit.
160
193
 
161
194
  ## License
162
195
 
@@ -1,9 +1,9 @@
1
1
  import type { DMMF } from '@prisma/generator-helper';
2
2
  /**
3
- * Generate TypeScript enum + Effect Schema.Enums wrapper
3
+ * Generate TypeScript enum + Effect Schema.Enum wrapper
4
4
  *
5
5
  * Output pattern:
6
- * - Native TS enum with SCREAMING_SNAKE_CASE (internal, for Schema.Enums)
6
+ * - Native TS enum with SCREAMING_SNAKE_CASE (internal, for Schema.Enum)
7
7
  * - PascalCase export IS the Schema (so it works in Schema.Struct)
8
8
  * - Type alias with same name (value + type pattern)
9
9
  */
@@ -1 +1 @@
1
- {"version":3,"file":"enum.d.ts","sourceRoot":"","sources":["../../src/effect/enum.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAKrD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,IAAI,CAAC,aAAa,UAuB7D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,IAAI,CAAC,aAAa,EAAE,UAMrE"}
1
+ {"version":3,"file":"enum.d.ts","sourceRoot":"","sources":["../../src/effect/enum.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAKrD;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,IAAI,CAAC,aAAa,UA0B7D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,SAAS,IAAI,CAAC,aAAa,EAAE,UAMrE"}
@@ -2,18 +2,21 @@ import { getEnumValueDbName } from '../prisma/enum.js';
2
2
  import { generateFileHeader } from '../utils/codegen.js';
3
3
  import { toPascalCase } from '../utils/naming.js';
4
4
  /**
5
- * Generate TypeScript enum + Effect Schema.Enums wrapper
5
+ * Generate TypeScript enum + Effect Schema.Enum wrapper
6
6
  *
7
7
  * Output pattern:
8
- * - Native TS enum with SCREAMING_SNAKE_CASE (internal, for Schema.Enums)
8
+ * - Native TS enum with SCREAMING_SNAKE_CASE (internal, for Schema.Enum)
9
9
  * - PascalCase export IS the Schema (so it works in Schema.Struct)
10
10
  * - Type alias with same name (value + type pattern)
11
11
  */
12
12
  export function generateEnumSchema(enumDef) {
13
- // Raw enum keeps original name (usually SCREAMING_SNAKE_CASE)
14
- const enumName = enumDef.name;
15
- // PascalCase name is exported as BOTH the Schema value AND the type
13
+ // PascalCase name is exported as BOTH the Schema value AND the type.
16
14
  const pascalName = toPascalCase(enumDef.name);
15
+ // The native TS enum keeps its original Prisma name (usually SCREAMING_SNAKE).
16
+ // But when that already equals the PascalCase const (e.g. `Role` -> `Role`),
17
+ // TypeScript forbids the enum/const identifier from merging, so suffix the
18
+ // enum with `Enum` (`Role` -> internal `RoleEnum`) to keep them distinct.
19
+ const enumName = enumDef.name === pascalName ? `${pascalName}Enum` : enumDef.name;
17
20
  // Generate native TypeScript enum members
18
21
  const enumMembers = enumDef.values
19
22
  .map((v) => {
@@ -28,7 +31,7 @@ export function generateEnumSchema(enumDef) {
28
31
  ${enumMembers}
29
32
  }
30
33
 
31
- export const ${pascalName} = Schema.Enums(${enumName});
34
+ export const ${pascalName} = Schema.Enum(${enumName});
32
35
  export type ${pascalName} = Schema.Schema.Type<typeof ${pascalName}>;`;
33
36
  }
34
37
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"enum.js","sourceRoot":"","sources":["../../src/effect/enum.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAA2B;IAC5D,8DAA8D;IAC9D,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC;IAC9B,oEAAoE;IACpE,MAAM,UAAU,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAE9C,0CAA0C;IAC1C,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM;SAC/B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,KAAK,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAAC;QACpC,OAAO,KAAK,CAAC,CAAC,IAAI,OAAO,KAAK,GAAG,CAAC;IACpC,CAAC,CAAC;SACD,IAAI,CAAC,KAAK,CAAC,CAAC;IAEf,iDAAiD;IACjD,uEAAuE;IACvE,+DAA+D;IAC/D,OAAO,eAAe,QAAQ;EAC9B,WAAW;;;eAGE,UAAU,mBAAmB,QAAQ;cACtC,UAAU,gCAAgC,UAAU,IAAI,CAAC;AACvE,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAoC;IACpE,MAAM,MAAM,GAAG,kBAAkB,EAAE,CAAC;IACpC,MAAM,OAAO,GAAG,kCAAkC,CAAC;IACnD,MAAM,WAAW,GAAG,KAAK,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAE/D,OAAO,GAAG,MAAM,OAAO,OAAO,OAAO,WAAW,EAAE,CAAC;AACrD,CAAC"}
1
+ {"version":3,"file":"enum.js","sourceRoot":"","sources":["../../src/effect/enum.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD;;;;;;;GAOG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAA2B;IAC5D,qEAAqE;IACrE,MAAM,UAAU,GAAG,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,+EAA+E;IAC/E,6EAA6E;IAC7E,2EAA2E;IAC3E,0EAA0E;IAC1E,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC;IAElF,0CAA0C;IAC1C,MAAM,WAAW,GAAG,OAAO,CAAC,MAAM;SAC/B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACT,MAAM,KAAK,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAAC;QACpC,OAAO,KAAK,CAAC,CAAC,IAAI,OAAO,KAAK,GAAG,CAAC;IACpC,CAAC,CAAC;SACD,IAAI,CAAC,KAAK,CAAC,CAAC;IAEf,iDAAiD;IACjD,uEAAuE;IACvE,+DAA+D;IAC/D,OAAO,eAAe,QAAQ;EAC9B,WAAW;;;eAGE,UAAU,kBAAkB,QAAQ;cACrC,UAAU,gCAAgC,UAAU,IAAI,CAAC;AACvE,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAoC;IACpE,MAAM,MAAM,GAAG,kBAAkB,EAAE,CAAC;IACpC,MAAM,OAAO,GAAG,kCAAkC,CAAC;IACnD,MAAM,WAAW,GAAG,KAAK,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IAE/D,OAAO,GAAG,MAAM,OAAO,OAAO,OAAO,WAAW,EAAE,CAAC;AACrD,CAAC"}
@@ -17,7 +17,8 @@ export declare class EffectGenerator {
17
17
  generateBrandedIdSchema(model: DMMF.Model, fields: readonly DMMF.Field[]): string | null;
18
18
  /**
19
19
  * Determine the base Effect Schema type for an ID field.
20
- * UUID strings → Schema.UUID, integers → Schema.Int, bigints → Schema.BigIntFromSelf, all others → Schema.String
20
+ * UUID strings → Schema.String.check(Schema.isUUID()), integers → Schema.Int,
21
+ * bigints → Schema.BigInt, all others → Schema.String
21
22
  */
22
23
  private getIdBaseType;
23
24
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/effect/generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAErD,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAQ/E;;GAEG;AACH,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,IAAI,CAAC,QAAQ;IAEhD;;OAEG;IACH,aAAa,CAAC,KAAK,EAAE,SAAS,IAAI,CAAC,aAAa,EAAE;IAIlD;;;OAGG;IACH,uBAAuB,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,IAAI,CAAC,KAAK,EAAE;IAcxE;;;OAGG;IACH,OAAO,CAAC,aAAa;IAOrB;;;;OAIG;IACH,mBAAmB,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,IAAI,CAAC,KAAK,EAAE;IAqBpE;;OAEG;IACH,mBAAmB,CAAC,QAAQ,EAAE,OAAO;IAsBrC;;OAEG;IACH,wBAAwB,CAAC,UAAU,EAAE,aAAa,EAAE;CAGrD"}
1
+ {"version":3,"file":"generator.d.ts","sourceRoot":"","sources":["../../src/effect/generator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAErD,OAAO,EAAsB,KAAK,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAQ/E;;GAEG;AACH,qBAAa,eAAe;IACd,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,IAAI,CAAC,QAAQ;IAEhD;;OAEG;IACH,aAAa,CAAC,KAAK,EAAE,SAAS,IAAI,CAAC,aAAa,EAAE;IAIlD;;;OAGG;IACH,uBAAuB,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,IAAI,CAAC,KAAK,EAAE;IAcxE;;;;OAIG;IACH,OAAO,CAAC,aAAa;IAOrB;;;;OAIG;IACH,mBAAmB,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,IAAI,CAAC,KAAK,EAAE;IAoCpE;;OAEG;IACH,mBAAmB,CAAC,QAAQ,EAAE,OAAO;IAoBrC;;OAEG;IACH,wBAAwB,CAAC,UAAU,EAAE,aAAa,EAAE;CAGrD"}
@@ -1,4 +1,4 @@
1
- import { buildKyselyFieldType } from '../kysely/type.js';
1
+ import { buildKyselyFieldType, fieldKeyMapping } from '../kysely/type.js';
2
2
  import { buildForeignKeyMap } from '../prisma/relation.js';
3
3
  import { isUuidField } from '../prisma/type.js';
4
4
  import { generateFileHeader } from '../utils/codegen.js';
@@ -37,15 +37,16 @@ export type ${name}Id = typeof ${name}Id.Type;`;
37
37
  }
38
38
  /**
39
39
  * Determine the base Effect Schema type for an ID field.
40
- * UUID strings → Schema.UUID, integers → Schema.Int, bigints → Schema.BigIntFromSelf, all others → Schema.String
40
+ * UUID strings → Schema.String.check(Schema.isUUID()), integers → Schema.Int,
41
+ * bigints → Schema.BigInt, all others → Schema.String
41
42
  */
42
43
  getIdBaseType(field) {
43
44
  if (isUuidField(field))
44
- return 'Schema.UUID';
45
+ return 'Schema.String.check(Schema.isUUID())';
45
46
  if (field.type === 'Int')
46
47
  return 'Schema.Int';
47
48
  if (field.type === 'BigInt')
48
- return 'Schema.BigIntFromSelf';
49
+ return 'Schema.BigInt';
49
50
  return 'Schema.String';
50
51
  }
51
52
  /**
@@ -56,19 +57,30 @@ export type ${name}Id = typeof ${name}Id.Type;`;
56
57
  generateModelSchema(model, fields) {
57
58
  const fkMap = buildForeignKeyMap(model, this.dmmf.datamodel.models);
58
59
  const name = toPascalCase(model.name);
60
+ // Collect @map renames; they are applied once as a struct-level encodeKeys
61
+ // (Effect 4 removed the per-field Schema.fromKey pattern).
62
+ const keyMappings = [];
59
63
  const fieldDefinitions = fields
60
64
  .map((field) => {
61
65
  // Get base Effect type
62
66
  const baseType = buildFieldType(field, this.dmmf, fkMap);
63
- // Apply Kysely helpers (columnType, generated) and @map directive
67
+ // Apply Kysely helpers (columnType, generated)
64
68
  // Pass model.name so @id fields use the model's branded ID type
65
69
  const fieldType = buildKyselyFieldType(baseType, field, model.name);
70
+ const mapping = fieldKeyMapping(field);
71
+ if (mapping)
72
+ keyMappings.push(mapping);
66
73
  return ` ${field.name}: ${fieldType}`;
67
74
  })
68
75
  .join(',\n');
76
+ const encodeKeys = keyMappings.length > 0
77
+ ? `.pipe(Schema.encodeKeys({ ${keyMappings
78
+ .map((m) => `${m.tsName}: "${m.dbName}"`)
79
+ .join(', ')} }))`
80
+ : '';
69
81
  return `export const ${name} = Schema.Struct({
70
82
  ${fieldDefinitions}
71
- });
83
+ })${encodeKeys};
72
84
  export type ${name} = typeof ${name};`;
73
85
  }
74
86
  /**
@@ -76,10 +88,8 @@ export type ${name} = typeof ${name};`;
76
88
  */
77
89
  generateTypesHeader(hasEnums) {
78
90
  const header = generateFileHeader();
79
- // Import runtime helpers from prisma-effect-kysely.
80
- // DateTime fields use Schema.DateFromSelf (Date Date), matching
81
- // Prisma's contract that DateTime values are native Date instances.
82
- // Decode through a Schema.Date contract schema at JSON wire boundaries.
91
+ // Import runtime helpers from prisma-effect-kysely
92
+ // columnType and generated are used for field type annotations
83
93
  const imports = [
84
94
  `import { Schema } from "effect";`,
85
95
  `import { columnType, generated, JsonValue } from "prisma-effect-kysely";`,
@@ -1 +1 @@
1
- {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../src/effect/generator.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../src/effect/generator.ts"],"names":[],"mappings":"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"}
@@ -5,14 +5,16 @@ import type { JoinTableInfo } from '../prisma/relation.js';
5
5
  *
6
6
  * Structure:
7
7
  * - Direct export with semantic snake_case field names
8
- * - Maps TypeScript names to database A/B columns using Schema.fromKey
9
- * - Uses columnType for read-only foreign keys (can't insert/update join table rows directly)
8
+ * - Maps TypeScript names to database A/B columns using Schema.encodeKeys
9
+ * - Uses columnType(Id, Id, Never) for the FK columns: provided on INSERT (you
10
+ * supply both foreign keys when linking a row) but read-only on UPDATE — a
11
+ * composite-PK join row is inserted or deleted, never updated in place
10
12
  * - No type exports - consumers use type utilities: Selectable<JoinTable>
11
13
  *
12
14
  * Example:
13
15
  * - Database columns: A, B (Prisma requirement for implicit many-to-many)
14
16
  * - TypeScript fields: product_id, product_tag_id (semantic names)
15
- * - Types: columnType(ProductId, Schema.Never, Schema.Never) (read-only, branded)
17
+ * - Types: columnType(ProductId, ProductId, Schema.Never) (insertable, read-only on update, branded)
16
18
  */
17
19
  export declare function generateJoinTableSchema(joinTable: JoinTableInfo, _dmmf: DMMF.Document): string;
18
20
  //# sourceMappingURL=join-table.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"join-table.d.ts","sourceRoot":"","sources":["../../src/effect/join-table.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAG3D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,aAAa,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,UA6BrF"}
1
+ {"version":3,"file":"join-table.d.ts","sourceRoot":"","sources":["../../src/effect/join-table.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAG3D;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,aAAa,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,UAgCrF"}
@@ -4,14 +4,16 @@ import { toPascalCase, toSnakeCase } from '../utils/naming.js';
4
4
  *
5
5
  * Structure:
6
6
  * - Direct export with semantic snake_case field names
7
- * - Maps TypeScript names to database A/B columns using Schema.fromKey
8
- * - Uses columnType for read-only foreign keys (can't insert/update join table rows directly)
7
+ * - Maps TypeScript names to database A/B columns using Schema.encodeKeys
8
+ * - Uses columnType(Id, Id, Never) for the FK columns: provided on INSERT (you
9
+ * supply both foreign keys when linking a row) but read-only on UPDATE — a
10
+ * composite-PK join row is inserted or deleted, never updated in place
9
11
  * - No type exports - consumers use type utilities: Selectable<JoinTable>
10
12
  *
11
13
  * Example:
12
14
  * - Database columns: A, B (Prisma requirement for implicit many-to-many)
13
15
  * - TypeScript fields: product_id, product_tag_id (semantic names)
14
- * - Types: columnType(ProductId, Schema.Never, Schema.Never) (read-only, branded)
16
+ * - Types: columnType(ProductId, ProductId, Schema.Never) (insertable, read-only on update, branded)
15
17
  */
16
18
  export function generateJoinTableSchema(joinTable, _dmmf) {
17
19
  const { tableName, relationName, modelA, modelB } = joinTable;
@@ -22,10 +24,13 @@ export function generateJoinTableSchema(joinTable, _dmmf) {
22
24
  // Reference branded ID schemas (e.g., ProductId, SellerId) generated earlier in the output
23
25
  const modelASchemaType = `${toPascalCase(modelA)}Id`;
24
26
  const modelBSchemaType = `${toPascalCase(modelB)}Id`;
25
- // Use columnType for read-only FK fields (can't insert/update join table rows directly)
26
- // Schema.propertySignature + Schema.fromKey maps TypeScript name to database column
27
- const columnAField = ` ${columnAFieldName}: Schema.propertySignature(columnType(${modelASchemaType}, Schema.Never, Schema.Never)).pipe(Schema.fromKey("A"))`;
28
- const columnBField = ` ${columnBFieldName}: Schema.propertySignature(columnType(${modelBSchemaType}, Schema.Never, Schema.Never)).pipe(Schema.fromKey("B"))`;
27
+ // columnType(Id, Id, Never): the FK is supplied on INSERT and read-only on
28
+ // UPDATE (composite-PK join rows are inserted/deleted, not updated).
29
+ // The struct uses semantic field names; Schema.encodeKeys renames them to the
30
+ // database A/B columns on the encoded side (Effect 4 replacement for the old
31
+ // Schema.propertySignature(...).pipe(Schema.fromKey(...)) pattern).
32
+ const columnAField = ` ${columnAFieldName}: columnType(${modelASchemaType}, ${modelASchemaType}, Schema.Never)`;
33
+ const columnBField = ` ${columnBFieldName}: columnType(${modelBSchemaType}, ${modelBSchemaType}, Schema.Never)`;
29
34
  // Use PascalCase for exported name (consistent with regular models)
30
35
  const pascalName = toPascalCase(relationName);
31
36
  // Generate schema with semantic names mapped to A/B
@@ -35,7 +40,7 @@ export function generateJoinTableSchema(joinTable, _dmmf) {
35
40
  export const ${pascalName} = Schema.Struct({
36
41
  ${columnAField},
37
42
  ${columnBField},
38
- });
43
+ }).pipe(Schema.encodeKeys({ ${columnAFieldName}: "A", ${columnBFieldName}: "B" }));
39
44
  export type ${pascalName} = typeof ${pascalName};`;
40
45
  }
41
46
  //# sourceMappingURL=join-table.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"join-table.js","sourceRoot":"","sources":["../../src/effect/join-table.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAE/D;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,uBAAuB,CAAC,SAAwB,EAAE,KAAoB;IACpF,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAE9D,4DAA4D;IAC5D,oEAAoE;IACpE,MAAM,gBAAgB,GAAG,GAAG,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC;IACrD,MAAM,gBAAgB,GAAG,GAAG,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC;IAErD,2FAA2F;IAC3F,MAAM,gBAAgB,GAAG,GAAG,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC;IACrD,MAAM,gBAAgB,GAAG,GAAG,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC;IAErD,wFAAwF;IACxF,oFAAoF;IACpF,MAAM,YAAY,GAAG,KAAK,gBAAgB,yCAAyC,gBAAgB,0DAA0D,CAAC;IAC9J,MAAM,YAAY,GAAG,KAAK,gBAAgB,yCAAyC,gBAAgB,0DAA0D,CAAC;IAE9J,oEAAoE;IACpE,MAAM,UAAU,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;IAE9C,oDAAoD;IACpD,OAAO,MAAM,SAAS;0BACE,MAAM,SAAS,MAAM;wBACvB,gBAAgB,KAAK,gBAAgB;eAC9C,UAAU;EACvB,YAAY;EACZ,YAAY;;cAEA,UAAU,aAAa,UAAU,GAAG,CAAC;AACnD,CAAC"}
1
+ {"version":3,"file":"join-table.js","sourceRoot":"","sources":["../../src/effect/join-table.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AAE/D;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,uBAAuB,CAAC,SAAwB,EAAE,KAAoB;IACpF,MAAM,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC;IAE9D,4DAA4D;IAC5D,oEAAoE;IACpE,MAAM,gBAAgB,GAAG,GAAG,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC;IACrD,MAAM,gBAAgB,GAAG,GAAG,WAAW,CAAC,MAAM,CAAC,KAAK,CAAC;IAErD,2FAA2F;IAC3F,MAAM,gBAAgB,GAAG,GAAG,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC;IACrD,MAAM,gBAAgB,GAAG,GAAG,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC;IAErD,2EAA2E;IAC3E,qEAAqE;IACrE,8EAA8E;IAC9E,6EAA6E;IAC7E,oEAAoE;IACpE,MAAM,YAAY,GAAG,KAAK,gBAAgB,gBAAgB,gBAAgB,KAAK,gBAAgB,iBAAiB,CAAC;IACjH,MAAM,YAAY,GAAG,KAAK,gBAAgB,gBAAgB,gBAAgB,KAAK,gBAAgB,iBAAiB,CAAC;IAEjH,oEAAoE;IACpE,MAAM,UAAU,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC;IAE9C,oDAAoD;IACpD,OAAO,MAAM,SAAS;0BACE,MAAM,SAAS,MAAM;wBACvB,gBAAgB,KAAK,gBAAgB;eAC9C,UAAU;EACvB,YAAY;EACZ,YAAY;8BACgB,gBAAgB,UAAU,gBAAgB;cAC1D,UAAU,aAAa,UAAU,GAAG,CAAC;AACnD,CAAC"}