@prisma/orm-target-sqlite 8.0.0-rc.22-dev.7 → 8.0.0-rc.22-dev.9

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 (95) hide show
  1. package/dist/{adapter-DhxErV2N.d.mts → adapter-BLfNDWw-.d.mts} +2 -2
  2. package/dist/{adapter-DhxErV2N.d.mts.map → adapter-BLfNDWw-.d.mts.map} +1 -1
  3. package/dist/{adapter-gpb0ICA2-DPjB1dMR.mjs → adapter-gpb0ICA2-Ddnczc5D.mjs} +3 -3
  4. package/dist/{adapter-gpb0ICA2-DPjB1dMR.mjs.map → adapter-gpb0ICA2-Ddnczc5D.mjs.map} +1 -1
  5. package/dist/{adapter-xFPDhkod-Cio6up_5.d.mts → adapter-xFPDhkod-DB6XDGgl.d.mts} +3 -3
  6. package/dist/{adapter-xFPDhkod-Cio6up_5.d.mts.map → adapter-xFPDhkod-DB6XDGgl.d.mts.map} +1 -1
  7. package/dist/adapter.d.mts +9 -9
  8. package/dist/adapter.mjs +2 -2
  9. package/dist/adapter__adapter.d.mts +2 -2
  10. package/dist/adapter__adapter.mjs +1 -1
  11. package/dist/adapter__codec-types.d.mts +3 -3
  12. package/dist/adapter__control.d.mts +2 -2
  13. package/dist/adapter__control.mjs +3 -3
  14. package/dist/adapter__runtime.d.mts +1 -1
  15. package/dist/adapter__runtime.mjs +3 -3
  16. package/dist/adapter__sql-renderer.d.mts +2 -2
  17. package/dist/adapter__sql-renderer.mjs +1 -1
  18. package/dist/adapter__types.d.mts +2 -2
  19. package/dist/{codec-types-CG5ZkqDU-DCx8ZvBg.d.mts → codec-types-CGtSWeZ5-B5RH39BA.d.mts} +3 -3
  20. package/dist/{codec-types-CG5ZkqDU-DCx8ZvBg.d.mts.map → codec-types-CGtSWeZ5-B5RH39BA.d.mts.map} +1 -1
  21. package/dist/codec-types-Dx9dQe-Y.d.mts +1 -0
  22. package/dist/codec-types-IproBv-Q.d.mts +2 -0
  23. package/dist/{codecs-BqOj4EKm-BaGeNd2m.d.mts → codecs-A0DbaZRE-CMyLhO_K.d.mts} +10 -2
  24. package/dist/codecs-A0DbaZRE-CMyLhO_K.d.mts.map +1 -0
  25. package/dist/{codecs-My4bmOU6-D4-b5qLd.mjs → codecs-BSYEsc7M-C4nov_lo.mjs} +10 -2
  26. package/dist/{codecs-My4bmOU6-D4-b5qLd.mjs.map → codecs-BSYEsc7M-C4nov_lo.mjs.map} +1 -1
  27. package/dist/{codecs-R3kGRyq2.mjs → codecs-BhRFtE69.mjs} +2 -2
  28. package/dist/{codecs-R3kGRyq2.mjs.map → codecs-BhRFtE69.mjs.map} +1 -1
  29. package/dist/codecs-CX5BZUdO.d.mts +1 -0
  30. package/dist/{control-DXzV_IVS.d.mts → control-Bc3l_1mV.d.mts} +3 -3
  31. package/dist/{control-DXzV_IVS.d.mts.map → control-Bc3l_1mV.d.mts.map} +1 -1
  32. package/dist/{descriptor-meta-DrNwC4vO-B9xeeDp9.mjs → descriptor-meta-DrNwC4vO-C_RZ1Tmp.mjs} +2 -2
  33. package/dist/{descriptor-meta-DrNwC4vO-B9xeeDp9.mjs.map → descriptor-meta-DrNwC4vO-C_RZ1Tmp.mjs.map} +1 -1
  34. package/dist/{migration-DYyfcQB_.mjs → migration-DBqx2jWN.mjs} +2 -2
  35. package/dist/{migration-DYyfcQB_.mjs.map → migration-DBqx2jWN.mjs.map} +1 -1
  36. package/dist/{migration-BHhnGEA2.d.mts → migration-Dekon_QC.d.mts} +3 -3
  37. package/dist/{migration-BHhnGEA2.d.mts.map → migration-Dekon_QC.d.mts.map} +1 -1
  38. package/dist/{op-factory-call-BQYECylm-K7S_xgRS.mjs → op-factory-call-Dxsyn5gF-CvHmM-h_.mjs} +506 -21
  39. package/dist/op-factory-call-Dxsyn5gF-CvHmM-h_.mjs.map +1 -0
  40. package/dist/{op-factory-call-B8bJlvkP.d.mts → op-factory-call-gQ5ddSys.d.mts} +78 -9
  41. package/dist/op-factory-call-gQ5ddSys.d.mts.map +1 -0
  42. package/dist/{pack-CXW0_XyO.d.mts → pack-D3gC0XlT.d.mts} +3 -3
  43. package/dist/{pack-CXW0_XyO.d.mts.map → pack-D3gC0XlT.d.mts.map} +1 -1
  44. package/dist/{planner-DNwf8s8s-BoXZwpVs.mjs → planner-DgEGQuj9-Ca6jqWrd.mjs} +111 -64
  45. package/dist/planner-DgEGQuj9-Ca6jqWrd.mjs.map +1 -0
  46. package/dist/{planner-DcOz-1na.d.mts → planner-ccevdpwf.d.mts} +2 -2
  47. package/dist/{planner-DcOz-1na.d.mts.map → planner-ccevdpwf.d.mts.map} +1 -1
  48. package/dist/{planner-produced-sqlite-migration-DyUPHRKW-CFtZi2VG.d.mts → planner-produced-sqlite-migration-BHH_ftCV-CndNmTrH.d.mts} +3 -3
  49. package/dist/planner-produced-sqlite-migration-BHH_ftCV-CndNmTrH.d.mts.map +1 -0
  50. package/dist/{planner-produced-sqlite-migration-Z9OWyjQn-DGMHz6I1.mjs → planner-produced-sqlite-migration-BMogETJ0-COe4tate.mjs} +4 -4
  51. package/dist/{planner-produced-sqlite-migration-Z9OWyjQn-DGMHz6I1.mjs.map → planner-produced-sqlite-migration-BMogETJ0-COe4tate.mjs.map} +1 -1
  52. package/dist/planner-produced-sqlite-migration-_n61WaiD.d.mts +1 -0
  53. package/dist/{render-ops-CJXrOY63-1y0nLsb8.mjs → render-ops-BUk3wlAR-Awdho1_L.mjs} +3 -3
  54. package/dist/{render-ops-CJXrOY63-1y0nLsb8.mjs.map → render-ops-BUk3wlAR-Awdho1_L.mjs.map} +1 -1
  55. package/dist/{runtime-BQKQH94p.d.mts → runtime-H_OgXoaz.d.mts} +2 -2
  56. package/dist/{runtime-BQKQH94p.d.mts.map → runtime-H_OgXoaz.d.mts.map} +1 -1
  57. package/dist/sql-renderer-_NdIrQuX.d.mts +1 -0
  58. package/dist/{sqlite-migration-BxQ65pKq-C7siJGuR.mjs → sqlite-migration-B42dCAqX-DiSO-PeZ.mjs} +47 -7
  59. package/dist/sqlite-migration-B42dCAqX-DiSO-PeZ.mjs.map +1 -0
  60. package/dist/{sqlite-migration-CcXBptQr-DlKSNedI.d.mts → sqlite-migration-C_9TPlLD-DtKN8bi9.d.mts} +52 -4
  61. package/dist/sqlite-migration-C_9TPlLD-DtKN8bi9.d.mts.map +1 -0
  62. package/dist/{tables-C9afyQN9-CkIhVNYn.d.mts → tables-BY3u6rl_-Coqqy4JK.d.mts} +11 -3
  63. package/dist/tables-BY3u6rl_-Coqqy4JK.d.mts.map +1 -0
  64. package/dist/target.d.mts +12 -12
  65. package/dist/target.mjs +9 -9
  66. package/dist/target__codec-types.d.mts +3 -3
  67. package/dist/target__codecs.d.mts +2 -2
  68. package/dist/target__codecs.mjs +2 -2
  69. package/dist/target__control.mjs +52 -32
  70. package/dist/target__control.mjs.map +1 -1
  71. package/dist/target__migration.d.mts +2 -2
  72. package/dist/target__migration.mjs +2 -2
  73. package/dist/target__op-factory-call.d.mts +2 -2
  74. package/dist/target__op-factory-call.mjs +2 -2
  75. package/dist/target__pack.d.mts +1 -1
  76. package/dist/target__planner-produced-sqlite-migration.d.mts +2 -2
  77. package/dist/target__planner-produced-sqlite-migration.mjs +1 -1
  78. package/dist/target__planner.d.mts +1 -1
  79. package/dist/target__planner.mjs +1 -1
  80. package/dist/target__render-ops.mjs +1 -1
  81. package/dist/{types-DpDK9e9U.d.mts → types-28E593y9.d.mts} +1 -1
  82. package/package.json +10 -10
  83. package/dist/codec-types-BQCSRfvR.d.mts +0 -1
  84. package/dist/codec-types-YpVaNlY0.d.mts +0 -2
  85. package/dist/codecs-BqOj4EKm-BaGeNd2m.d.mts.map +0 -1
  86. package/dist/codecs-DzkRnjjE.d.mts +0 -1
  87. package/dist/op-factory-call-B8bJlvkP.d.mts.map +0 -1
  88. package/dist/op-factory-call-BQYECylm-K7S_xgRS.mjs.map +0 -1
  89. package/dist/planner-DNwf8s8s-BoXZwpVs.mjs.map +0 -1
  90. package/dist/planner-produced-sqlite-migration-DxyN096L.d.mts +0 -1
  91. package/dist/planner-produced-sqlite-migration-DyUPHRKW-CFtZi2VG.d.mts.map +0 -1
  92. package/dist/sql-renderer-CgY2Sghp.d.mts +0 -1
  93. package/dist/sqlite-migration-BxQ65pKq-C7siJGuR.mjs.map +0 -1
  94. package/dist/sqlite-migration-CcXBptQr-DlKSNedI.d.mts.map +0 -1
  95. package/dist/tables-C9afyQN9-CkIhVNYn.d.mts.map +0 -1
@@ -7,16 +7,17 @@ import { DdlColumn, FunctionColumnDefault, LiteralColumnDefault, opaqueSql } fro
7
7
  import { ifDefined } from "@prisma/orm-framework/utils/defined";
8
8
  import "@prisma/orm-framework/components/codec";
9
9
  import { blindCast } from "@prisma/orm-framework/utils/casts";
10
- import { assertNever } from "@prisma/orm-framework/utils/internal-error";
10
+ import { InternalError, assertNever } from "@prisma/orm-framework/utils/internal-error";
11
11
  import "@prisma/orm-family-sql/contract/data-type";
12
12
  import { type } from "arktype";
13
+ import { CliStructuredError } from "@prisma/orm-framework/errors/control";
13
14
  import { expectAbsent, expectPresent, issueOutcome } from "@prisma/orm-framework/components/control";
14
- import { checkSqlExpressionText, convertedColumnMeta, createSqlTextSources, encodeStoredValue, isUnfilledSlot, requiredColumnMeta, rowsHoldingValueMeta, slotSource, sqlOfSlot } from "@prisma/orm-family-sql/family/control";
15
+ import { UnfilledValue, checkSqlExpressionText, convertedColumnMeta, createSqlTextSources, encodeStoredValue, fillKeyProblem, fillValueProblem, isUnfilledSlot, keyPathOf, parseJsonPath, requiredColumnMeta, rowsHoldingValueMeta, slotSource, sqlOfSlot } from "@prisma/orm-family-sql/family/control";
15
16
  import { RelationalSchemaNodeKind, sqlColumnTypesEqual } from "@prisma/orm-family-sql/schema-ir/types";
16
- import { errorMigrationOperationOptionRemoved, unfilledPlaceholderOperation } from "@prisma/orm-framework/errors/migration";
17
+ import { errorMigrationOperationOptionRemoved, errorUnfilledPlaceholder, unfilledPlaceholderOperation } from "@prisma/orm-framework/errors/migration";
17
18
  import { REFERENTIAL_ACTION_SQL } from "@prisma/orm-family-sql/contract/referential-action-sql";
18
19
  import { TsExpression, jsonToTsSource, tsArraySource, tsObjectSource, tsQuotedTextSource } from "@prisma/orm-framework/ts-render";
19
- //#region ../../../3-targets/sqlite/1-target/dist/op-factory-call-BQYECylm.mjs
20
+ //#region ../../../3-targets/sqlite/1-target/dist/op-factory-call-Dxsyn5gF.mjs
20
21
  function foldAsciiLetters(identifier) {
21
22
  return identifier.replace(/[A-Z]/g, (letter) => letter.toLowerCase());
22
23
  }
@@ -208,6 +209,130 @@ async function renameColumn(tableName, fromName, toName, lowerer) {
208
209
  postcheck: [step(`verify column "${toName}" exists on "${tableName}"`, toPresent.sql, toPresent.params, expectPresent(`column "${toName}" on "${tableName}"`)), step(`verify column "${fromName}" no longer exists on "${tableName}"`, fromAbsent.sql, fromAbsent.params, expectAbsent(`column "${fromName}" on "${tableName}"`))]
209
210
  };
210
211
  }
212
+ /** The SQLite JSON path of member `name`, as an SQL string literal. */
213
+ function memberPath(name) {
214
+ return `'${escapeLiteral(`$.${JSON.stringify(name)}`)}'`;
215
+ }
216
+ function child(value, depth) {
217
+ return `(${value} -> e${depth}.fullkey)`;
218
+ }
219
+ /**
220
+ * The children of a list or an object rewritten one by one, keeping their order; `kept` filters
221
+ * them. Any other value is left as it is.
222
+ */
223
+ function rewrittenChildren(value, depth, rewrite, kept) {
224
+ const e = `e${depth}`;
225
+ const each = `FROM json_each(${value}) AS ${e}${kept === void 0 ? "" : ` WHERE ${kept(child(value, depth))}`}`;
226
+ const rewritten = `json(${rewrite(child(value, depth))})`;
227
+ return `CASE json_type(${value}) WHEN 'array' THEN (SELECT json_group_array(${rewritten} ORDER BY ${e}.key) ${each}) WHEN 'object' THEN (SELECT json_group_object(${e}.key, ${rewritten} ORDER BY ${e}.id) ${each}) ELSE ${value} END`;
228
+ }
229
+ /** Whether `value` is a list or an object with a child that passes `test`. */
230
+ function someChild(value, depth, test) {
231
+ return `(json_type(${value}) IN ('array', 'object') AND EXISTS (SELECT 1 FROM json_each(${value}) AS e${depth} WHERE ${test(child(value, depth))}))`;
232
+ }
233
+ /**
234
+ * The JSON text that rewrites `value` along `steps` with `leaf`. A member step that meets no object
235
+ * with the member, or a wildcard step that meets neither a list nor an object, leaves the value as
236
+ * it is.
237
+ */
238
+ function jsonRewrite(value, steps, leaf, depth = 0) {
239
+ const [first, ...rest] = steps;
240
+ if (first === void 0) return leaf(value, depth);
241
+ if (first.kind === "member") {
242
+ const member = memberPath(first.name);
243
+ return `CASE WHEN json_type(${value}) = 'object' AND json_type(${value}, ${member}) IS NOT NULL THEN json_set(${value}, ${member}, json(${jsonRewrite(`(${value} -> ${member})`, rest, leaf, depth)})) ELSE ${value} END`;
244
+ }
245
+ return rewrittenChildren(value, depth, (c) => jsonRewrite(c, rest, leaf, depth + 1));
246
+ }
247
+ /**
248
+ * Whether the JSON text `value` holds, at the end of `steps`, a value that passes `test`. Each step
249
+ * tests the type of what it reads first, so a SQL `NULL`, a JSON `null`, a missing member, a value
250
+ * of another type, or a `null` element or member value never fails the match.
251
+ */
252
+ function jsonMatches(value, steps, test, depth = 0) {
253
+ const [first, ...rest] = steps;
254
+ if (first === void 0) return test(value, depth);
255
+ if (first.kind === "member") {
256
+ const member = memberPath(first.name);
257
+ return `(json_type(${value}) = 'object' AND json_type(${value}, ${member}) IS NOT NULL AND ${jsonMatches(`(${value} -> ${member})`, rest, test, depth)})`;
258
+ }
259
+ return someChild(value, depth, (c) => jsonMatches(c, rest, test, depth + 1));
260
+ }
261
+ /** Whether the JSON text `value` is an object that has member `name`. */
262
+ function hasMember(value, name) {
263
+ return `(json_type(${value}) = 'object' AND json_type(${value}, ${memberPath(name)}) IS NOT NULL)`;
264
+ }
265
+ function at(keyPath) {
266
+ return `at path ${JSON.stringify(keyPath.objectsText)}`;
267
+ }
268
+ /** Where in a column a key operation acts, as its label and step descriptions write it. */
269
+ function keyPlace(keyPath, tableName, columnName) {
270
+ return `${at(keyPath)} in column "${columnName}" of "${tableName}"`;
271
+ }
272
+ function noRowWhere(tableName, condition) {
273
+ return `SELECT NOT EXISTS (SELECT 1 FROM ${quoteIdentifier(tableName)} WHERE ${condition}) AS result`;
274
+ }
275
+ function objectsMatch(columnName, keyPath, test) {
276
+ return jsonMatches(quoteIdentifier(columnName), keyPath.objects, test);
277
+ }
278
+ function renameStoredKey(tableName, columnName, keyPath, to) {
279
+ const column = quoteIdentifier(columnName);
280
+ const from = memberPath(keyPath.key);
281
+ const renamed = jsonRewrite(column, keyPath.objects, (object) => `CASE WHEN ${hasMember(object, keyPath.key)} THEN json_set(json_remove(${object}, ${from}), ${memberPath(to)}, json(${object} -> ${from})) ELSE ${object} END`);
282
+ const holdsKey = objectsMatch(columnName, keyPath, (object) => hasMember(object, keyPath.key));
283
+ const place = keyPlace(keyPath, tableName, columnName);
284
+ return {
285
+ id: `renameStoredKey.${tableName}.${columnName}.${keyPath.path.text}`,
286
+ label: `Rename key "${keyPath.key}" to "${to}" ${place}`,
287
+ operationClass: "widening",
288
+ target: {
289
+ id: "sqlite",
290
+ details: buildTargetDetails("column", columnName, tableName)
291
+ },
292
+ precheck: [step(`ensure no object ${place} has both key "${keyPath.key}" and key "${to}"; remove one of the two keys from those objects`, noRowWhere(tableName, objectsMatch(columnName, keyPath, (object) => `(${hasMember(object, keyPath.key)} AND ${hasMember(object, to)})`)))],
293
+ execute: [step(`rename key "${keyPath.key}" to "${to}" ${place}`, `UPDATE ${quoteIdentifier(tableName)} SET ${column} = ${renamed} WHERE ${holdsKey}`)],
294
+ postcheck: [step(`verify no object ${place} has key "${keyPath.key}"`, noRowWhere(tableName, holdsKey))]
295
+ };
296
+ }
297
+ function removeStoredKey(tableName, columnName, keyPath) {
298
+ const column = quoteIdentifier(columnName);
299
+ const removed = jsonRewrite(column, keyPath.objects, (object) => `CASE WHEN ${hasMember(object, keyPath.key)} THEN json_remove(${object}, ${memberPath(keyPath.key)}) ELSE ${object} END`);
300
+ const holdsKey = objectsMatch(columnName, keyPath, (object) => hasMember(object, keyPath.key));
301
+ const place = keyPlace(keyPath, tableName, columnName);
302
+ return {
303
+ id: `removeStoredKey.${tableName}.${columnName}.${keyPath.path.text}`,
304
+ label: `Remove key "${keyPath.key}" ${place}`,
305
+ operationClass: "destructive",
306
+ target: {
307
+ id: "sqlite",
308
+ details: buildTargetDetails("column", columnName, tableName)
309
+ },
310
+ precheck: [],
311
+ execute: [step(`remove key "${keyPath.key}" ${place}`, `UPDATE ${quoteIdentifier(tableName)} SET ${column} = ${removed} WHERE ${holdsKey}`)],
312
+ postcheck: [step(`verify no object ${place} has key "${keyPath.key}"`, noRowWhere(tableName, holdsKey))]
313
+ };
314
+ }
315
+ /** `value` is the JSON text of the value the key takes, passed as a parameter. A key that holds JSON `null` is filled too, as a member that becomes required needs. */
316
+ function fillStoredKey(tableName, columnName, keyPath, value) {
317
+ const column = quoteIdentifier(columnName);
318
+ const member = memberPath(keyPath.key);
319
+ const lacks = (object) => `(json_type(${object}) = 'object' AND coalesce(json_type(${object}, ${member}), 'null') = 'null')`;
320
+ const filled = jsonRewrite(column, keyPath.objects, (object) => `CASE WHEN ${lacks(object)} THEN json_set(${object}, ${member}, json(?1)) ELSE ${object} END`);
321
+ const lacksKey = objectsMatch(columnName, keyPath, lacks);
322
+ const place = keyPlace(keyPath, tableName, columnName);
323
+ return {
324
+ id: `fillStoredKey.${tableName}.${columnName}.${keyPath.path.text}`,
325
+ label: `Fill key "${keyPath.key}" ${place}`,
326
+ operationClass: "widening",
327
+ target: {
328
+ id: "sqlite",
329
+ details: buildTargetDetails("column", columnName, tableName)
330
+ },
331
+ precheck: [],
332
+ execute: [step(`fill key "${keyPath.key}" ${place} in every object that lacks it or holds JSON null there`, `UPDATE ${quoteIdentifier(tableName)} SET ${column} = ${filled} WHERE ${lacksKey}`, [value])],
333
+ postcheck: [step(`verify every object ${place} has key "${keyPath.key}"`, noRowWhere(tableName, lacksKey))]
334
+ };
335
+ }
211
336
  /** A stored value as operation ids, labels and step descriptions write it. */
212
337
  function storedValueText(value) {
213
338
  return JSON.stringify(value);
@@ -246,12 +371,140 @@ function removeStoredValue(tableName, columnName, value, mark) {
246
371
  },
247
372
  precheck: [{
248
373
  ...columnTakesNullOrValueAbsentCheck(tableName, columnName, value, takesNull),
249
- ...mark === void 0 ? {} : { meta: rowsHoldingValueMeta(mark) }
374
+ ...mark === void 0 ? {} : { meta: rowsHoldingValueMeta({
375
+ ...mark,
376
+ table: tableName,
377
+ column: columnName,
378
+ storedValue: value
379
+ }) }
250
380
  }],
251
381
  execute: [step(`remove value ${storedValueText(value)} from column "${columnName}"`, `UPDATE ${quoteIdentifier(tableName)} SET ${column} = NULL WHERE ${column} = ?1`, [value])],
252
382
  postcheck: [valueAbsentCheck(tableName, columnName, value)]
253
383
  };
254
384
  }
385
+ function cascadeStep(tableName, columnName, value, cascade) {
386
+ const referenced = cascade.columns.map((column, index) => {
387
+ const reference = cascade.references[index];
388
+ if (reference === void 0) throw new InternalError(`Cascade of table "${cascade.table}" has no reference for column "${column}"`);
389
+ return `${quoteIdentifier(tableName)}.${quoteIdentifier(reference)} = ${quoteIdentifier(cascade.table)}.${quoteIdentifier(column)}`;
390
+ }).join(" AND ");
391
+ return step(`delete the rows of "${cascade.table}" whose row of "${tableName}" holds value ${storedValueText(value)} in column "${columnName}", as their foreign key's cascade would`, `DELETE FROM ${quoteIdentifier(cascade.table)} WHERE EXISTS (SELECT 1 FROM ${quoteIdentifier(tableName)} WHERE ${quoteIdentifier(tableName)}.${quoteIdentifier(columnName)} = ?1 AND ${referenced})`, [value]);
392
+ }
393
+ /** The label of a `deleteRows` operation, which names each `cascade` table whose rows it deletes too. */
394
+ function deleteRowsLabel(tableName, columnName, value, cascade) {
395
+ const rows = `Delete the rows of "${tableName}" that hold value ${storedValueText(value)} in column "${columnName}"`;
396
+ if (cascade.length === 0) return rows;
397
+ return `${rows}, and the rows of ${cascade.map(({ table }) => `"${table}"`).join(", ")} that reference them`;
398
+ }
399
+ /** Deletes the rows that hold `value`, after the rows of each `cascade` table that reference them: the runner turns foreign keys off, so no declared cascade runs. */
400
+ function deleteRows(tableName, columnName, value, cascade) {
401
+ return {
402
+ id: `deleteRows.${tableName}.${columnName}.${idText(value)}`,
403
+ label: deleteRowsLabel(tableName, columnName, value, cascade),
404
+ operationClass: "destructive",
405
+ target: {
406
+ id: "sqlite",
407
+ details: buildTargetDetails("column", columnName, tableName)
408
+ },
409
+ precheck: [],
410
+ execute: [...cascade.map((foreignKey) => cascadeStep(tableName, columnName, value, foreignKey)), step(`delete the rows of "${tableName}" that hold value ${storedValueText(value)} in column "${columnName}"`, `DELETE FROM ${quoteIdentifier(tableName)} WHERE ${quoteIdentifier(columnName)} = ?1`, [value])],
411
+ postcheck: [valueAbsentCheck(tableName, columnName, value)]
412
+ };
413
+ }
414
+ function pathParts(path) {
415
+ const last = path.steps.at(-1);
416
+ if (last === void 0) throw new InternalError("A stored value path ends at a selector");
417
+ return {
418
+ reach: path.steps.slice(0, -1),
419
+ last
420
+ };
421
+ }
422
+ /** Whether the JSON text `value` is the JSON form of `stored`, bound as parameter `param`. */
423
+ function isStoredValue(value, stored, param) {
424
+ return `(json_type(${value}) ${typeof stored === "string" ? `= 'text'` : `IN ('integer', 'real')`} AND (${value} ->> '$') = ${param})`;
425
+ }
426
+ function memberHolds(object, name, stored, param) {
427
+ return `(json_type(${object}) = 'object' AND ${isStoredValue(`(${object} -> ${memberPath(name)})`, stored, param)})`;
428
+ }
429
+ /** Whether the JSON text of `column` holds `stored`, bound as parameter `param`, at the end of `path`. */
430
+ function holdsAtPath(column, path, stored, param) {
431
+ const { reach, last } = pathParts(path);
432
+ return jsonMatches(column, reach, (value, depth) => last.kind === "member" ? memberHolds(value, last.name, stored, param) : someChild(value, depth, (child) => isStoredValue(child, stored, param)));
433
+ }
434
+ /** The JSON text that sets the value at the end of `path` that equals `?1` to `replacement`, or, when `replacement` is undefined, removes it from a list or removes the member of an object that holds it. */
435
+ function rewriteAtPath(column, path, stored, replacement) {
436
+ const { reach, last } = pathParts(path);
437
+ if (last.kind === "member") return jsonRewrite(column, reach, (object) => `CASE WHEN ${memberHolds(object, last.name, stored, "?1")} THEN json_set(${object}, ${memberPath(last.name)}, ${replacement ?? "json('null')"}) ELSE ${object} END`);
438
+ return jsonRewrite(column, reach, (container, depth) => replacement === void 0 ? rewrittenChildren(container, depth, (child) => child, (child) => `NOT ${isStoredValue(child, stored, "?1")}`) : rewrittenChildren(container, depth, (child) => `CASE WHEN ${isStoredValue(child, stored, "?1")} THEN json_quote(${replacement}) ELSE ${child} END`));
439
+ }
440
+ function atPath(path, tableName, columnName) {
441
+ return `at path ${JSON.stringify(path.text)} in column "${columnName}" of "${tableName}"`;
442
+ }
443
+ function valueAbsentAtPathCheck(tableName, columnName, value, path) {
444
+ return step(`verify no row holds value ${storedValueText(value)} ${atPath(path, tableName, columnName)}`, `SELECT NOT EXISTS (SELECT 1 FROM ${quoteIdentifier(tableName)} WHERE ${holdsAtPath(quoteIdentifier(columnName), path, value, "?1")}) AS result`, [value]);
445
+ }
446
+ /** The condition and parameters that find the rows of a column that hold a stored value, at `path` inside its JSON when it is given. */
447
+ function holdingStoredValue(columnName, storedValue, path) {
448
+ const column = quoteIdentifier(columnName);
449
+ if (path === void 0) return {
450
+ condition: `${column} = ?1`,
451
+ params: [storedValue]
452
+ };
453
+ const parsed = parseJsonPath(path);
454
+ if (!parsed.ok) throw new InternalError(`A stored value path is a JSON path: ${parsed.failure}`);
455
+ return {
456
+ condition: holdsAtPath(column, parsed.value, storedValue, "?1"),
457
+ params: [storedValue]
458
+ };
459
+ }
460
+ function replaceStoredValueAtPath(tableName, columnName, from, to, path) {
461
+ const column = quoteIdentifier(columnName);
462
+ const place = atPath(path, tableName, columnName);
463
+ return {
464
+ id: `replaceStoredValue.${tableName}.${columnName}.${path.text}.${idText(from)}`,
465
+ label: `Replace value ${storedValueText(from)} with ${storedValueText(to)} ${place}`,
466
+ operationClass: "widening",
467
+ target: {
468
+ id: "sqlite",
469
+ details: buildTargetDetails("column", columnName, tableName)
470
+ },
471
+ precheck: [],
472
+ execute: [step(`replace value ${storedValueText(from)} with ${storedValueText(to)} ${place}`, `UPDATE ${quoteIdentifier(tableName)} SET ${column} = ${rewriteAtPath(column, path, from, "?2")} WHERE ${holdsAtPath(column, path, from, "?1")}`, [from, to])],
473
+ postcheck: [valueAbsentAtPathCheck(tableName, columnName, from, path)]
474
+ };
475
+ }
476
+ /**
477
+ * Removes a stored value inside JSON: takes it out of a list, or removes its member from an object,
478
+ * when `path` ends at a wildcard, else sets the member to JSON `null`. When the member takes no
479
+ * null, the check fails while a row holds the value.
480
+ */
481
+ function removeStoredValueAtPath(tableName, columnName, value, path, nullable, mark) {
482
+ const column = quoteIdentifier(columnName);
483
+ const place = atPath(path, tableName, columnName);
484
+ const holds = holdsAtPath(column, path, value, "?1");
485
+ const required = pathParts(path).last.kind === "member" && !nullable;
486
+ return {
487
+ id: `removeStoredValue.${tableName}.${columnName}.${path.text}.${idText(value)}`,
488
+ label: `Remove value ${storedValueText(value)} ${place}`,
489
+ operationClass: "destructive",
490
+ target: {
491
+ id: "sqlite",
492
+ details: buildTargetDetails("column", columnName, tableName)
493
+ },
494
+ precheck: required ? [{
495
+ ...step(`ensure no row holds ${storedValueText(value)} ${place}, a member that takes no null`, `SELECT NOT EXISTS (SELECT 1 FROM ${quoteIdentifier(tableName)} WHERE ${holds}) AS result`, [value]),
496
+ ...mark === void 0 ? {} : { meta: rowsHoldingValueMeta({
497
+ ...mark,
498
+ table: tableName,
499
+ column: columnName,
500
+ storedValue: value,
501
+ path: path.text
502
+ }) }
503
+ }] : [],
504
+ execute: [step(`remove value ${storedValueText(value)} ${place}`, `UPDATE ${quoteIdentifier(tableName)} SET ${column} = ${rewriteAtPath(column, path, value, void 0)} WHERE ${holds}`, [value])],
505
+ postcheck: [valueAbsentAtPathCheck(tableName, columnName, value, path)]
506
+ };
507
+ }
255
508
  function buildCreateIndexSql(tableName, indexName, columns, unique = false) {
256
509
  return `CREATE ${unique ? "UNIQUE " : ""}INDEX ${quoteIdentifier(indexName)} ON ${quoteIdentifier(tableName)} (${columns.map(quoteIdentifier).join(", ")})`;
257
510
  }
@@ -842,7 +1095,7 @@ var DropTableCall = class extends SqliteOpFactoryCallNode {
842
1095
  constructor(tableName) {
843
1096
  super();
844
1097
  this.tableName = tableName;
845
- this.label = `Drop table ${tableName}`;
1098
+ this.label = `Drop table "${tableName}"`;
846
1099
  this.freeze();
847
1100
  }
848
1101
  async toOp(lowerer) {
@@ -855,7 +1108,7 @@ var DropTableCall = class extends SqliteOpFactoryCallNode {
855
1108
  const absent = await lowerer.lowerToExecuteRequest(checks.tableAbsent());
856
1109
  return {
857
1110
  id: `dropTable.${this.tableName}`,
858
- label: `Drop table ${this.tableName}`,
1111
+ label: `Drop table "${this.tableName}"`,
859
1112
  summary: `Drops table ${this.tableName} which is not in the contract`,
860
1113
  operationClass: "destructive",
861
1114
  target: {
@@ -1206,7 +1459,7 @@ var DropColumnCall = class extends SqliteOpFactoryCallNode {
1206
1459
  super();
1207
1460
  this.tableName = tableName;
1208
1461
  this.columnName = columnName;
1209
- this.label = `Drop column ${columnName} on ${tableName}`;
1462
+ this.label = `Drop column "${columnName}" from "${tableName}"`;
1210
1463
  this.freeze();
1211
1464
  }
1212
1465
  async toOp(lowerer) {
@@ -1220,7 +1473,7 @@ var DropColumnCall = class extends SqliteOpFactoryCallNode {
1220
1473
  const absent = await lowerer.lowerToExecuteRequest(checks.columnAbsent());
1221
1474
  return {
1222
1475
  id: `dropColumn.${this.tableName}.${this.columnName}`,
1223
- label: `Drop column ${this.columnName} on ${this.tableName}`,
1476
+ label: `Drop column "${this.columnName}" from "${this.tableName}"`,
1224
1477
  summary: `Drops column ${this.columnName} on ${this.tableName} which is not in the contract`,
1225
1478
  operationClass: "destructive",
1226
1479
  target: {
@@ -1335,15 +1588,34 @@ var DropIndexCall = class extends SqliteOpFactoryCallNode {
1335
1588
  return [];
1336
1589
  }
1337
1590
  };
1338
- function replaceStoredValueSource(tableName, columnName, from, to, codec) {
1591
+ function replaceStoredValueSource(tableName, columnName, from, to, codec, path) {
1339
1592
  return `this.replaceStoredValue({ ${[
1340
1593
  `table: ${jsonToTsSource(tableName)}`,
1341
1594
  `column: ${jsonToTsSource(columnName)}`,
1595
+ ...pathSource(path),
1342
1596
  `from: ${from}`,
1343
1597
  `to: ${to}`,
1344
1598
  ...codecRefSource(codec)
1345
1599
  ].join(", ")} })`;
1346
1600
  }
1601
+ function pathSource(path) {
1602
+ return path === void 0 ? [] : [`path: ${jsonToTsSource(path.text)}`];
1603
+ }
1604
+ function storedValuePlace(path, tableName, columnName) {
1605
+ return path === void 0 ? `in column "${columnName}" of "${tableName}"` : `at path ${JSON.stringify(path.text)} in column "${columnName}" of "${tableName}"`;
1606
+ }
1607
+ /**
1608
+ * The path a stored value call acts at inside JSON: it ends at a member name or at a list
1609
+ * wildcard, and the value is compared as a JSON value, so it takes no codec.
1610
+ */
1611
+ function requireValuePath(path, codec, factory, tableName, columnName) {
1612
+ if (path === void 0) return void 0;
1613
+ const location = columnCallLocation(tableName, columnName);
1614
+ const parsed = requirePath(path, factory, location);
1615
+ if (parsed.steps.length === 0) refusedArgument(factory, location, "path", `path ${JSON.stringify(path)} does not end at a member name or a wildcard ([*] or .*)`);
1616
+ if (codec !== void 0) refusedArgument(factory, location, "path", "path takes no codecRef: a value inside JSON is compared as a JSON value");
1617
+ return parsed;
1618
+ }
1347
1619
  function codecRefSource(codec) {
1348
1620
  return codec === void 0 ? [] : [`codecRef: ${jsonToTsSource(codec.codecRef)}`];
1349
1621
  }
@@ -1352,7 +1624,7 @@ function requireStoredValue(value, factory, tableName, columnName, argument) {
1352
1624
  const got = value;
1353
1625
  throw sqliteError("CONTRACT.ARGUMENT_INVALID", `${factory}({ table: ${jsonToTsSource(tableName)}, column: ${jsonToTsSource(columnName)} }): ${argument} must be a string or a number; got ${typeof got === "number" || got === void 0 ? String(got) : JSON.stringify(got)}`, { meta: { what: `${factory} ${argument}` } });
1354
1626
  }
1355
- /** Changes one stored value of a column to another, in every row that holds it. */
1627
+ /** Changes one stored value of a column to another, in every row that holds it, or at a path inside the JSON of the column. */
1356
1628
  var ReplaceStoredValueCall = class extends SqliteOpFactoryCallNode {
1357
1629
  factoryName = "replaceStoredValue";
1358
1630
  operationClass = "widening";
@@ -1362,28 +1634,32 @@ var ReplaceStoredValueCall = class extends SqliteOpFactoryCallNode {
1362
1634
  to;
1363
1635
  /** The column's codec, which writes `from` and `to` as the database stores them. */
1364
1636
  codec;
1637
+ /** The path inside the column's JSON at which the value is replaced, compared as a JSON value. */
1638
+ path;
1365
1639
  label;
1366
- constructor(tableName, columnName, from, to, codec) {
1640
+ constructor(tableName, columnName, from, to, codec, path) {
1367
1641
  super();
1368
1642
  this.codec = codec;
1369
1643
  this.tableName = tableName;
1370
1644
  this.columnName = columnName;
1371
1645
  this.from = requireStoredValue(from, this.factoryName, tableName, columnName, "from");
1372
1646
  this.to = requireStoredValue(to, this.factoryName, tableName, columnName, "to");
1373
- this.label = `Replace value ${storedValueText(from)} with ${storedValueText(to)} in column "${columnName}" of "${tableName}"`;
1647
+ this.path = requireValuePath(path, codec, this.factoryName, tableName, columnName);
1648
+ this.label = `Replace value ${storedValueText(from)} with ${storedValueText(to)} ${storedValuePlace(this.path, tableName, columnName)}`;
1374
1649
  this.freeze();
1375
1650
  }
1376
1651
  async toOp() {
1652
+ if (this.path !== void 0) return replaceStoredValueAtPath(this.tableName, this.columnName, this.from, this.to, this.path);
1377
1653
  return replaceStoredValue(this.tableName, this.columnName, await encodeStoredValue(this.from, this.codec), await encodeStoredValue(this.to, this.codec));
1378
1654
  }
1379
1655
  renderTypeScript() {
1380
- return replaceStoredValueSource(this.tableName, this.columnName, jsonToTsSource(this.from), jsonToTsSource(this.to), this.codec);
1656
+ return replaceStoredValueSource(this.tableName, this.columnName, jsonToTsSource(this.from), jsonToTsSource(this.to), this.codec, this.path);
1381
1657
  }
1382
1658
  importRequirements() {
1383
1659
  return [];
1384
1660
  }
1385
1661
  };
1386
- /** Removes one stored value of a column by setting it to NULL in every row that holds it. */
1662
+ /** Removes one stored value of a column by setting it to NULL in every row that holds it, or removes it at a path inside the JSON of the column. */
1387
1663
  var RemoveStoredValueCall = class extends SqliteOpFactoryCallNode {
1388
1664
  factoryName = "removeStoredValue";
1389
1665
  operationClass = "destructive";
@@ -1394,29 +1670,40 @@ var RemoveStoredValueCall = class extends SqliteOpFactoryCallNode {
1394
1670
  codec;
1395
1671
  /** The enum value whose stored value is removed, which the check that fails while rows of a required column hold it names. */
1396
1672
  enumValue;
1673
+ /** The path inside the column's JSON at which the value is removed, compared as a JSON value. */
1674
+ path;
1675
+ /** Whether the member at `path` takes JSON `null`; when it does not, the check fails while a row holds the value. */
1676
+ nullable;
1397
1677
  label;
1398
- constructor(tableName, columnName, value, codec, enumValue) {
1678
+ constructor(tableName, columnName, value, codec, enumValue, path, nullable) {
1399
1679
  super();
1400
1680
  this.codec = codec;
1401
1681
  this.enumValue = enumValue;
1402
1682
  this.tableName = tableName;
1403
1683
  this.columnName = columnName;
1404
1684
  this.value = requireStoredValue(value, this.factoryName, tableName, columnName, "value");
1405
- this.label = `Remove value ${storedValueText(value)} from column "${columnName}" of "${tableName}"`;
1685
+ this.path = requireValuePath(path, codec, this.factoryName, tableName, columnName);
1686
+ if (nullable !== void 0 && this.path === void 0) refusedArgument(this.factoryName, columnCallLocation(tableName, columnName), "nullable", "nullable needs a path: a column says itself whether it takes NULL");
1687
+ this.nullable = nullable === true;
1688
+ this.label = `Remove value ${storedValueText(value)} ${this.path === void 0 ? `from column "${columnName}" of "${tableName}"` : storedValuePlace(this.path, tableName, columnName)}`;
1406
1689
  this.freeze();
1407
1690
  }
1408
1691
  async toOp() {
1409
- return removeStoredValue(this.tableName, this.columnName, await encodeStoredValue(this.value, this.codec), this.enumValue === void 0 ? void 0 : {
1692
+ const mark = this.enumValue === void 0 ? void 0 : {
1410
1693
  enumValue: this.enumValue,
1411
1694
  value: this.value,
1412
1695
  ...this.codec === void 0 ? {} : { codecRef: this.codec.codecRef }
1413
- });
1696
+ };
1697
+ if (this.path !== void 0) return removeStoredValueAtPath(this.tableName, this.columnName, this.value, this.path, this.nullable, mark);
1698
+ return removeStoredValue(this.tableName, this.columnName, await encodeStoredValue(this.value, this.codec), mark);
1414
1699
  }
1415
1700
  renderTypeScript() {
1416
1701
  return `this.removeStoredValue({ ${[
1417
1702
  `table: ${jsonToTsSource(this.tableName)}`,
1418
1703
  `column: ${jsonToTsSource(this.columnName)}`,
1704
+ ...pathSource(this.path),
1419
1705
  `value: ${jsonToTsSource(this.value)}`,
1706
+ ...this.nullable ? ["nullable: true"] : [],
1420
1707
  ...codecRefSource(this.codec),
1421
1708
  ...this.enumValue === void 0 ? [] : [`enumValue: ${jsonToTsSource(this.enumValue)}`]
1422
1709
  ].join(", ")} })`;
@@ -1425,6 +1712,204 @@ var RemoveStoredValueCall = class extends SqliteOpFactoryCallNode {
1425
1712
  return [];
1426
1713
  }
1427
1714
  };
1715
+ /** Deletes the rows whose column holds one stored value. */
1716
+ var DeleteRowsCall = class extends SqliteOpFactoryCallNode {
1717
+ factoryName = "deleteRows";
1718
+ operationClass = "destructive";
1719
+ tableName;
1720
+ columnName;
1721
+ value;
1722
+ /** The column's codec, which writes `value` as the database stores it. */
1723
+ codec;
1724
+ /** The tables whose rows reference the deleted rows through a foreign key that cascades, deleted first. */
1725
+ cascade;
1726
+ label;
1727
+ constructor(tableName, columnName, value, codec, cascade = []) {
1728
+ super();
1729
+ this.codec = codec;
1730
+ this.tableName = tableName;
1731
+ this.columnName = columnName;
1732
+ this.value = requireStoredValue(value, this.factoryName, tableName, columnName, "value");
1733
+ this.cascade = cascade.map((foreignKey) => requireCascade(foreignKey, this.factoryName, columnCallLocation(tableName, columnName)));
1734
+ this.label = deleteRowsLabel(tableName, columnName, value, this.cascade);
1735
+ this.freeze();
1736
+ }
1737
+ async toOp() {
1738
+ return deleteRows(this.tableName, this.columnName, await encodeStoredValue(this.value, this.codec), this.cascade);
1739
+ }
1740
+ renderTypeScript() {
1741
+ return `this.deleteRows({ ${[
1742
+ `table: ${jsonToTsSource(this.tableName)}`,
1743
+ `column: ${jsonToTsSource(this.columnName)}`,
1744
+ `value: ${jsonToTsSource(this.value)}`,
1745
+ ...codecRefSource(this.codec),
1746
+ ...this.cascade.length === 0 ? [] : [`cascade: ${jsonToTsSource(this.cascade)}`]
1747
+ ].join(", ")} })`;
1748
+ }
1749
+ importRequirements() {
1750
+ return [];
1751
+ }
1752
+ };
1753
+ function requireCascade(foreignKey, factory, location) {
1754
+ const { columns, references } = foreignKey;
1755
+ return columns.length > 0 && columns.length === references.length ? {
1756
+ table: foreignKey.table,
1757
+ columns: [...columns],
1758
+ references: [...references]
1759
+ } : refusedArgument(factory, location, "cascade", `cascade of table ${JSON.stringify(foreignKey.table)} must name as many columns as references, and at least one`);
1760
+ }
1761
+ function columnCallLocation(tableName, columnName) {
1762
+ return [`table: ${jsonToTsSource(tableName)}`, `column: ${jsonToTsSource(columnName)}`];
1763
+ }
1764
+ function refusedArgument(factory, location, argument, why) {
1765
+ throw sqliteError("CONTRACT.ARGUMENT_INVALID", `${factory}({ ${location.join(", ")} }): ${why}`, { meta: { what: `${factory} ${argument}` } });
1766
+ }
1767
+ function requirePath(text, factory, location) {
1768
+ const parsed = parseJsonPath(text);
1769
+ return parsed.ok ? parsed.value : refusedArgument(factory, location, "path", parsed.failure);
1770
+ }
1771
+ function requireKeyPath(text, factory, location) {
1772
+ return keyPathOf(requirePath(text, factory, location)) ?? refusedArgument(factory, location, "path", `path ${JSON.stringify(text)} does not end at a member name, the key the operation acts on`);
1773
+ }
1774
+ /** Renames the key at the end of a JSON path, in every object of a column the path reaches. */
1775
+ var RenameStoredKeyCall = class extends SqliteOpFactoryCallNode {
1776
+ factoryName = "renameStoredKey";
1777
+ operationClass = "widening";
1778
+ tableName;
1779
+ columnName;
1780
+ path;
1781
+ to;
1782
+ label;
1783
+ constructor(tableName, columnName, path, to) {
1784
+ super();
1785
+ const location = columnCallLocation(tableName, columnName);
1786
+ this.tableName = tableName;
1787
+ this.columnName = columnName;
1788
+ this.path = requireKeyPath(path, this.factoryName, location);
1789
+ if (typeof to !== "string" || to === this.path.key) refusedArgument(this.factoryName, location, "to", `to must be a key other than ${JSON.stringify(this.path.key)}; got ${JSON.stringify(to)}`);
1790
+ this.to = to;
1791
+ this.label = `Rename key "${this.path.key}" to "${to}" ${keyPlace(this.path, tableName, columnName)}`;
1792
+ this.freeze();
1793
+ }
1794
+ toOp() {
1795
+ return renameStoredKey(this.tableName, this.columnName, this.path, this.to);
1796
+ }
1797
+ renderTypeScript() {
1798
+ return `this.renameStoredKey({ ${[
1799
+ ...columnCallLocation(this.tableName, this.columnName),
1800
+ `path: ${jsonToTsSource(this.path.path.text)}`,
1801
+ `to: ${jsonToTsSource(this.to)}`
1802
+ ].join(", ")} })`;
1803
+ }
1804
+ importRequirements() {
1805
+ return [];
1806
+ }
1807
+ };
1808
+ /** Removes the key at the end of a JSON path, from every object of a column the path reaches. */
1809
+ var RemoveStoredKeyCall = class extends SqliteOpFactoryCallNode {
1810
+ factoryName = "removeStoredKey";
1811
+ operationClass = "destructive";
1812
+ tableName;
1813
+ columnName;
1814
+ path;
1815
+ label;
1816
+ constructor(tableName, columnName, path) {
1817
+ super();
1818
+ this.tableName = tableName;
1819
+ this.columnName = columnName;
1820
+ this.path = requireKeyPath(path, this.factoryName, columnCallLocation(tableName, columnName));
1821
+ this.label = `Remove key "${this.path.key}" ${keyPlace(this.path, tableName, columnName)}`;
1822
+ this.freeze();
1823
+ }
1824
+ toOp() {
1825
+ return removeStoredKey(this.tableName, this.columnName, this.path);
1826
+ }
1827
+ renderTypeScript() {
1828
+ return `this.removeStoredKey({ ${[...columnCallLocation(this.tableName, this.columnName), `path: ${jsonToTsSource(this.path.path.text)}`].join(", ")} })`;
1829
+ }
1830
+ importRequirements() {
1831
+ return [];
1832
+ }
1833
+ };
1834
+ function requireJsonValue(value, factory, location) {
1835
+ return JSON.stringify(value) === void 0 ? refusedArgument(factory, location, "value", "value must be a JSON value; got undefined") : value;
1836
+ }
1837
+ function requireFillValue(value, field, factory, location) {
1838
+ const json = requireJsonValue(value, factory, location);
1839
+ const problem = fillValueProblem(json, field);
1840
+ return problem === void 0 ? json : refusedArgument(factory, location, "value", problem);
1841
+ }
1842
+ /**
1843
+ * The operation of a fill whose value is an unfilled placeholder: a promise that rejects with
1844
+ * `MIGRATION.UNFILLED_PLACEHOLDER`, whose meta names the operation it stands for, so that a plan can
1845
+ * list it before the user fills the value.
1846
+ */
1847
+ function unfilledFillOperation(slot, standsFor) {
1848
+ const unfilled = errorUnfilledPlaceholder(slot);
1849
+ const operation = Promise.reject(new CliStructuredError(unfilled.code, unfilled.message, {
1850
+ ...ifDefined("why", unfilled.why),
1851
+ ...ifDefined("fix", unfilled.fix),
1852
+ meta: {
1853
+ slot,
1854
+ operation: {
1855
+ id: standsFor.id,
1856
+ label: standsFor.label,
1857
+ operationClass: standsFor.operationClass
1858
+ }
1859
+ }
1860
+ }));
1861
+ operation.catch(() => void 0);
1862
+ return operation;
1863
+ }
1864
+ /**
1865
+ * Sets the key at the end of a JSON path to a value, in every object of a column the path reaches that lacks it or holds JSON `null` there. An unfilled value is a placeholder the user fills in `migration.ts`; until then the call lowers to no operation.
1866
+ */
1867
+ var FillStoredKeyCall = class extends SqliteOpFactoryCallNode {
1868
+ factoryName = "fillStoredKey";
1869
+ operationClass = "widening";
1870
+ tableName;
1871
+ columnName;
1872
+ path;
1873
+ value;
1874
+ /** The value object field the key holds, which must take `value`. */
1875
+ field;
1876
+ label;
1877
+ constructor(tableName, columnName, path, value, field) {
1878
+ super();
1879
+ const location = columnCallLocation(tableName, columnName);
1880
+ this.tableName = tableName;
1881
+ this.columnName = columnName;
1882
+ this.path = requireKeyPath(path, this.factoryName, location);
1883
+ const keyProblem = fillKeyProblem({
1884
+ key: this.path.key,
1885
+ path: this.path.path.text
1886
+ }, field);
1887
+ if (keyProblem !== void 0) refusedArgument(this.factoryName, location, "field", keyProblem);
1888
+ this.field = field;
1889
+ this.value = value instanceof UnfilledValue ? value : requireFillValue(value, field, this.factoryName, location);
1890
+ this.label = `Fill key "${this.path.key}" ${keyPlace(this.path, tableName, columnName)}`;
1891
+ this.freeze();
1892
+ }
1893
+ toOp() {
1894
+ if (this.value instanceof UnfilledValue) return unfilledFillOperation(this.value.slot, fillStoredKey(this.tableName, this.columnName, this.path, "null"));
1895
+ return fillStoredKey(this.tableName, this.columnName, this.path, JSON.stringify(this.value));
1896
+ }
1897
+ renderTypeScript() {
1898
+ const value = this.value instanceof UnfilledValue ? `placeholder(${jsonToTsSource(this.value.slot)})` : jsonToTsSource(this.value);
1899
+ return `this.fillStoredKey({ ${[
1900
+ ...columnCallLocation(this.tableName, this.columnName),
1901
+ `path: ${jsonToTsSource(this.path.path.text)}`,
1902
+ `value: ${value}`,
1903
+ ...this.field === void 0 ? [] : [`field: ${jsonToTsSource(this.field.text)}`]
1904
+ ].join(", ")} })`;
1905
+ }
1906
+ importRequirements() {
1907
+ return this.value instanceof UnfilledValue ? [{
1908
+ moduleSpecifier: TARGET_MIGRATION_MODULE,
1909
+ symbol: "placeholder"
1910
+ }] : [];
1911
+ }
1912
+ };
1428
1913
  /**
1429
1914
  * A planner-generated data-transform stub. The current default strategy
1430
1915
  * (`nullabilityTighteningBackfillStrategy`) emits one of these with a
@@ -1551,6 +2036,6 @@ function renderPostcheck(postcheck) {
1551
2036
  }));
1552
2037
  }
1553
2038
  //#endregion
1554
- export { sqliteDefaultToDdlColumnDefault as C, renameTableSteps as S, step as T, buildRecreateSummary as _, DropColumnCall as a, isSqliteOpFactoryCall as b, INLINE_AUTOINCREMENT_PRIMARY_KEY as c, RemoveStoredValueCall as d, RenameColumnCall as f, buildRecreatePostchecks as g, affinityStorageClasses as h, DataTransformCall as i, RawSqlCall as l, ReplaceStoredValueCall as m, CreateIndexCall as n, DropIndexCall as o, RenameTableCall as p, CreateTableCall as r, DropTableCall as s, AddColumnCall as t, RecreateTableCall as u, conversionCopyOf as v, sqliteIdentifiersCollide as w, rebuiltTableName as x, indexReplacementCalls as y };
2039
+ export { step as A, holdingStoredValue as C, renameTableSteps as D, rebuiltTableName as E, sqliteDefaultToDdlColumnDefault as O, conversionCopyOf as S, isSqliteOpFactoryCall as T, RenameTableCall as _, DeleteRowsCall as a, buildRecreatePostchecks as b, DropTableCall as c, RawSqlCall as d, RecreateTableCall as f, RenameStoredKeyCall as g, RenameColumnCall as h, DataTransformCall as i, sqliteIdentifiersCollide as k, FillStoredKeyCall as l, RemoveStoredValueCall as m, CreateIndexCall as n, DropColumnCall as o, RemoveStoredKeyCall as p, CreateTableCall as r, DropIndexCall as s, AddColumnCall as t, INLINE_AUTOINCREMENT_PRIMARY_KEY as u, ReplaceStoredValueCall as v, indexReplacementCalls as w, buildRecreateSummary as x, affinityStorageClasses as y };
1555
2040
 
1556
- //# sourceMappingURL=op-factory-call-BQYECylm-K7S_xgRS.mjs.map
2041
+ //# sourceMappingURL=op-factory-call-Dxsyn5gF-CvHmM-h_.mjs.map