@sequenceholdings/orm 2.8.3 → 2.8.4

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 (52) hide show
  1. package/dist/authoring.cjs +31 -8
  2. package/dist/authoring.cjs.map +1 -1
  3. package/dist/authoring.d.cts +8 -2
  4. package/dist/authoring.d.ts +8 -2
  5. package/dist/authoring.js +31 -8
  6. package/dist/authoring.js.map +1 -1
  7. package/dist/canonical.cjs +1 -1
  8. package/dist/canonical.cjs.map +1 -1
  9. package/dist/canonical.d.cts +1 -1
  10. package/dist/canonical.d.ts +1 -1
  11. package/dist/canonical.js +1 -1
  12. package/dist/canonical.js.map +1 -1
  13. package/dist/cli.cjs +1 -1
  14. package/dist/cli.cjs.map +1 -1
  15. package/dist/cli.js +1 -1
  16. package/dist/cli.js.map +1 -1
  17. package/dist/compiler.cjs +1 -1
  18. package/dist/compiler.cjs.map +1 -1
  19. package/dist/compiler.d.cts +4 -4
  20. package/dist/compiler.d.ts +4 -4
  21. package/dist/compiler.js +1 -1
  22. package/dist/compiler.js.map +1 -1
  23. package/dist/index.cjs +31 -8
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +7 -9
  26. package/dist/index.d.ts +7 -9
  27. package/dist/index.js +31 -8
  28. package/dist/index.js.map +1 -1
  29. package/dist/ir.cjs +1 -1
  30. package/dist/ir.cjs.map +1 -1
  31. package/dist/ir.d.cts +2 -2
  32. package/dist/ir.d.ts +2 -2
  33. package/dist/ir.js +1 -1
  34. package/dist/ir.js.map +1 -1
  35. package/dist/{naming-CU9ThP_E.d.cts → naming-BMZKFO0W.d.cts} +1 -2
  36. package/dist/{naming-CPfx7EfF.d.ts → naming-DUk7AcXs.d.ts} +1 -2
  37. package/dist/predicate-v2.cjs +1 -1
  38. package/dist/predicate-v2.cjs.map +1 -1
  39. package/dist/predicate-v2.d.cts +3 -3
  40. package/dist/predicate-v2.d.ts +3 -3
  41. package/dist/predicate-v2.js +1 -1
  42. package/dist/predicate-v2.js.map +1 -1
  43. package/dist/sdl.cjs +1 -1
  44. package/dist/sdl.cjs.map +1 -1
  45. package/dist/sdl.d.cts +2 -2
  46. package/dist/sdl.d.ts +2 -2
  47. package/dist/sdl.js +1 -1
  48. package/dist/sdl.js.map +1 -1
  49. package/package.json +1 -1
  50. package/src/codegen-gql/apollo-hooks.cjs +1 -1
  51. package/src/codegen-gql/server-fns.cjs +1 -1
  52. package/templates/orm-namespace/graphql/example/queries.ts +1 -1
package/dist/index.d.cts CHANGED
@@ -578,6 +578,12 @@ interface SyncedBacking {
578
578
  */
579
579
  readonly readGuard?: 'crm-serving-v2';
580
580
  }
581
+ /**
582
+ * Atlas reassigns every relation in `orm_<ns>` to this role after apply.
583
+ * Synced views resolve backing access as their owner. Grant this role narrow
584
+ * SELECT before CREATE VIEW, or preserve a separately verified publisher grant.
585
+ */
586
+ declare const SYNCED_VIEW_EXECUTION_OWNER_ROLE = "orm_exec";
581
587
  /**
582
588
  * Emit CREATE OR REPLACE VIEW + SELECT-only grants for synced tables that have
583
589
  * a registered backing. Synced tables cannot use table-level RLS (they may be
@@ -586,12 +592,6 @@ interface SyncedBacking {
586
592
  * Each view lives in `orm_<ns>` alongside regular tables, named identically to
587
593
  * the synced table definition so read routing is transparent.
588
594
  */
589
- /**
590
- * Atlas reassigns every relation in `orm_<ns>` to this role after apply.
591
- * Synced views resolve backing access as their owner. Grant this role narrow
592
- * SELECT before CREATE VIEW, or preserve a separately verified publisher grant.
593
- */
594
- declare const SYNCED_VIEW_EXECUTION_OWNER_ROLE = "orm_exec";
595
595
  declare function syncedViewStatements({ definition, backings, readPolicyIrByTable, policyTables, columnNamesByTable, columnTypesByTable, executionOwnerRole, backingPrivileges, viewPrivileges, statementClaimChecks, }: {
596
596
  definition: NamespaceDefinition;
597
597
  backings: readonly SyncedBacking[];
@@ -747,9 +747,7 @@ declare function planCommittedMigrationV2({ canonical, previousSnapshot, reprodu
747
747
  * and types the live view from the SELECT itself. Assert the live catalog in
748
748
  * the same transaction so a stale alias/type or an incomplete dependency
749
749
  * declaration can never be persisted as a successful schema version.
750
- */
751
- /**
752
- * Assert live view columns/types/dependencies match the canonical contract.
750
+ *
753
751
  * Used by planMigrationV2 and by CASCADE recovery recreates (same transaction).
754
752
  */
755
753
  declare function validateViewCatalogStatement({ canonical, view, schema, }: {
package/dist/index.d.ts CHANGED
@@ -578,6 +578,12 @@ interface SyncedBacking {
578
578
  */
579
579
  readonly readGuard?: 'crm-serving-v2';
580
580
  }
581
+ /**
582
+ * Atlas reassigns every relation in `orm_<ns>` to this role after apply.
583
+ * Synced views resolve backing access as their owner. Grant this role narrow
584
+ * SELECT before CREATE VIEW, or preserve a separately verified publisher grant.
585
+ */
586
+ declare const SYNCED_VIEW_EXECUTION_OWNER_ROLE = "orm_exec";
581
587
  /**
582
588
  * Emit CREATE OR REPLACE VIEW + SELECT-only grants for synced tables that have
583
589
  * a registered backing. Synced tables cannot use table-level RLS (they may be
@@ -586,12 +592,6 @@ interface SyncedBacking {
586
592
  * Each view lives in `orm_<ns>` alongside regular tables, named identically to
587
593
  * the synced table definition so read routing is transparent.
588
594
  */
589
- /**
590
- * Atlas reassigns every relation in `orm_<ns>` to this role after apply.
591
- * Synced views resolve backing access as their owner. Grant this role narrow
592
- * SELECT before CREATE VIEW, or preserve a separately verified publisher grant.
593
- */
594
- declare const SYNCED_VIEW_EXECUTION_OWNER_ROLE = "orm_exec";
595
595
  declare function syncedViewStatements({ definition, backings, readPolicyIrByTable, policyTables, columnNamesByTable, columnTypesByTable, executionOwnerRole, backingPrivileges, viewPrivileges, statementClaimChecks, }: {
596
596
  definition: NamespaceDefinition;
597
597
  backings: readonly SyncedBacking[];
@@ -747,9 +747,7 @@ declare function planCommittedMigrationV2({ canonical, previousSnapshot, reprodu
747
747
  * and types the live view from the SELECT itself. Assert the live catalog in
748
748
  * the same transaction so a stale alias/type or an incomplete dependency
749
749
  * declaration can never be persisted as a successful schema version.
750
- */
751
- /**
752
- * Assert live view columns/types/dependencies match the canonical contract.
750
+ *
753
751
  * Used by planMigrationV2 and by CASCADE recovery recreates (same transaction).
754
752
  */
755
753
  declare function validateViewCatalogStatement({ canonical, view, schema, }: {
package/dist/index.js CHANGED
@@ -1759,7 +1759,7 @@ var canonicalTableSchema = z.object({
1759
1759
  table: identifier2,
1760
1760
  displayName: z.string().trim().min(1).max(120).optional(),
1761
1761
  description: z.string().max(2e3).optional(),
1762
- /** Table-level rename hint (new in v2). */
1762
+ /** Table-level rename hint. */
1763
1763
  renamedFrom: identifier2.optional(),
1764
1764
  /** Synced/reflected read-only tables (`source: schema.table`) — v1 semantics. */
1765
1765
  source: z.string().regex(QUALIFIED_TABLE_PATTERN).optional(),
@@ -2423,6 +2423,12 @@ function builtinCell({ comparator, candidates }) {
2423
2423
  }
2424
2424
  };
2425
2425
  }
2426
+ function existsInCell(candidate) {
2427
+ return {
2428
+ value: candidate.value,
2429
+ parts: { source: literal(candidate.source), at: candidate.at, by: candidate.by, conflicts: "CAST(NULL AS jsonb)" }
2430
+ };
2431
+ }
2426
2432
  function customCell({ candidates, orderBy, index: index3 }) {
2427
2433
  const w = ident(`w_${index3}`);
2428
2434
  const rows = candidates.map((c, ord) => `(${literal(c.source)}, ${c.has}, ${c.value}, ${c.at}, ${c.by}, ${ord})`);
@@ -2446,26 +2452,29 @@ function buildResolvedQuery({ context, plan }) {
2446
2452
  const tombstoned2 = RESOLVED_SOURCE_SYSTEM_COLUMNS.tombstoned.columnName;
2447
2453
  const sourceByName = new Map(plan.plans.map((source) => [source.name, source]));
2448
2454
  const schema = getTableConfig(relation(plan.plans[0].table)).schema;
2455
+ const keySource = plan.existsIn === void 0 ? void 0 : sourceByName.get(plan.existsIn);
2456
+ const keyRef = keySource ? `${alias(keySource.name)}.${ident(physical({ source: keySource, property: plan.pk }))}` : `"k".${ident(pkColumn)}`;
2449
2457
  const keyBranches = plan.plans.map((source) => {
2450
2458
  const where = source.kind === "prefetch" ? sql.raw(` WHERE NOT ${ident(tombstoned2)}`) : sql.raw("");
2451
2459
  const sourcePk = ident(physical({ source, property: plan.pk }));
2452
2460
  const selectedPk = sourcePk === ident(pkColumn) ? sourcePk : `${sourcePk} AS ${ident(pkColumn)}`;
2453
2461
  return sql`SELECT ${sql.raw(selectedPk)} FROM ${relation(source.table)}${where}`;
2454
2462
  });
2455
- const joins = plan.plans.map(
2456
- (source) => sql` LEFT JOIN ${relation(source.table)} AS ${sql.raw(alias(source.name))} ON ${sql.raw(`${alias(source.name)}.${ident(physical({ source, property: plan.pk }))} = "k".${ident(pkColumn)}`)}`
2463
+ const base2 = keySource ? sql`${relation(keySource.table)} AS ${sql.raw(alias(keySource.name))}` : sql`(${sql.join(keyBranches, sql.raw(" UNION "))}) AS "k"`;
2464
+ const joins = plan.plans.filter((source) => source !== keySource).map(
2465
+ (source) => sql` LEFT JOIN ${relation(source.table)} AS ${sql.raw(alias(source.name))} ON ${sql.raw(`${alias(source.name)}.${ident(physical({ source, property: plan.pk }))} = ${keyRef}`)}`
2457
2466
  );
2458
2467
  const cells = /* @__PURE__ */ new Map();
2459
2468
  plan.rules.forEach((rule, index3) => {
2460
2469
  const canonical = plan.columns[rule.column].toCanonicalColumn(rule.column);
2461
2470
  const pgType = typeof canonical.type === "string" ? PG_TYPE[canonical.type] : `${schema === void 0 ? "" : `${ident(schema)}.`}${ident(canonical.type.enum)}`;
2462
2471
  const candidates = rule.candidates.map((name2) => candidateSql({ source: sourceByName.get(name2), column: rule.column, pk: plan.pk, pgType }));
2463
- cells.set(rule.column, rule.comparator === "custom" ? customCell({ candidates, orderBy: rule.orderBy, index: index3 }) : builtinCell({ comparator: rule.comparator, candidates }));
2472
+ cells.set(rule.column, keySource && rule.candidates.length === 1 && rule.candidates[0] === keySource.name ? existsInCell(candidates[0]) : rule.comparator === "custom" ? customCell({ candidates, orderBy: rule.orderBy, index: index3 }) : builtinCell({ comparator: rule.comparator, candidates }));
2464
2473
  });
2465
2474
  const laterals = [...cells.values()].flatMap((cell) => cell.lateral ? [sql.raw(cell.lateral)] : []);
2466
- const from = sql.join([sql`(${sql.join(keyBranches, sql.raw(" UNION "))}) AS "k"`, ...joins, ...laterals], sql.raw(""));
2475
+ const from = sql.join([base2, ...joins, ...laterals], sql.raw(""));
2467
2476
  const selected = {
2468
- [plan.pk]: sql.raw(`"k".${ident(pkColumn)}`).as(pkColumn)
2477
+ [plan.pk]: sql.raw(keyRef).as(pkColumn)
2469
2478
  };
2470
2479
  for (const [property, builder] of Object.entries(plan.columns)) {
2471
2480
  if (property === plan.pk) continue;
@@ -2537,10 +2546,12 @@ function defineResolvedTable(name2, columns, config) {
2537
2546
  for (const source of deletedBy) {
2538
2547
  if (plans.find((plan) => plan.name === source)?.kind !== "manual") fail3(`rows.deletedBy "${source}" must be the manual source`);
2539
2548
  }
2549
+ const existsIn = planExistsIn({ config, plans, fail: fail3 });
2550
+ const notNullColumns = existsIn ? inheritedNotNullColumns({ existsIn, rules }) : /* @__PURE__ */ new Set();
2540
2551
  const partType = { source: t.text, at: t.timestamptz, by: t.text, conflicts: t.jsonb };
2541
2552
  const viewColumns = {};
2542
2553
  for (const [property, builder] of Object.entries(columns)) {
2543
- viewColumns[property] = cloneColumn({ builder, property, notNull: property === pk, primaryKey: property === pk });
2554
+ viewColumns[property] = cloneColumn({ builder, property, notNull: property === pk || notNullColumns.has(property), primaryKey: property === pk });
2544
2555
  if (property === pk) continue;
2545
2556
  const columnName = builder.columnNameFor(property);
2546
2557
  for (const part of RESOLVED_VIEW_CELL_PARTS) {
@@ -2556,7 +2567,7 @@ function defineResolvedTable(name2, columns, config) {
2556
2567
  deletedBy,
2557
2568
  policies: writePolicies
2558
2569
  };
2559
- const queryPlan = { columns, pk, plans, rules, deletedBy };
2570
+ const queryPlan = { columns, pk, plans, rules, deletedBy, ...existsIn ? { existsIn: existsIn.name } : {} };
2560
2571
  const typedColumns = viewColumns;
2561
2572
  const surface = {
2562
2573
  ...config.description !== void 0 ? { description: config.description } : {},
@@ -2598,6 +2609,18 @@ function defineResolvedTable(name2, columns, config) {
2598
2609
  }
2599
2610
  });
2600
2611
  }
2612
+ function planExistsIn({ config, plans, fail: fail3 }) {
2613
+ const names = config.rows?.existsIn;
2614
+ if (names === void 0) return void 0;
2615
+ if (names.length !== 1) fail3("rows.existsIn names exactly one etl source");
2616
+ const source = plans.find((plan) => plan.name === names[0]);
2617
+ if (source?.kind !== "etl") fail3(`rows.existsIn "${names[0]}" must be an etl source`);
2618
+ if (config.policies.insert !== void 0 || config.policies.write !== void 0) fail3("rows.existsIn allows only an update policy: rows come from the etl source");
2619
+ return source;
2620
+ }
2621
+ function inheritedNotNullColumns({ existsIn, rules }) {
2622
+ return new Set(rules.filter((rule) => rule.candidates.length === 1 && rule.candidates[0] === existsIn.name).map((rule) => rule.column).filter((column) => existsIn.table.columns[column]?.toCanonicalColumn(column).nullable === false));
2623
+ }
2601
2624
  function planRules({ config, plans, dataColumns, fail: fail3 }) {
2602
2625
  const kindOrder = { manual: 0, prefetch: 1, etl: 2 };
2603
2626
  const fallback = config.default ?? priority([...plans].sort((a, b) => kindOrder[a.kind] - kindOrder[b.kind]).map((plan) => plan.name));