@supawatch/target-seed 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +57 -2
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -130,6 +130,12 @@ export class SeedTarget {
130
130
  ];
131
131
  const tables = snapshot.tables.filter((t) => t.kind === "table");
132
132
  const byName = new Map(tables.map((t) => [`${t.schema}.${t.name}`, t]));
133
+ // A base-type literal for a constrained domain is a guess against a
134
+ // CHECK the snapshot does not parse. Never guess: let the database
135
+ // fill nullable or defaulted columns of constrained domains, and
136
+ // skip tables that require one. Unconstrained domains behave as
137
+ // their base type and seed normally.
138
+ const constrainedDomains = new Set(snapshot.domains.filter((d) => d.hasConstraints).map((d) => d.name));
133
139
  // The literal a table's Nth row uses for its single-column primary
134
140
  // key. Children reuse this for their FK cells, so uuid and numeric
135
141
  // parents both reference correctly. Deterministic by construction.
@@ -155,9 +161,10 @@ export class SeedTarget {
155
161
  for (const t of cyclic) {
156
162
  lines.push(`-- skipped ${t.schema}.${t.name}: required foreign keys form a cycle`);
157
163
  }
164
+ // Pass 1: plan each table's insertable columns and its own skip
165
+ // reasons, without emitting yet.
166
+ const plans = [];
158
167
  for (const table of ordered) {
159
- const q = (s) => '"' + s.replace(/"/g, '""') + '"';
160
- const ident = `${q(table.schema)}.${q(table.name)}`;
161
168
  const pk = table.primaryKey.length === 1 ? table.primaryKey[0] : null;
162
169
  const skipReasons = [];
163
170
  const cols = [];
@@ -175,6 +182,15 @@ export class SeedTarget {
175
182
  }
176
183
  continue;
177
184
  }
185
+ if (constrainedDomains.has(col.pgTypeName)) {
186
+ if (col.name === pk) {
187
+ skipReasons.push(`primary key ${col.name} has a constrained domain type`);
188
+ }
189
+ else if (!col.nullable && !col.hasDefault) {
190
+ skipReasons.push(`constrained domain type on ${col.name}`);
191
+ }
192
+ continue;
193
+ }
178
194
  if (col.name === pk) {
179
195
  cols.push(col);
180
196
  continue;
@@ -192,6 +208,45 @@ export class SeedTarget {
192
208
  }
193
209
  cols.push(col);
194
210
  }
211
+ plans.push({ table, cols, skipReasons });
212
+ }
213
+ // A required single-column FK into a table that is not seeded (its
214
+ // own reasons, a cycle, or outside the snapshot) makes the child
215
+ // unseedable too; propagate to a fixpoint so seed.sql always
216
+ // applies.
217
+ const skipped = new Set(cyclic.map((t) => `${t.schema}.${t.name}`));
218
+ for (const p of plans) {
219
+ if (p.skipReasons.length > 0)
220
+ skipped.add(`${p.table.schema}.${p.table.name}`);
221
+ }
222
+ let changed = true;
223
+ while (changed) {
224
+ changed = false;
225
+ for (const p of plans) {
226
+ const key = `${p.table.schema}.${p.table.name}`;
227
+ if (skipped.has(key))
228
+ continue;
229
+ for (const fk of p.table.foreignKeys) {
230
+ if (f_multi(fk))
231
+ continue;
232
+ const col = p.table.columns.find((c) => fk.columns.includes(c.name));
233
+ if (!col || col.nullable || col.hasDefault)
234
+ continue;
235
+ const parentKey = `${fk.referencedSchema}.${fk.referencedTable}`;
236
+ if (skipped.has(parentKey) || !byName.has(parentKey)) {
237
+ p.skipReasons.push(`required foreign key ${col.name} references ${parentKey}, which is not seeded`);
238
+ skipped.add(key);
239
+ changed = true;
240
+ break;
241
+ }
242
+ }
243
+ }
244
+ }
245
+ // Pass 2: emit.
246
+ for (const { table, cols, skipReasons } of plans) {
247
+ const q = (s) => '"' + s.replace(/"/g, '""') + '"';
248
+ const ident = `${q(table.schema)}.${q(table.name)}`;
249
+ const pk = table.primaryKey.length === 1 ? table.primaryKey[0] : null;
195
250
  if (skipReasons.length > 0) {
196
251
  lines.push(`-- skipped ${table.schema}.${table.name}: ${skipReasons.join("; ")}`);
197
252
  continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@supawatch/target-seed",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "Deterministic FK-aware seed.sql generated from live Postgres by supawatch: topological order, identity overriding, sequence resync.",
5
5
  "keywords": [
6
6
  "supabase",
@@ -34,7 +34,7 @@
34
34
  "dist"
35
35
  ],
36
36
  "dependencies": {
37
- "@supawatch/core": "0.5.0"
37
+ "@supawatch/core": "0.7.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/node": "^22.20.1",