qubu 0.0.0 → 0.3.3

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 (109) hide show
  1. package/dist/canonical-BbnqavJm.mjs +972 -0
  2. package/dist/codegen.d.mts +107 -0
  3. package/dist/codegen.mjs +1199 -0
  4. package/dist/column-CXMxx8Hq.mjs +118 -0
  5. package/dist/complete-D5Djh-zo.mjs +1788 -0
  6. package/dist/complete-types-BdFqUfbb.d.mts +371 -0
  7. package/dist/core.d.mts +33 -0
  8. package/dist/core.mjs +36 -0
  9. package/dist/ddl.d.mts +118 -0
  10. package/dist/ddl.mjs +1120 -0
  11. package/dist/dialect-b2-Z6uBF.mjs +13 -0
  12. package/dist/diff.d.mts +2 -0
  13. package/dist/diff.mjs +883 -0
  14. package/dist/drizzle-mysql.d.mts +24 -0
  15. package/dist/drizzle-mysql.mjs +72 -0
  16. package/dist/drizzle-postgres.d.mts +24 -0
  17. package/dist/drizzle-postgres.mjs +73 -0
  18. package/dist/drizzle-sqlite.d.mts +24 -0
  19. package/dist/drizzle-sqlite.mjs +82 -0
  20. package/dist/drizzle.d.mts +13 -0
  21. package/dist/drizzle.mjs +2 -0
  22. package/dist/errors-BGCoLe_r.mjs +14 -0
  23. package/dist/errors-Dxv73YJu.mjs +26 -0
  24. package/dist/explain-CkIK13L_.mjs +98 -0
  25. package/dist/index-Ds7-mhJi.d.mts +146 -0
  26. package/dist/index-Dug5HnLB.d.mts +204 -0
  27. package/dist/index.d.mts +2 -0
  28. package/dist/index.mjs +1276 -0
  29. package/dist/introspection.d.mts +117 -0
  30. package/dist/introspection.mjs +4341 -0
  31. package/dist/json-CUZlv4HT.mjs +169 -0
  32. package/dist/migration.d.mts +215 -0
  33. package/dist/migration.mjs +1161 -0
  34. package/dist/mysql-DqkqXB6A.mjs +355 -0
  35. package/dist/mysql.d.mts +5 -0
  36. package/dist/mysql.mjs +50 -0
  37. package/dist/naming-QVCOnSj2.mjs +20 -0
  38. package/dist/omit-OxV58AwX.mjs +5 -0
  39. package/dist/on-conflict-BxnxubMb.mjs +143 -0
  40. package/dist/postgres-DEBBeh52.mjs +235 -0
  41. package/dist/postgres.d.mts +123 -0
  42. package/dist/postgres.mjs +52 -0
  43. package/dist/registry-BufIskVN.mjs +569 -0
  44. package/dist/relational-DCZrrNia.mjs +77 -0
  45. package/dist/runtime-BTr-MTlo.mjs +193 -0
  46. package/dist/schema.d.mts +2 -0
  47. package/dist/schema.mjs +5 -0
  48. package/dist/serialize-PF1cfH2P.mjs +556 -0
  49. package/dist/snapshot-CWPgzxNx.mjs +392 -0
  50. package/dist/snapshot.d.mts +4 -0
  51. package/dist/snapshot.mjs +8 -0
  52. package/dist/source-DUoJVXmL.mjs +68 -0
  53. package/dist/sqlite-BU6DBxef.mjs +320 -0
  54. package/dist/sqlite.d.mts +5 -0
  55. package/dist/sqlite.mjs +48 -0
  56. package/dist/standard-BTVYKh_F.mjs +12 -0
  57. package/dist/table-llv9tsZ8.mjs +384 -0
  58. package/dist/types-4Q076HKo.d.mts +3636 -0
  59. package/dist/types-BX0mckiU.d.mts +644 -0
  60. package/dist/types-CO1KaRNc.d.mts +46 -0
  61. package/dist/types-Cec0xzo4.mjs +116 -0
  62. package/dist/value-BvilP0oz.mjs +29 -0
  63. package/dist/vite/ambient.d.ts +449 -0
  64. package/dist/vite.d.mts +35 -0
  65. package/dist/vite.mjs +498 -0
  66. package/docs/config.json +21 -0
  67. package/docs/dialects-and-execution.md +427 -0
  68. package/docs/getting-started.md +96 -0
  69. package/docs/guides/compose-queries.md +225 -0
  70. package/docs/guides/drizzle.md +129 -0
  71. package/docs/guides/extensions/dialects.md +40 -0
  72. package/docs/guides/extensions/overview.md +36 -0
  73. package/docs/guides/extensions/sources-and-clauses.md +82 -0
  74. package/docs/guides/extensions/typed-expressions.md +94 -0
  75. package/docs/guides/extensions/unsafe-syntax.md +27 -0
  76. package/docs/guides/json.md +65 -0
  77. package/docs/guides/mutations.md +138 -0
  78. package/docs/guides/select/conditions.md +110 -0
  79. package/docs/guides/select/grouping-and-windows.md +82 -0
  80. package/docs/guides/select/ordering-and-pagination.md +78 -0
  81. package/docs/guides/select/overview.md +139 -0
  82. package/docs/guides/sql-templates.md +157 -0
  83. package/docs/guides/vite-plugin.md +92 -0
  84. package/docs/index.md +111 -0
  85. package/docs/query-model/fragments.md +109 -0
  86. package/docs/query-model/result-shapes.md +125 -0
  87. package/docs/query-model/source-scope.md +182 -0
  88. package/docs/reference/introspection-support.md +196 -0
  89. package/docs/reference/mysql-snapshot.md +55 -0
  90. package/docs/reference/postgres-snapshot.md +61 -0
  91. package/docs/reference/sqlite-snapshot.md +53 -0
  92. package/docs/reference/supported-surface.md +104 -0
  93. package/docs/schema/catalog-model.md +58 -0
  94. package/docs/schema/code-generation.md +139 -0
  95. package/docs/schema/columns-and-writes.md +130 -0
  96. package/docs/schema/constraints-and-indexes.md +180 -0
  97. package/docs/schema/ddl-emission.md +75 -0
  98. package/docs/schema/diff.md +75 -0
  99. package/docs/schema/introspection.md +214 -0
  100. package/docs/schema/migration-plans.md +76 -0
  101. package/docs/schema/snapshots.md +93 -0
  102. package/docs/schema/storage-and-schema-sql.md +104 -0
  103. package/docs/schema/tables-and-names.md +106 -0
  104. package/docs/sql-semantic-types.md +141 -0
  105. package/docs/troubleshooting.md +151 -0
  106. package/package.json +132 -10
  107. package/skills/qubu/SKILL.md +42 -0
  108. package/skills/qubu/agents/openai.yaml +6 -0
  109. package/readme.md +0 -1
package/dist/diff.mjs ADDED
@@ -0,0 +1,883 @@
1
+ import { a as toSnapshotJsonValue, i as schemaSnapshotDigest, l as decodeSchemaSnapshot, n as canonicalJson } from "./canonical-BbnqavJm.mjs";
2
+ import { c as decodeCompleteSchemaSnapshot, s as completeSchemaSnapshotDigest } from "./complete-D5Djh-zo.mjs";
3
+ //#region src/diff/diff.ts
4
+ const objectKinds = /* @__PURE__ */ new Set([
5
+ "namespace",
6
+ "table",
7
+ "column",
8
+ "constraint",
9
+ "index",
10
+ "view",
11
+ "materialized-view",
12
+ "sequence",
13
+ "enum",
14
+ "domain",
15
+ "collation",
16
+ "trigger",
17
+ "routine",
18
+ "partition",
19
+ "policy",
20
+ "extension",
21
+ "comment",
22
+ "ownership",
23
+ "deferred-object",
24
+ "opaque-object"
25
+ ]);
26
+ const operationOrder = /* @__PURE__ */ new Map([
27
+ ["remove", 0],
28
+ ["physical-rename", 1],
29
+ ["property-change", 2],
30
+ ["add", 3]
31
+ ]);
32
+ /**
33
+ * Compare Snapshot v1 or v2 values and return immutable, reviewable data.
34
+ *
35
+ * The function never opens a connection, renders SQL, or turns a heuristic
36
+ * match into a rename. Invalid input and malformed hints are represented by
37
+ * diagnostics in the returned value.
38
+ */
39
+ function diffSnapshots(beforeInput, afterInput, options = {}) {
40
+ const diagnostics = [];
41
+ const before = decodeSnapshot(beforeInput);
42
+ const after = decodeSnapshot(afterInput);
43
+ diagnostics.push(...before.diagnostics, ...after.diagnostics);
44
+ const hintResult = validateSnapshotRenameHints(options.renameHints ?? options.renames ?? []);
45
+ diagnostics.push(...hintResult.diagnostics);
46
+ const validHints = hintResult.value;
47
+ if (!before.snapshot || !after.snapshot) return freezeDiff({
48
+ equal: false,
49
+ operations: [],
50
+ changes: [],
51
+ additions: [],
52
+ removals: [],
53
+ propertyChanges: [],
54
+ renames: [],
55
+ added: [],
56
+ removed: [],
57
+ changed: [],
58
+ physicalRenames: [],
59
+ suggestions: [],
60
+ diagnostics: sortDiagnostics(diagnostics),
61
+ issues: sortDiagnostics(diagnostics),
62
+ renameHints: validHints,
63
+ hints: validHints,
64
+ ...before.snapshot ? {
65
+ beforeVersion: before.snapshot.version,
66
+ beforeDialect: before.snapshot.value.dialect,
67
+ beforeDigest: before.snapshot.digest
68
+ } : {},
69
+ ...after.snapshot ? {
70
+ afterVersion: after.snapshot.version,
71
+ afterDialect: after.snapshot.value.dialect,
72
+ afterDigest: after.snapshot.digest
73
+ } : {}
74
+ });
75
+ const left = before.snapshot;
76
+ const right = after.snapshot;
77
+ if (left.value.dialect.name !== right.value.dialect.name) diagnostics.push(diffDiagnostic("dialect-mismatch", `Snapshots use different dialects: "${left.value.dialect.name}" and "${right.value.dialect.name}"`, [], { dialect: right.value.dialect }));
78
+ const beforeByKey = groupByKey(left.records);
79
+ const afterByKey = groupByKey(right.records);
80
+ for (const [key, records] of [...beforeByKey, ...afterByKey]) {
81
+ if (records.length < 2) continue;
82
+ diagnostics.push(diffDiagnostic("ambiguous", `Multiple ${records[0].object.kind} records share one logical identity; stable matching is disabled for this key`, records[0].object.path, {
83
+ kind: records[0].object.kind,
84
+ namespace: records[0].object.namespace,
85
+ logicalId: records[0].object.id,
86
+ physicalName: records[0].object.physicalName,
87
+ dialect: records[0].object.dialect,
88
+ relatedPaths: records.slice(1).map((record) => record.object.path),
89
+ evidence: [evidence("ambiguity", void 0, void 0, void 0, `Duplicate stable key ${key}`)]
90
+ }));
91
+ }
92
+ const usedBefore = /* @__PURE__ */ new Set();
93
+ const usedAfter = /* @__PURE__ */ new Set();
94
+ const matches = [];
95
+ for (const hint of validHints) {
96
+ const beforeCandidates = resolveHintTarget(left.records, hint.kind, hint.namespace, hint.from);
97
+ const afterCandidates = resolveHintTarget(right.records, hint.kind, hint.namespace, hint.to);
98
+ if (beforeCandidates.length !== 1 || afterCandidates.length !== 1) {
99
+ const candidates = [...beforeCandidates, ...afterCandidates];
100
+ diagnostics.push(diffDiagnostic(beforeCandidates.length === 0 || afterCandidates.length === 0 ? "invalid-rename-hint" : "ambiguous", `Rename hint for ${hint.kind} did not resolve to one object on each side`, [], {
101
+ kind: hint.kind,
102
+ namespace: hint.namespace,
103
+ relatedPaths: candidates.map((candidate) => candidate.object.path),
104
+ evidence: [evidence("explicit-hint", void 0, void 0, 1, "Hint target")]
105
+ }));
106
+ continue;
107
+ }
108
+ const beforeObject = beforeCandidates[0];
109
+ const afterObject = afterCandidates[0];
110
+ if (usedBefore.has(beforeObject) || usedAfter.has(afterObject)) {
111
+ diagnostics.push(diffDiagnostic("rename-conflict", `Rename hint for ${hint.kind} reuses an object already mapped by another hint`, beforeObject.object.path, {
112
+ kind: hint.kind,
113
+ namespace: hint.namespace,
114
+ relatedPaths: [afterObject.object.path]
115
+ }));
116
+ continue;
117
+ }
118
+ usedBefore.add(beforeObject);
119
+ usedAfter.add(afterObject);
120
+ matches.push({
121
+ before: beforeObject,
122
+ after: afterObject,
123
+ source: "explicit-hint",
124
+ evidence: [evidence("explicit-hint", void 0, toSnapshotJsonValue(hint), 1, "Explicit rename hint")]
125
+ });
126
+ }
127
+ for (const [key, beforeCandidates] of beforeByKey) {
128
+ const afterCandidates = afterByKey.get(key) ?? [];
129
+ if (beforeCandidates.length !== 1 || afterCandidates.length !== 1) continue;
130
+ const beforeObject = beforeCandidates[0];
131
+ const afterObject = afterCandidates[0];
132
+ if (usedBefore.has(beforeObject) || usedAfter.has(afterObject)) continue;
133
+ usedBefore.add(beforeObject);
134
+ usedAfter.add(afterObject);
135
+ matches.push({
136
+ before: beforeObject,
137
+ after: afterObject,
138
+ source: "stable-id",
139
+ evidence: [evidence("logical-id", [...beforeObject.object.path, "id"], beforeObject.object.id, 1, "Stable logical ID match")]
140
+ });
141
+ }
142
+ const suggestions = [];
143
+ const suggestionThreshold = clampThreshold(options.suggestionThreshold);
144
+ const unmatchedBefore = left.records.filter((record) => !usedBefore.has(record));
145
+ const unmatchedAfter = right.records.filter((record) => !usedAfter.has(record));
146
+ if (options.suggestions !== false) for (const beforeObject of unmatchedBefore) {
147
+ if (beforeObject.lossy || beforeObject.unsupported) continue;
148
+ const candidates = unmatchedAfter.filter((candidate) => !candidate.lossy && !candidate.unsupported && candidate.scopeKey === beforeObject.scopeKey).map((candidate) => ({
149
+ candidate,
150
+ score: structuralScore(beforeObject, candidate)
151
+ })).filter((candidate) => candidate.score >= suggestionThreshold).sort((leftCandidate, rightCandidate) => rightCandidate.score - leftCandidate.score || compareObject(leftCandidate.candidate, rightCandidate.candidate));
152
+ if (candidates.length === 0) continue;
153
+ const best = candidates[0];
154
+ const tied = candidates.filter((candidate) => Math.abs(candidate.score - best.score) < 1e-6);
155
+ if (tied.length > 1) {
156
+ diagnostics.push(diffDiagnostic("ambiguous", `Multiple structural matches are possible for ${beforeObject.object.kind} "${beforeObject.object.id}"; no rename was inferred`, beforeObject.object.path, {
157
+ kind: beforeObject.object.kind,
158
+ namespace: beforeObject.object.namespace,
159
+ logicalId: beforeObject.object.id,
160
+ physicalName: beforeObject.object.physicalName,
161
+ dialect: beforeObject.object.dialect,
162
+ relatedPaths: tied.map((item) => item.candidate.object.path),
163
+ evidence: [evidence("ambiguity", void 0, void 0, best.score, `${tied.length} candidates scored ${best.score.toFixed(3)}`)]
164
+ }));
165
+ continue;
166
+ }
167
+ suggestions.push({
168
+ type: "rename-suggestion",
169
+ operation: "rename-suggestion",
170
+ kind: beforeObject.object.kind,
171
+ objectKind: beforeObject.object.kind,
172
+ namespace: beforeObject.object.namespace,
173
+ before: beforeObject.object,
174
+ after: best.candidate.object,
175
+ confidence: best.score,
176
+ evidence: [evidence("structural", void 0, void 0, best.score, "Stable structure matched after removing identity evidence")]
177
+ });
178
+ }
179
+ const operations = [];
180
+ for (const match of matches) {
181
+ const operation = operationForMatch(match, diagnostics);
182
+ if (operation !== void 0) operations.push(operation);
183
+ }
184
+ for (const record of unmatchedBefore) {
185
+ const operation = operationForUnmatched("remove", record.object);
186
+ operations.push(operation);
187
+ diagnostics.push(diffDiagnostic("destructive", `Removing ${record.object.kind} "${record.object.id}" is destructive`, record.object.path, {
188
+ kind: record.object.kind,
189
+ namespace: record.object.namespace,
190
+ logicalId: record.object.id,
191
+ physicalName: record.object.physicalName,
192
+ dialect: record.object.dialect,
193
+ evidence: record.object.evidence
194
+ }));
195
+ }
196
+ for (const record of unmatchedAfter) operations.push(operationForUnmatched("add", record.object));
197
+ for (const record of [...left.records, ...right.records]) {
198
+ if (!record.lossy && !record.unsupported) continue;
199
+ diagnostics.push(diffDiagnostic(record.lossy ? "lossy" : "unsupported", record.lossy ? `${record.object.kind} "${record.object.id}" contains opaque or deferred facts` : `${record.object.kind} "${record.object.id}" is outside the supported comparison surface`, record.object.path, {
200
+ kind: record.object.kind,
201
+ namespace: record.object.namespace,
202
+ logicalId: record.object.id,
203
+ physicalName: record.object.physicalName,
204
+ dialect: record.object.dialect,
205
+ evidence: record.object.evidence
206
+ }));
207
+ if (record.lossy && record.unsupported) diagnostics.push(diffDiagnostic("unsupported", `${record.object.kind} "${record.object.id}" cannot be structurally compared yet`, record.object.path, {
208
+ kind: record.object.kind,
209
+ namespace: record.object.namespace,
210
+ logicalId: record.object.id,
211
+ physicalName: record.object.physicalName,
212
+ dialect: record.object.dialect,
213
+ evidence: record.object.evidence
214
+ }));
215
+ if (record.object.kind === "deferred-object") diagnostics.push(diffDiagnostic("unknown", `${record.object.kind} "${record.object.id}" retains an observed object without a complete model`, record.object.path, {
216
+ kind: record.object.kind,
217
+ namespace: record.object.namespace,
218
+ logicalId: record.object.id,
219
+ physicalName: record.object.physicalName,
220
+ dialect: record.object.dialect,
221
+ evidence: record.object.evidence
222
+ }));
223
+ }
224
+ const sortedOperations = operations.sort(compareOperation);
225
+ const frozenSuggestions = suggestions.sort(compareSuggestion);
226
+ const additions = sortedOperations.filter((operation) => operation.type === "add");
227
+ const removals = sortedOperations.filter((operation) => operation.type === "remove");
228
+ const propertyChanges = sortedOperations.filter((operation) => operation.type === "property-change");
229
+ const renames = sortedOperations.filter((operation) => operation.type === "physical-rename");
230
+ const frozenDiagnostics = sortDiagnostics(diagnostics);
231
+ return freezeDiff({
232
+ equal: sortedOperations.length === 0,
233
+ beforeVersion: left.version,
234
+ afterVersion: right.version,
235
+ beforeDialect: left.value.dialect,
236
+ afterDialect: right.value.dialect,
237
+ beforeDigest: left.digest,
238
+ afterDigest: right.digest,
239
+ operations: sortedOperations,
240
+ changes: sortedOperations,
241
+ additions,
242
+ removals,
243
+ propertyChanges,
244
+ renames,
245
+ added: additions,
246
+ removed: removals,
247
+ changed: sortedOperations,
248
+ physicalRenames: renames,
249
+ suggestions: frozenSuggestions,
250
+ diagnostics: frozenDiagnostics,
251
+ issues: frozenDiagnostics,
252
+ renameHints: validHints,
253
+ hints: validHints
254
+ });
255
+ }
256
+ /** Alias that reads naturally in comparison-oriented callers. */
257
+ const compareSnapshots = diffSnapshots;
258
+ /** Decode and normalize a Snapshot v1 or v2 value without throwing. */
259
+ function decodeSnapshotForDiff(input) {
260
+ const decoded = decodeSnapshot(input);
261
+ if (!decoded.snapshot) return {
262
+ ok: false,
263
+ diagnostics: decoded.diagnostics
264
+ };
265
+ return {
266
+ ok: true,
267
+ version: decoded.snapshot.version,
268
+ value: decoded.snapshot.value
269
+ };
270
+ }
271
+ /** Validate rename hints and return a frozen, serializable list. */
272
+ function validateSnapshotRenameHints(hints) {
273
+ const diagnostics = [];
274
+ const values = [];
275
+ if (!Array.isArray(hints)) {
276
+ diagnostics.push(diffDiagnostic("invalid-rename-hint", "Rename hints must be an array", ["renameHints"]));
277
+ return {
278
+ ok: false,
279
+ value: [],
280
+ diagnostics: freeze(diagnostics)
281
+ };
282
+ }
283
+ const seen = /* @__PURE__ */ new Set();
284
+ for (const [index, value] of hints.entries()) {
285
+ const path = ["renameHints", index];
286
+ const diagnosticCount = diagnostics.length;
287
+ if (!isRecord(value)) {
288
+ diagnostics.push(diffDiagnostic("invalid-rename-hint", "Rename hint must be an object", path));
289
+ continue;
290
+ }
291
+ for (const key of Object.keys(value)) if (key !== "kind" && key !== "namespace" && key !== "from" && key !== "to") diagnostics.push(diffDiagnostic("invalid-rename-hint", `Unknown rename hint field "${key}"`, [...path, key]));
292
+ const kind = value.kind;
293
+ const namespace = value.namespace;
294
+ if (!isObjectKind(kind)) diagnostics.push(diffDiagnostic("invalid-rename-hint", "Rename hint kind is not a supported snapshot object kind", [...path, "kind"]));
295
+ if (namespace !== void 0 && (typeof namespace !== "string" || namespace.length === 0)) diagnostics.push(diffDiagnostic("invalid-rename-hint", "Rename hint namespace must be a non-empty string when provided", [...path, "namespace"]));
296
+ const from = normalizeRenameTarget(value.from, [...path, "from"], diagnostics);
297
+ const to = normalizeRenameTarget(value.to, [...path, "to"], diagnostics);
298
+ if (!isObjectKind(kind) || typeof namespace !== "string" && namespace !== void 0 || namespace === "" || from === void 0 || to === void 0 || diagnostics.length !== diagnosticCount) continue;
299
+ const hint = freeze({
300
+ kind,
301
+ ...typeof namespace === "string" ? { namespace } : {},
302
+ from: typeof value.from === "string" ? value.from : from,
303
+ to: typeof value.to === "string" ? value.to : to
304
+ });
305
+ const key = `${kind}\u0000${namespace ?? ""}\u0000${targetKey(from)}\u0000${targetKey(to)}`;
306
+ if (seen.has(key)) {
307
+ diagnostics.push(diffDiagnostic("rename-conflict", "Duplicate rename hint", path, {
308
+ kind,
309
+ namespace: typeof namespace === "string" ? namespace : void 0
310
+ }));
311
+ continue;
312
+ }
313
+ seen.add(key);
314
+ values.push(hint);
315
+ }
316
+ const frozenValues = freeze(values.sort(compareHint));
317
+ if (diagnostics.length > 0) return {
318
+ ok: false,
319
+ value: frozenValues,
320
+ diagnostics: freeze(sortDiagnostics(diagnostics))
321
+ };
322
+ return {
323
+ ok: true,
324
+ value: frozenValues,
325
+ diagnostics: freeze([])
326
+ };
327
+ }
328
+ /** Serialize validated rename hints as deterministic JSON. */
329
+ function encodeSnapshotRenameHints(hints) {
330
+ const result = validateSnapshotRenameHints(hints);
331
+ if (!result.ok) throw new TypeError(result.diagnostics.map((item) => item.message).join("\n"));
332
+ return canonicalJson(toSnapshotJsonValue(result.value));
333
+ }
334
+ /** Decode deterministic rename-hint JSON without throwing. */
335
+ function decodeSnapshotRenameHints(input) {
336
+ let value = input;
337
+ if (typeof input === "string") try {
338
+ value = JSON.parse(input);
339
+ } catch (error) {
340
+ return {
341
+ ok: false,
342
+ value: [],
343
+ diagnostics: freeze([diffDiagnostic("invalid-rename-hint", `Rename hint JSON could not be parsed: ${error instanceof Error ? error.message : String(error)}`, [])])
344
+ };
345
+ }
346
+ return validateSnapshotRenameHints(value);
347
+ }
348
+ /** Short aliases for callers that keep rename helpers beside `diffSnapshots`. */
349
+ const validateRenameHints = validateSnapshotRenameHints;
350
+ const encodeRenameHints = encodeSnapshotRenameHints;
351
+ const decodeRenameHints = decodeSnapshotRenameHints;
352
+ function decodeSnapshot(input) {
353
+ const diagnostics = [];
354
+ let value = input;
355
+ if (typeof input === "string") try {
356
+ value = JSON.parse(input);
357
+ } catch (error) {
358
+ diagnostics.push(diffDiagnostic("invalid-snapshot", `Snapshot JSON could not be parsed: ${error instanceof Error ? error.message : String(error)}`, []));
359
+ return { diagnostics };
360
+ }
361
+ if (!isRecord(value)) {
362
+ diagnostics.push(diffDiagnostic("invalid-snapshot", "Snapshot must be an object", []));
363
+ return { diagnostics };
364
+ }
365
+ const version = value.version;
366
+ const normalized = sortSnapshotArrays(value, version === 2 ? 2 : 1);
367
+ if (version === 2) {
368
+ const result = decodeCompleteSchemaSnapshot(normalized);
369
+ if (!result.ok) {
370
+ diagnostics.push(...mapSnapshotDiagnostics(result.diagnostics));
371
+ return { diagnostics };
372
+ }
373
+ const snapshot = result.value;
374
+ return {
375
+ diagnostics,
376
+ snapshot: {
377
+ version: 2,
378
+ value: snapshot,
379
+ records: extractCompleteObjects(snapshot),
380
+ digest: completeSchemaSnapshotDigest(snapshot)
381
+ }
382
+ };
383
+ }
384
+ if (version === 1) {
385
+ const result = decodeSchemaSnapshot(normalized);
386
+ if (!result.ok) {
387
+ diagnostics.push(...mapSnapshotDiagnostics(result.diagnostics));
388
+ return { diagnostics };
389
+ }
390
+ const snapshot = result.value;
391
+ return {
392
+ diagnostics,
393
+ snapshot: {
394
+ version: 1,
395
+ value: snapshot,
396
+ records: extractV1Objects(snapshot),
397
+ digest: schemaSnapshotDigest(snapshot)
398
+ }
399
+ };
400
+ }
401
+ diagnostics.push(diffDiagnostic("invalid-snapshot", `Unsupported schema snapshot version: ${String(version)}`, ["version"]));
402
+ return { diagnostics };
403
+ }
404
+ function extractV1Objects(snapshot) {
405
+ const records = [];
406
+ const namespace = snapshot.namespace;
407
+ for (const [tableIndex, table] of snapshot.tables.entries()) {
408
+ const tableObject = addInternalObject(records, "table", table, ["tables", tableIndex], namespace, void 0, snapshot);
409
+ for (const [columnIndex, column] of table.columns.entries()) addInternalObject(records, "column", column, [
410
+ "tables",
411
+ tableIndex,
412
+ "columns",
413
+ columnIndex
414
+ ], namespace, tableObject, snapshot);
415
+ for (const [constraintIndex, constraint] of table.constraints.entries()) addInternalObject(records, "constraint", constraint, [
416
+ "tables",
417
+ tableIndex,
418
+ "constraints",
419
+ constraintIndex
420
+ ], namespace, tableObject, snapshot);
421
+ for (const [indexIndex, index] of table.indexes.entries()) addInternalObject(records, "index", index, [
422
+ "tables",
423
+ tableIndex,
424
+ "indexes",
425
+ indexIndex
426
+ ], namespace, tableObject, snapshot);
427
+ }
428
+ return freeze(records.sort(compareObject));
429
+ }
430
+ function extractCompleteObjects(snapshot) {
431
+ const records = [];
432
+ const namespace = snapshot.namespace.name;
433
+ addInternalObject(records, "namespace", snapshot.namespace, ["namespace"], namespace, void 0, snapshot);
434
+ for (const [tableIndex, table] of snapshot.tables.entries()) {
435
+ const tableObject = addInternalObject(records, "table", table, ["tables", tableIndex], namespace, void 0, snapshot);
436
+ for (const [columnIndex, column] of table.columns.entries()) addInternalObject(records, "column", column, [
437
+ "tables",
438
+ tableIndex,
439
+ "columns",
440
+ columnIndex
441
+ ], namespace, tableObject, snapshot);
442
+ for (const [constraintIndex, constraint] of table.constraints.entries()) addInternalObject(records, "constraint", constraint, [
443
+ "tables",
444
+ tableIndex,
445
+ "constraints",
446
+ constraintIndex
447
+ ], namespace, tableObject, snapshot);
448
+ for (const [indexIndex, index] of table.indexes.entries()) addInternalObject(records, "index", index, [
449
+ "tables",
450
+ tableIndex,
451
+ "indexes",
452
+ indexIndex
453
+ ], namespace, tableObject, snapshot);
454
+ }
455
+ for (const [group, defaultKind] of [
456
+ ["views", "view"],
457
+ ["sequences", "sequence"],
458
+ ["enums", "enum"],
459
+ ["domains", "domain"],
460
+ ["collations", "collation"],
461
+ ["triggers", "trigger"],
462
+ ["routines", "routine"],
463
+ ["partitions", "partition"],
464
+ ["policies", "policy"],
465
+ ["extensions", "extension"],
466
+ ["deferredObjects", "deferred-object"],
467
+ ["opaqueObjects", "opaque-object"],
468
+ ["comments", "comment"],
469
+ ["ownership", "ownership"]
470
+ ]) {
471
+ const values = snapshot[group];
472
+ if (!Array.isArray(values)) continue;
473
+ for (const [index, value] of values.entries()) {
474
+ const kind = defaultKind === "view" && value.kind === "materialized-view" ? "materialized-view" : defaultKind;
475
+ const object = addInternalObject(records, kind, value, [String(group), index], namespace, void 0, snapshot);
476
+ if (kind === "view" || kind === "materialized-view") {
477
+ const columns = value.columns;
478
+ for (const [columnIndex, column] of columns.entries()) addInternalObject(records, "column", column, [
479
+ String(group),
480
+ index,
481
+ "columns",
482
+ columnIndex
483
+ ], namespace, object, snapshot);
484
+ }
485
+ }
486
+ }
487
+ return freeze(records.sort(compareObject));
488
+ }
489
+ function addInternalObject(records, kind, value, path, namespace, parent, snapshot) {
490
+ const record = value;
491
+ const id = typeof record.id === "string" ? record.id : kind === "namespace" && typeof record.name === "string" ? record.name : typeof record.physicalName === "string" ? record.physicalName : `${kind}:${path.join(".")}`;
492
+ const physicalName = typeof record.physicalName === "string" ? record.physicalName : kind === "namespace" && typeof record.name === "string" ? record.name : void 0;
493
+ const physicalReference = record.physicalReference;
494
+ const provenance = record.provenance;
495
+ const observedKind = kind === "deferred-object" || kind === "opaque-object" ? typeof record.objectKind === "string" ? record.objectKind : void 0 : void 0;
496
+ const dialect = snapshot.dialect;
497
+ const internal = {
498
+ object: freeze({
499
+ kind,
500
+ ...observedKind === void 0 ? {} : { observedKind },
501
+ ...namespace === void 0 ? {} : { namespace },
502
+ path: freeze([...path]),
503
+ ...parent === void 0 ? {} : { parent: freeze({
504
+ kind: parent.object.kind,
505
+ id: parent.object.id,
506
+ ...namespace === void 0 ? {} : { namespace }
507
+ }) },
508
+ id,
509
+ ...physicalName === void 0 ? {} : { physicalName },
510
+ ...physicalReference === void 0 ? {} : { physicalReference },
511
+ dialect,
512
+ ...provenance === void 0 ? {} : { provenance },
513
+ value: record,
514
+ evidence: makeEvidence(record, path, id, physicalName, dialect)
515
+ }),
516
+ key: objectKey(kind, namespace, parent, id),
517
+ scopeKey: `${kind}\u0000${namespace ?? ""}\u0000${parent?.object.id ?? ""}`,
518
+ signature: structuralSignature(record),
519
+ lossy: kind === "opaque-object" || kind === "deferred-object",
520
+ unsupported: kind === "opaque-object" || kind === "deferred-object"
521
+ };
522
+ records.push(internal);
523
+ return internal;
524
+ }
525
+ function operationForMatch(match, diagnostics) {
526
+ const before = match.before.object;
527
+ const after = match.after.object;
528
+ const changedProperties = propertyChangesBetween(before.value, after.value);
529
+ const physicalRename = before.physicalName !== void 0 && after.physicalName !== void 0 && before.physicalName !== after.physicalName;
530
+ const blockedRename = match.before.lossy || match.after.lossy;
531
+ const effectiveChanges = physicalRename && blockedRename && changedProperties.length === 0 ? freeze([{
532
+ path: freeze(["physicalName"]),
533
+ before: before.physicalName,
534
+ after: after.physicalName
535
+ }]) : changedProperties;
536
+ if (physicalRename && blockedRename) diagnostics.push(diffDiagnostic("lossy", `Physical-name change for ${before.kind} "${before.id}" is not promoted to a rename because the record is opaque or deferred`, before.path, {
537
+ kind: before.kind,
538
+ namespace: before.namespace,
539
+ logicalId: before.id,
540
+ physicalName: before.physicalName,
541
+ dialect: after.dialect,
542
+ relatedPaths: [after.path],
543
+ evidence: [...before.evidence, ...after.evidence]
544
+ }));
545
+ if (physicalRename && !blockedRename) return freeze({
546
+ type: "physical-rename",
547
+ operation: "physical-rename",
548
+ classification: "physical-rename",
549
+ changeKind: "physical-rename",
550
+ kind: before.kind,
551
+ objectKind: before.kind,
552
+ namespace: before.namespace ?? after.namespace,
553
+ path: after.path,
554
+ dialect: after.dialect,
555
+ ...after.physicalReference === void 0 ? {} : { physicalReference: after.physicalReference },
556
+ ...after.provenance === void 0 ? {} : { provenance: after.provenance },
557
+ before,
558
+ after,
559
+ object: after,
560
+ logicalId: after.id,
561
+ physicalName: after.physicalName,
562
+ ...changedProperties.length === 0 ? {} : { changedProperties },
563
+ evidence: freeze([
564
+ ...match.evidence,
565
+ evidence("physical-name", [...before.path, "physicalName"], before.physicalName, 1, "Physical name changed while the logical match remained stable"),
566
+ evidence("physical-name", [...after.path, "physicalName"], after.physicalName, 1, "New physical name")
567
+ ]),
568
+ source: match.source,
569
+ destructive: false
570
+ });
571
+ if (effectiveChanges.length === 0) return void 0;
572
+ const destructive = effectiveChanges.some(isDestructiveProperty);
573
+ if (destructive) diagnostics.push(diffDiagnostic("destructive", `Property changes on ${before.kind} "${before.id}" may remove or narrow existing data`, before.path, {
574
+ kind: before.kind,
575
+ namespace: before.namespace,
576
+ logicalId: before.id,
577
+ physicalName: before.physicalName,
578
+ dialect: after.dialect,
579
+ relatedPaths: [after.path],
580
+ evidence: match.evidence
581
+ }));
582
+ return freeze({
583
+ type: "property-change",
584
+ operation: "property-change",
585
+ classification: "property-change",
586
+ changeKind: "property-change",
587
+ kind: before.kind,
588
+ objectKind: before.kind,
589
+ namespace: before.namespace ?? after.namespace,
590
+ path: after.path,
591
+ dialect: after.dialect,
592
+ ...after.physicalReference === void 0 ? {} : { physicalReference: after.physicalReference },
593
+ ...after.provenance === void 0 ? {} : { provenance: after.provenance },
594
+ before,
595
+ after,
596
+ object: after,
597
+ logicalId: after.id,
598
+ physicalName: after.physicalName,
599
+ changedProperties: effectiveChanges,
600
+ evidence: match.evidence,
601
+ source: match.source,
602
+ destructive
603
+ });
604
+ }
605
+ function operationForUnmatched(type, object) {
606
+ return freeze({
607
+ type,
608
+ operation: type,
609
+ classification: type,
610
+ changeKind: type,
611
+ kind: object.kind,
612
+ objectKind: object.kind,
613
+ namespace: object.namespace,
614
+ path: object.path,
615
+ dialect: object.dialect,
616
+ ...object.physicalReference === void 0 ? {} : { physicalReference: object.physicalReference },
617
+ ...object.provenance === void 0 ? {} : { provenance: object.provenance },
618
+ ...type === "remove" ? { before: object } : { after: object },
619
+ object,
620
+ logicalId: object.id,
621
+ physicalName: object.physicalName,
622
+ evidence: object.evidence,
623
+ source: "stable-id",
624
+ destructive: type === "remove"
625
+ });
626
+ }
627
+ function resolveHintTarget(records, kind, namespace, target) {
628
+ const scalarTarget = typeof target === "string" ? target : void 0;
629
+ const normalized = typeof target === "string" ? void 0 : target;
630
+ return records.filter((record) => {
631
+ if (record.object.kind !== kind || namespace !== void 0 && record.object.namespace !== namespace) return false;
632
+ if (scalarTarget !== void 0 && record.object.id !== scalarTarget && record.object.physicalName !== scalarTarget) return false;
633
+ if (normalized === void 0) return true;
634
+ if (normalized.path !== void 0 && !samePath(record.object.path, normalized.path)) return false;
635
+ if (normalized.id !== void 0 && record.object.id !== normalized.id) return false;
636
+ if (normalized.physicalName !== void 0 && record.object.physicalName !== normalized.physicalName) return false;
637
+ return true;
638
+ });
639
+ }
640
+ function structuralScore(left, right) {
641
+ if (left.signature === right.signature) return 1;
642
+ const leftRecord = stripIdentity(left.object.value);
643
+ const rightRecord = stripIdentity(right.object.value);
644
+ if (!isSnapshotRecord(leftRecord) || !isSnapshotRecord(rightRecord)) return 0;
645
+ const leftKeys = new Set(Object.keys(leftRecord));
646
+ const rightKeys = new Set(Object.keys(rightRecord));
647
+ const keys = /* @__PURE__ */ new Set([...leftKeys, ...rightKeys]);
648
+ if (keys.size === 0) return 0;
649
+ let equal = 0;
650
+ for (const key of keys) if (key in leftRecord && key in rightRecord && canonicalEquivalent(leftRecord[key], rightRecord[key])) equal += 1;
651
+ return equal / keys.size;
652
+ }
653
+ function structuralSignature(value) {
654
+ return canonicalJson(stripIdentity(value));
655
+ }
656
+ function stripIdentity(value) {
657
+ if (Array.isArray(value)) return value.map((item) => stripIdentity(item));
658
+ if (value === null || typeof value !== "object") return value;
659
+ const result = {};
660
+ for (const [key, child] of Object.entries(value)) {
661
+ if (key === "id" || key === "physicalName" || key === "physicalReference" || key === "provenance") continue;
662
+ result[key] = stripIdentity(child);
663
+ }
664
+ return result;
665
+ }
666
+ function propertyChangesBetween(before, after) {
667
+ const changes = [];
668
+ compareValues(before, after, [], changes);
669
+ return freeze(changes);
670
+ }
671
+ function compareValues(before, after, path, changes) {
672
+ const key = path[path.length - 1];
673
+ if (key === "id" || key === "physicalName" || key === "physicalReference") return;
674
+ if (path.length === 1 && (key === "columns" || key === "constraints" || key === "indexes")) return;
675
+ if (canonicalEquivalent(before, after)) return;
676
+ if (isSnapshotRecord(before) && isSnapshotRecord(after)) {
677
+ const keys = /* @__PURE__ */ new Set([...Object.keys(before), ...Object.keys(after)]);
678
+ for (const childKey of [...keys].sort()) compareValues(before[childKey], after[childKey], [...path, childKey], changes);
679
+ return;
680
+ }
681
+ if (Array.isArray(before) && Array.isArray(after)) {
682
+ const length = Math.max(before.length, after.length);
683
+ for (let index = 0; index < length; index += 1) compareValues(before[index], after[index], [...path, index], changes);
684
+ return;
685
+ }
686
+ changes.push({
687
+ path: freeze([...path]),
688
+ ...before === void 0 ? {} : { before },
689
+ ...after === void 0 ? {} : { after }
690
+ });
691
+ }
692
+ function isDestructiveProperty(change) {
693
+ if (change.after === void 0) return true;
694
+ const key = change.path[change.path.length - 1];
695
+ if (key === "nullable") return change.after === false;
696
+ if (key === "storage" || key === "default" || key === "generatedColumn") return true;
697
+ if (key === "columns" || key === "terms" || key === "constraints") return true;
698
+ return false;
699
+ }
700
+ function makeEvidence(record, path, id, physicalName, dialect) {
701
+ const result = [evidence("logical-id", [...path, "id"], id, 1, "Stable logical ID"), evidence("dialect", void 0, toSnapshotJsonValue(dialect), 1, "Snapshot dialect")];
702
+ if (physicalName !== void 0) result.push(evidence("physical-name", [...path, "physicalName"], physicalName, 1, "Physical name evidence"));
703
+ if (record.physicalReference !== void 0) result.push(evidence("physical-reference", [...path, "physicalReference"], record.physicalReference, 1, "Physical reference evidence"));
704
+ if (record.provenance !== void 0) result.push(evidence("provenance", [...path, "provenance"], record.provenance, 1, "Catalog or decompiler provenance"));
705
+ return freeze(result);
706
+ }
707
+ function evidence(kind, path, value, confidence, message) {
708
+ return freeze({
709
+ kind,
710
+ ...path === void 0 ? {} : { path: freeze([...path]) },
711
+ ...value === void 0 ? {} : { value },
712
+ ...confidence === void 0 ? {} : { confidence },
713
+ ...message === void 0 ? {} : { message }
714
+ });
715
+ }
716
+ function diffDiagnostic(code, message, path, options = {}) {
717
+ return {
718
+ code,
719
+ category: code,
720
+ severity: code === "ambiguous" || code === "invalid-snapshot" || code === "invalid-rename-hint" || code === "rename-conflict" || code === "dialect-mismatch" ? "error" : "warning",
721
+ message,
722
+ path: freeze([...path]),
723
+ ...options.relatedPaths === void 0 ? {} : { relatedPaths: freeze(options.relatedPaths.map((item) => freeze([...item]))) },
724
+ ...options.kind === void 0 ? {} : {
725
+ kind: options.kind,
726
+ objectKind: options.kind
727
+ },
728
+ ...options.namespace === void 0 ? {} : { namespace: options.namespace },
729
+ ...options.logicalId === void 0 ? {} : { logicalId: options.logicalId },
730
+ ...options.physicalName === void 0 ? {} : { physicalName: options.physicalName },
731
+ ...options.dialect === void 0 ? {} : { dialect: options.dialect },
732
+ ...options.evidence === void 0 ? {} : { evidence: freeze([...options.evidence]) }
733
+ };
734
+ }
735
+ function mapSnapshotDiagnostics(diagnostics) {
736
+ return diagnostics.map((item) => {
737
+ return diffDiagnostic(item.code === "unknown-field" ? "unknown" : item.code === "future-version" || item.code === "unsupported-expression" || item.code === "unsupported-dialect-option" ? "unsupported" : "invalid-snapshot", item.message, item.path, { relatedPaths: item.relatedPaths });
738
+ });
739
+ }
740
+ function groupByKey(records) {
741
+ const groups = /* @__PURE__ */ new Map();
742
+ for (const record of records) {
743
+ const values = groups.get(record.key);
744
+ if (values === void 0) groups.set(record.key, [record]);
745
+ else values.push(record);
746
+ }
747
+ return groups;
748
+ }
749
+ function objectKey(kind, namespace, parent, id) {
750
+ return `${kind}\u0000${namespace ?? ""}\u0000${parent?.object.kind ?? ""}\u0000${parent?.object.id ?? ""}\u0000${id}`;
751
+ }
752
+ function sortSnapshotArrays(value, version) {
753
+ const visit = (current, key) => {
754
+ if (Array.isArray(current)) {
755
+ const values = current.map((item) => visit(item, key));
756
+ if (key !== void 0 && isObjectArrayKey(key, version) && values.every(isRecord)) return values.sort(compareUnknownRecords);
757
+ if (key === "dependencies" || key === "roles" || key === "events" || key === "includedColumns") return values.sort(compareUnknownRecords);
758
+ if (key === "terms" || key === "values" || key === "parameters") return values.sort(comparePositionedRecords);
759
+ return values;
760
+ }
761
+ if (!isRecord(current)) return current;
762
+ const output = {};
763
+ for (const [childKey, child] of Object.entries(current)) output[childKey] = visit(child, childKey);
764
+ return output;
765
+ };
766
+ return visit(value, void 0);
767
+ }
768
+ function isObjectArrayKey(key, version) {
769
+ if (version === 1) return key === "tables" || key === "columns" || key === "constraints" || key === "indexes";
770
+ return key === "tables" || key === "views" || key === "sequences" || key === "enums" || key === "domains" || key === "collations" || key === "triggers" || key === "routines" || key === "partitions" || key === "policies" || key === "extensions" || key === "deferredObjects" || key === "opaqueObjects" || key === "comments" || key === "ownership" || key === "columns" || key === "constraints" || key === "indexes";
771
+ }
772
+ function compareUnknownRecords(left, right) {
773
+ const leftRecord = isRecord(left) ? left : void 0;
774
+ const rightRecord = isRecord(right) ? right : void 0;
775
+ const leftId = leftRecord && typeof leftRecord.id === "string" ? leftRecord.id : "";
776
+ const rightId = rightRecord && typeof rightRecord.id === "string" ? rightRecord.id : "";
777
+ if (leftId !== rightId) return leftId < rightId ? -1 : 1;
778
+ return safeString(left).localeCompare(safeString(right));
779
+ }
780
+ function comparePositionedRecords(left, right) {
781
+ const leftRecord = isRecord(left) ? left : void 0;
782
+ const rightRecord = isRecord(right) ? right : void 0;
783
+ return (leftRecord && typeof leftRecord.position === "number" ? leftRecord.position : leftRecord && typeof leftRecord.ordinalPosition === "number" ? leftRecord.ordinalPosition : Number.MAX_SAFE_INTEGER) - (rightRecord && typeof rightRecord.position === "number" ? rightRecord.position : rightRecord && typeof rightRecord.ordinalPosition === "number" ? rightRecord.ordinalPosition : Number.MAX_SAFE_INTEGER) || compareUnknownRecords(left, right);
784
+ }
785
+ function safeString(value) {
786
+ try {
787
+ return JSON.stringify(value) ?? String(value);
788
+ } catch {
789
+ return String(value);
790
+ }
791
+ }
792
+ function canonicalEquivalent(left, right) {
793
+ try {
794
+ return canonicalJson(toSnapshotJsonValue(left)) === canonicalJson(toSnapshotJsonValue(right));
795
+ } catch {
796
+ return safeString(left) === safeString(right);
797
+ }
798
+ }
799
+ function normalizeRenameTarget(value, path, diagnostics) {
800
+ if (typeof value === "string") {
801
+ if (value.length === 0) {
802
+ diagnostics.push(diffDiagnostic("invalid-rename-hint", "Rename target cannot be empty", path));
803
+ return;
804
+ }
805
+ return freeze({ id: value });
806
+ }
807
+ if (!isRecord(value)) {
808
+ diagnostics.push(diffDiagnostic("invalid-rename-hint", "Rename target must be a string or object", path));
809
+ return;
810
+ }
811
+ for (const key of Object.keys(value)) if (key !== "id" && key !== "physicalName" && key !== "path") diagnostics.push(diffDiagnostic("invalid-rename-hint", `Unknown rename target field "${key}"`, [...path, key]));
812
+ const id = value.id;
813
+ const physicalName = value.physicalName;
814
+ const targetPath = value.path;
815
+ if (id !== void 0 && (typeof id !== "string" || id.length === 0)) diagnostics.push(diffDiagnostic("invalid-rename-hint", "Rename target id must be a non-empty string", [...path, "id"]));
816
+ if (physicalName !== void 0 && (typeof physicalName !== "string" || physicalName.length === 0)) diagnostics.push(diffDiagnostic("invalid-rename-hint", "Rename target physicalName must be a non-empty string", [...path, "physicalName"]));
817
+ if (targetPath !== void 0 && !isPath(targetPath)) diagnostics.push(diffDiagnostic("invalid-rename-hint", "Rename target path must be an array", [...path, "path"]));
818
+ if (id === void 0 && physicalName === void 0 && targetPath === void 0) {
819
+ diagnostics.push(diffDiagnostic("invalid-rename-hint", "Rename target needs an id, physicalName, or path", path));
820
+ return;
821
+ }
822
+ if (id !== void 0 && (typeof id !== "string" || id.length === 0) || physicalName !== void 0 && (typeof physicalName !== "string" || physicalName.length === 0) || targetPath !== void 0 && !isPath(targetPath)) return void 0;
823
+ return freeze({
824
+ ...typeof id === "string" ? { id } : {},
825
+ ...typeof physicalName === "string" ? { physicalName } : {},
826
+ ...isPath(targetPath) ? { path: freeze([...targetPath]) } : {}
827
+ });
828
+ }
829
+ function targetKey(target) {
830
+ return `${target.id ?? ""}\u0000${target.physicalName ?? ""}\u0000${target.path?.join(".") ?? ""}`;
831
+ }
832
+ function isObjectKind(value) {
833
+ return typeof value === "string" && objectKinds.has(value);
834
+ }
835
+ function isPath(value) {
836
+ return Array.isArray(value) && value.every((item) => typeof item === "string" || Number.isSafeInteger(item));
837
+ }
838
+ function samePath(left, right) {
839
+ return left.length === right.length && left.every((item, index) => item === right[index]);
840
+ }
841
+ function compareObject(left, right) {
842
+ return left.object.kind.localeCompare(right.object.kind) || (left.object.namespace ?? "").localeCompare(right.object.namespace ?? "") || (left.object.parent?.id ?? "").localeCompare(right.object.parent?.id ?? "") || left.object.id.localeCompare(right.object.id) || safeString(left.object.path).localeCompare(safeString(right.object.path));
843
+ }
844
+ function compareOperation(left, right) {
845
+ return (operationOrder.get(left.type) ?? 99) - (operationOrder.get(right.type) ?? 99) || left.kind.localeCompare(right.kind) || (left.namespace ?? "").localeCompare(right.namespace ?? "") || (left.logicalId ?? "").localeCompare(right.logicalId ?? "") || safeString(left.before?.path ?? left.after?.path).localeCompare(safeString(right.before?.path ?? right.after?.path));
846
+ }
847
+ function compareSuggestion(left, right) {
848
+ return left.kind.localeCompare(right.kind) || (left.namespace ?? "").localeCompare(right.namespace ?? "") || left.before.id.localeCompare(right.before.id) || left.after.id.localeCompare(right.after.id);
849
+ }
850
+ function compareHint(left, right) {
851
+ return left.kind.localeCompare(right.kind) || (left.namespace ?? "").localeCompare(right.namespace ?? "") || targetKey(normalizeTargetForCompare(left.from)).localeCompare(targetKey(normalizeTargetForCompare(right.from))) || targetKey(normalizeTargetForCompare(left.to)).localeCompare(targetKey(normalizeTargetForCompare(right.to)));
852
+ }
853
+ function normalizeTargetForCompare(target) {
854
+ return typeof target === "string" ? { id: target } : target;
855
+ }
856
+ function clampThreshold(value) {
857
+ return value === void 0 || !Number.isFinite(value) ? .75 : Math.min(1, Math.max(0, value));
858
+ }
859
+ function sortDiagnostics(diagnostics) {
860
+ return freeze([...diagnostics].sort((left, right) => left.code.localeCompare(right.code) || safeString(left.path).localeCompare(safeString(right.path)) || left.message.localeCompare(right.message)));
861
+ }
862
+ function freezeDiff(value) {
863
+ return deepFreeze(value);
864
+ }
865
+ function deepFreeze(value, seen = /* @__PURE__ */ new WeakSet()) {
866
+ if (value === null || typeof value !== "object") return value;
867
+ if (seen.has(value)) return value;
868
+ seen.add(value);
869
+ if (Array.isArray(value)) for (const item of value) deepFreeze(item, seen);
870
+ else for (const child of Object.values(value)) deepFreeze(child, seen);
871
+ return Object.freeze(value);
872
+ }
873
+ function freeze(value) {
874
+ return deepFreeze(value);
875
+ }
876
+ function isRecord(value) {
877
+ return typeof value === "object" && value !== null && !Array.isArray(value);
878
+ }
879
+ function isSnapshotRecord(value) {
880
+ return typeof value === "object" && value !== null && !Array.isArray(value);
881
+ }
882
+ //#endregion
883
+ export { compareSnapshots, decodeRenameHints, decodeSnapshotForDiff, decodeSnapshotRenameHints, diffSnapshots, encodeRenameHints, encodeSnapshotRenameHints, validateRenameHints, validateSnapshotRenameHints };