@sequenceholdings/orm 2.8.5 → 2.10.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 (56) hide show
  1. package/dist/authoring.cjs.map +1 -1
  2. package/dist/authoring.d.cts +2 -2
  3. package/dist/authoring.d.ts +2 -2
  4. package/dist/authoring.js.map +1 -1
  5. package/dist/canonical.cjs.map +1 -1
  6. package/dist/canonical.d.cts +72 -72
  7. package/dist/canonical.d.ts +72 -72
  8. package/dist/canonical.js.map +1 -1
  9. package/dist/cli.cjs +122 -68
  10. package/dist/cli.cjs.map +1 -1
  11. package/dist/cli.d.cts +32 -1
  12. package/dist/cli.d.ts +32 -1
  13. package/dist/cli.js +117 -69
  14. package/dist/cli.js.map +1 -1
  15. package/dist/client.cjs.map +1 -1
  16. package/dist/client.d.cts +1 -1
  17. package/dist/client.d.ts +1 -1
  18. package/dist/client.js.map +1 -1
  19. package/dist/compiler.cjs +259 -18
  20. package/dist/compiler.cjs.map +1 -1
  21. package/dist/compiler.d.cts +183 -3
  22. package/dist/compiler.d.ts +183 -3
  23. package/dist/compiler.js +254 -19
  24. package/dist/compiler.js.map +1 -1
  25. package/dist/{constants-Dou1RkTo.d.cts → constants-D2o0vK6U.d.cts} +8 -1
  26. package/dist/{constants-Dou1RkTo.d.ts → constants-D2o0vK6U.d.ts} +8 -1
  27. package/dist/dynamic-input.d.cts +1 -1
  28. package/dist/dynamic-input.d.ts +1 -1
  29. package/dist/index.cjs +53 -31
  30. package/dist/index.cjs.map +1 -1
  31. package/dist/index.d.cts +61 -60
  32. package/dist/index.d.ts +61 -60
  33. package/dist/index.js +51 -32
  34. package/dist/index.js.map +1 -1
  35. package/dist/ir.cjs +9 -7
  36. package/dist/ir.cjs.map +1 -1
  37. package/dist/ir.d.cts +85 -74
  38. package/dist/ir.d.ts +85 -74
  39. package/dist/ir.js +9 -8
  40. package/dist/ir.js.map +1 -1
  41. package/dist/predicate-v2.cjs +9 -7
  42. package/dist/predicate-v2.cjs.map +1 -1
  43. package/dist/predicate-v2.d.cts +3 -1
  44. package/dist/predicate-v2.d.ts +3 -1
  45. package/dist/predicate-v2.js +9 -7
  46. package/dist/predicate-v2.js.map +1 -1
  47. package/dist/sdl.cjs +8 -7
  48. package/dist/sdl.cjs.map +1 -1
  49. package/dist/sdl.d.cts +1 -1
  50. package/dist/sdl.d.ts +1 -1
  51. package/dist/sdl.js +8 -7
  52. package/dist/sdl.js.map +1 -1
  53. package/package.json +3 -3
  54. package/templates/orm-namespace/package.json +2 -1
  55. package/dist/{definition-DjfC7Lll.d.cts → definition-B99BsOAO.d.cts} +4 -4
  56. package/dist/{definition-DjfC7Lll.d.ts → definition-B99BsOAO.d.ts} +4 -4
package/dist/cli.d.cts CHANGED
@@ -1,6 +1,22 @@
1
1
  /** Set by the seq-studio delegate; direct users export them. */
2
2
  declare const BASE_URL_ENV = "SEQUENCE_ORM_BASE_URL";
3
3
  declare const TOKEN_ENV = "SEQUENCE_ORM_TOKEN";
4
+ /** JSON `{repository_id, commit, path, dirty?}` naming the Git Service source of this apply; set by the seq-studio delegate. */
5
+ declare const SOURCE_ENV = "SEQUENCE_ORM_SOURCE";
6
+ /** Namespace-admin override reason for an apply whose source does not verify; set by `seq-studio orm apply --unverified-source`. */
7
+ declare const UNVERIFIED_SOURCE_REASON_ENV = "SEQUENCE_ORM_UNVERIFIED_SOURCE_REASON";
8
+ /**
9
+ * The caller's token for the source claim's `source_environment`; set by `seq-studio orm apply --source-env`.
10
+ * Only the apply request carries it (as `x-sequence-source-authorization`), so Atlas can read the source there.
11
+ */
12
+ declare const SOURCE_AUTHORIZATION_ENV = "SEQUENCE_ORM_SOURCE_AUTHORIZATION";
13
+ /** The apply request's source environment credential header, when the delegate supplied one. */
14
+ declare function applySourceHeaders(env?: NodeJS.ProcessEnv): Record<string, string>;
15
+ /** The apply body's `source` claim, when the delegate supplied one. Atlas verifies it; it never authorizes anything. */
16
+ declare function applySourceFromEnv(env?: NodeJS.ProcessEnv): {
17
+ source?: Record<string, unknown>;
18
+ unverified_source_reason?: string;
19
+ };
4
20
  /**
5
21
  * Test/dev hook mirroring `LoadNamespaceOptions.ormModulePath`: when set, bare
6
22
  * `'@sequenceholdings/orm'` imports in consumer files resolve to this path
@@ -12,6 +28,8 @@ type TokenProvider = () => Promise<string | null>;
12
28
  /** Hand the CLI a refreshing token source (used by the seq-studio delegate). */
13
29
  declare function setTokenProvider(provider: TokenProvider): void;
14
30
  declare function runCli(argv: readonly string[]): Promise<number>;
31
+ /** Where the live version came from: `live since <time> from <commit> on <branch> at <path> by <actor>`. */
32
+ declare function describeActivation(activation: RegistryActivation): string;
15
33
  interface ApplyWait {
16
34
  wait: boolean;
17
35
  timeoutMs: number;
@@ -24,5 +42,18 @@ declare function parseApplyWait(rest: readonly string[]): ApplyWait;
24
42
  * details name the variable, column, constraint, or requirement at fault.
25
43
  */
26
44
  declare function errorDetail(payload: unknown): string | null;
45
+ /** One row of the namespace's activation history, as the detail route returns it (absent on older Atlas). */
46
+ interface RegistryActivation {
47
+ id: string;
48
+ provenance_class: string;
49
+ /** Absent on an older Atlas; null for this deployment's own Git Service. */
50
+ source_environment?: string | null;
51
+ source_commit: string | null;
52
+ source_branch: string | null;
53
+ source_path: string | null;
54
+ reported_source: Record<string, unknown> | null;
55
+ actor_sub: string;
56
+ activated_at: string;
57
+ }
27
58
 
28
- export { BASE_URL_ENV, ORM_MODULE_PATH_ENV, TOKEN_ENV, errorDetail, parseApplyWait, runCli, setTokenProvider };
59
+ export { BASE_URL_ENV, ORM_MODULE_PATH_ENV, type RegistryActivation, SOURCE_AUTHORIZATION_ENV, SOURCE_ENV, TOKEN_ENV, UNVERIFIED_SOURCE_REASON_ENV, applySourceFromEnv, applySourceHeaders, describeActivation, errorDetail, parseApplyWait, runCli, setTokenProvider };
package/dist/cli.d.ts CHANGED
@@ -1,6 +1,22 @@
1
1
  /** Set by the seq-studio delegate; direct users export them. */
2
2
  declare const BASE_URL_ENV = "SEQUENCE_ORM_BASE_URL";
3
3
  declare const TOKEN_ENV = "SEQUENCE_ORM_TOKEN";
4
+ /** JSON `{repository_id, commit, path, dirty?}` naming the Git Service source of this apply; set by the seq-studio delegate. */
5
+ declare const SOURCE_ENV = "SEQUENCE_ORM_SOURCE";
6
+ /** Namespace-admin override reason for an apply whose source does not verify; set by `seq-studio orm apply --unverified-source`. */
7
+ declare const UNVERIFIED_SOURCE_REASON_ENV = "SEQUENCE_ORM_UNVERIFIED_SOURCE_REASON";
8
+ /**
9
+ * The caller's token for the source claim's `source_environment`; set by `seq-studio orm apply --source-env`.
10
+ * Only the apply request carries it (as `x-sequence-source-authorization`), so Atlas can read the source there.
11
+ */
12
+ declare const SOURCE_AUTHORIZATION_ENV = "SEQUENCE_ORM_SOURCE_AUTHORIZATION";
13
+ /** The apply request's source environment credential header, when the delegate supplied one. */
14
+ declare function applySourceHeaders(env?: NodeJS.ProcessEnv): Record<string, string>;
15
+ /** The apply body's `source` claim, when the delegate supplied one. Atlas verifies it; it never authorizes anything. */
16
+ declare function applySourceFromEnv(env?: NodeJS.ProcessEnv): {
17
+ source?: Record<string, unknown>;
18
+ unverified_source_reason?: string;
19
+ };
4
20
  /**
5
21
  * Test/dev hook mirroring `LoadNamespaceOptions.ormModulePath`: when set, bare
6
22
  * `'@sequenceholdings/orm'` imports in consumer files resolve to this path
@@ -12,6 +28,8 @@ type TokenProvider = () => Promise<string | null>;
12
28
  /** Hand the CLI a refreshing token source (used by the seq-studio delegate). */
13
29
  declare function setTokenProvider(provider: TokenProvider): void;
14
30
  declare function runCli(argv: readonly string[]): Promise<number>;
31
+ /** Where the live version came from: `live since <time> from <commit> on <branch> at <path> by <actor>`. */
32
+ declare function describeActivation(activation: RegistryActivation): string;
15
33
  interface ApplyWait {
16
34
  wait: boolean;
17
35
  timeoutMs: number;
@@ -24,5 +42,18 @@ declare function parseApplyWait(rest: readonly string[]): ApplyWait;
24
42
  * details name the variable, column, constraint, or requirement at fault.
25
43
  */
26
44
  declare function errorDetail(payload: unknown): string | null;
45
+ /** One row of the namespace's activation history, as the detail route returns it (absent on older Atlas). */
46
+ interface RegistryActivation {
47
+ id: string;
48
+ provenance_class: string;
49
+ /** Absent on an older Atlas; null for this deployment's own Git Service. */
50
+ source_environment?: string | null;
51
+ source_commit: string | null;
52
+ source_branch: string | null;
53
+ source_path: string | null;
54
+ reported_source: Record<string, unknown> | null;
55
+ actor_sub: string;
56
+ activated_at: string;
57
+ }
27
58
 
28
- export { BASE_URL_ENV, ORM_MODULE_PATH_ENV, TOKEN_ENV, errorDetail, parseApplyWait, runCli, setTokenProvider };
59
+ export { BASE_URL_ENV, ORM_MODULE_PATH_ENV, type RegistryActivation, SOURCE_AUTHORIZATION_ENV, SOURCE_ENV, TOKEN_ENV, UNVERIFIED_SOURCE_REASON_ENV, applySourceFromEnv, applySourceHeaders, describeActivation, errorDetail, parseApplyWait, runCli, setTokenProvider };
package/dist/cli.js CHANGED
@@ -102,6 +102,8 @@ function materializedRefreshFunctionName(table) {
102
102
  function materializeLockKey(schema, table) {
103
103
  return `orm:materialize:${schema}.${table}`;
104
104
  }
105
+ var OPERATION_DOCUMENTS_FILE = "operations.documents.json";
106
+ var OPERATION_DOCUMENTS_VERSION = 1;
105
107
 
106
108
  // src/hash.ts
107
109
  function stableJson(value) {
@@ -1811,19 +1813,20 @@ var variableIrSchema = z.object({
1811
1813
  defaultValue: variableDefaultIrSchema.optional()
1812
1814
  });
1813
1815
  var sqlColumnSchema = z.object({ name: z.string(), type: z.enum(["text", "boolean", "integer", "bigint", "numeric", "double", "uuid", "date", "timestamptz", "jsonb"]), nullable: z.boolean() });
1816
+ var sqlObjectIrSchema = z.object({
1817
+ ref: z.string(),
1818
+ table: z.string(),
1819
+ columns: z.array(sqlColumnSchema.extend({ type: z.string() })).min(1),
1820
+ scan: z.string().min(1),
1821
+ read: z.array(z.string()).min(1)
1822
+ }).strict();
1814
1823
  var compiledOperationSchema = z.object({
1815
1824
  language: z.literal("sql").optional(),
1816
1825
  /** Server-compiled object catalog and permission ceiling, never caller SQL. */
1817
1826
  sqlQuery: z.object({
1818
1827
  catalog: z.object({
1819
1828
  identity: z.string(),
1820
- objects: z.array(z.object({
1821
- ref: z.string(),
1822
- table: z.string(),
1823
- columns: z.array(sqlColumnSchema.extend({ type: z.string() })).min(1),
1824
- scan: z.string().min(1),
1825
- read: z.array(z.string()).min(1)
1826
- }).strict()).min(1).max(256)
1829
+ objects: z.array(sqlObjectIrSchema).min(1).max(256)
1827
1830
  }).strict(),
1828
1831
  read: z.array(z.string()).min(1).max(256),
1829
1832
  maxRows: z.number().int().min(1).max(1e4)
@@ -2483,6 +2486,7 @@ var FLIPPED = {
2483
2486
  ">=": "<="
2484
2487
  };
2485
2488
  function resolveTable(resolver, table) {
2489
+ resolver.onTable?.(table);
2486
2490
  const info = resolver.tables[table];
2487
2491
  if (!info) {
2488
2492
  throw new PolicyCompileError(`unknown table '${table}'`, "unknown-table", Object.keys(resolver.tables));
@@ -5398,10 +5402,13 @@ function sqlHash(value) {
5398
5402
  return `sha256:${createHash("sha256").update(value).digest("hex")}`;
5399
5403
  }
5400
5404
  function createSqlCatalog({ canonical, publishedObjects, executionTarget }) {
5401
- if (!publishedObjects.length || new Set(publishedObjects).size !== publishedObjects.length) throw new Error("SQL objects must be explicitly declared and unique");
5402
5405
  const { ctx, diagnostics } = buildCompileContext(canonical);
5403
5406
  if (diagnostics.some((d) => d.severity === "error")) throw new Error(diagnostics.map((d) => d.message).join("; "));
5404
- const identity = sqlHash(`ontology-sql-v1\0${computeSchemaHash(canonical)}\0${executionTarget}`);
5407
+ return createSqlCatalogFromContext({ canonical, publishedObjects, executionTarget, ctx });
5408
+ }
5409
+ function createSqlCatalogFromContext({ canonical, publishedObjects, executionTarget, ctx, identity: suppliedIdentity }) {
5410
+ if (!publishedObjects.length || new Set(publishedObjects).size !== publishedObjects.length) throw new Error("SQL objects must be explicitly declared and unique");
5411
+ const identity = suppliedIdentity ?? sqlHash(`ontology-sql-v1\0${computeSchemaHash(canonical)}\0${executionTarget}`);
5405
5412
  const logicalNames = new Map(Object.entries(ctx.tables).map(([key, meta]) => [meta, key]));
5406
5413
  const refOf = (meta) => `${meta.namespace ?? canonical.namespace}/${logicalNames.get(meta)}`;
5407
5414
  const definitions = [canonical, ...canonical.imports ?? []];
@@ -5512,9 +5519,10 @@ var SqlEmitter = class {
5512
5519
  only(cte, ["ctename", "ctequery", "ctematerialized", "location"]);
5513
5520
  const name2 = str(cte.ctename);
5514
5521
  if (scope.ctes.has(name2)) fail3("duplicate CTE");
5522
+ const materialization = cte.ctematerialized === "CTEMaterializeAlways" ? "MATERIALIZED " : cte.ctematerialized === "CTEMaterializeNever" ? "NOT MATERIALIZED " : cte.ctematerialized === "CTEMaterializeDefault" ? "" : fail3("unsupported CTE materialization");
5515
5523
  const result2 = this.select(obj(cte.ctequery), { ...parent, ctes: scope.ctes });
5516
5524
  scope.ctes.set(name2, result2);
5517
- ctes.push(`${quoteIdentifier(name2)} AS (${result2.text})`);
5525
+ ctes.push(`${quoteIdentifier(name2)} AS ${materialization}(${result2.text})`);
5518
5526
  }
5519
5527
  }
5520
5528
  const result = node.op === "SETOP_UNION" ? this.union(node, scope) : this.projection(node, scope);
@@ -10343,6 +10351,42 @@ async function readSnapshot(dir, ordinal) {
10343
10351
  const path = join(dir, MIGRATIONS_DIRNAME, META_DIRNAME, `${pad(ordinal)}.snapshot.json`);
10344
10352
  return parseJson(path, await readSnapshotText(dir, ordinal));
10345
10353
  }
10354
+ async function verifyCommittedLedger(dir, entries) {
10355
+ const firstRequiredSnapshot = entries.length - 2;
10356
+ let previousId3;
10357
+ let previousSnapshotPresent = false;
10358
+ let previousHash = null;
10359
+ for (const [index3, entry] of entries.entries()) {
10360
+ const sql4 = await readMigrationSql(dir, entry);
10361
+ if (checksumOf(sql4) !== entry.checksum) {
10362
+ return `${MIGRATIONS_DIRNAME}/${entry.name}.sql does not match its journal checksum \u2014 committed migrations must not be edited`;
10363
+ }
10364
+ const snapshotText = index3 >= firstRequiredSnapshot ? await readSnapshotText(dir, entry.ordinal) : await readPrunableSnapshotText(dir, entry.ordinal);
10365
+ if (snapshotText !== void 0) {
10366
+ if (checksumOf(snapshotText) !== entry.snapshot_checksum) {
10367
+ return `the snapshot for ${entry.name} does not match its journal checksum \u2014 committed snapshots must not be edited (the next diff builds on it)`;
10368
+ }
10369
+ const snapshot = JSON.parse(snapshotText);
10370
+ if (previousSnapshotPresent && snapshot.prevId !== previousId3) {
10371
+ return `snapshot chain broken at ${entry.name} \u2014 regenerate the workspace with \`seq-studio orm diff\``;
10372
+ }
10373
+ previousId3 = snapshot.id;
10374
+ }
10375
+ previousSnapshotPresent = snapshotText !== void 0;
10376
+ if (entry.previous_content_hash !== previousHash) {
10377
+ return `content-hash chain broken at ${entry.name} \u2014 migrations must be appended in order by \`seq-studio orm diff\``;
10378
+ }
10379
+ previousHash = entry.content_hash;
10380
+ }
10381
+ return void 0;
10382
+ }
10383
+ async function readPrunableSnapshotText(dir, ordinal) {
10384
+ const path = join(dir, MIGRATIONS_DIRNAME, META_DIRNAME, `${pad(ordinal)}.snapshot.json`);
10385
+ return readFile(path, "utf8").catch((error) => {
10386
+ if (error instanceof Error && "code" in error && error.code === "ENOENT") return void 0;
10387
+ throw error;
10388
+ });
10389
+ }
10346
10390
  async function readMigrationSql(dir, entry) {
10347
10391
  const path = join(dir, MIGRATIONS_DIRNAME, `${entry.name}.sql`);
10348
10392
  return readFile(path, "utf8").catch(() => {
@@ -10503,30 +10547,10 @@ async function checkWorkspace({
10503
10547
  log(`no committed migrations for '${loaded.definition.namespace}' \u2014 run \`seq-studio orm diff\` and commit the result`);
10504
10548
  return 1;
10505
10549
  }
10506
- let previousId3;
10507
- let previousHash = null;
10508
- for (const entry of workspace.journal.entries) {
10509
- const sql4 = await readMigrationSql(dir, entry);
10510
- if (checksumOf(sql4) !== entry.checksum) {
10511
- log(`${MIGRATIONS_DIRNAME}/${entry.name}.sql does not match its journal checksum \u2014 committed migrations must not be edited`);
10512
- return 1;
10513
- }
10514
- const snapshotText = await readSnapshotText(dir, entry.ordinal);
10515
- if (checksumOf(snapshotText) !== entry.snapshot_checksum) {
10516
- log(`the snapshot for ${entry.name} does not match its journal checksum \u2014 committed snapshots must not be edited (the next diff builds on it)`);
10517
- return 1;
10518
- }
10519
- const snapshot = JSON.parse(snapshotText);
10520
- if (entry.ordinal > 1 && snapshot.prevId !== previousId3) {
10521
- log(`snapshot chain broken at ${entry.name} \u2014 regenerate the workspace with \`seq-studio orm diff\``);
10522
- return 1;
10523
- }
10524
- if (entry.previous_content_hash !== previousHash) {
10525
- log(`content-hash chain broken at ${entry.name} \u2014 migrations must be appended in order by \`seq-studio orm diff\``);
10526
- return 1;
10527
- }
10528
- previousId3 = snapshot.id;
10529
- previousHash = entry.content_hash;
10550
+ const broken = await verifyCommittedLedger(dir, workspace.journal.entries);
10551
+ if (broken) {
10552
+ log(broken);
10553
+ return 1;
10530
10554
  }
10531
10555
  if (tip.content_hash !== loaded.contentHash) {
10532
10556
  log(`definitions changed since ${tip.name} \u2014 run \`seq-studio orm diff\` and commit the new migration`);
@@ -11405,6 +11429,27 @@ function parseArgs(argv) {
11405
11429
  // src/cli.ts
11406
11430
  var BASE_URL_ENV = "SEQUENCE_ORM_BASE_URL";
11407
11431
  var TOKEN_ENV = "SEQUENCE_ORM_TOKEN";
11432
+ var SOURCE_ENV = "SEQUENCE_ORM_SOURCE";
11433
+ var UNVERIFIED_SOURCE_REASON_ENV = "SEQUENCE_ORM_UNVERIFIED_SOURCE_REASON";
11434
+ var SOURCE_AUTHORIZATION_ENV = "SEQUENCE_ORM_SOURCE_AUTHORIZATION";
11435
+ function applySourceHeaders(env = process.env) {
11436
+ const token = env[SOURCE_AUTHORIZATION_ENV]?.trim();
11437
+ return token ? { "x-sequence-source-authorization": `Bearer ${token}` } : {};
11438
+ }
11439
+ function applySourceFromEnv(env = process.env) {
11440
+ const reason = env[UNVERIFIED_SOURCE_REASON_ENV]?.trim();
11441
+ const override = reason ? { unverified_source_reason: reason } : {};
11442
+ const raw = env[SOURCE_ENV];
11443
+ if (!raw) return override;
11444
+ let parsed;
11445
+ try {
11446
+ parsed = JSON.parse(raw);
11447
+ } catch {
11448
+ throw new Error(`${SOURCE_ENV} is not valid JSON`);
11449
+ }
11450
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) throw new Error(`${SOURCE_ENV} must be a JSON object`);
11451
+ return { source: Object.fromEntries(Object.entries(parsed)), ...override };
11452
+ }
11408
11453
  var ORM_MODULE_PATH_ENV = "SEQUENCE_ORM_MODULE_PATH";
11409
11454
  var USAGE = `usage:
11410
11455
  seq-studio orm init <dir> scaffold a namespace package (v2: TypeScript schema + persisted operations)
@@ -11722,15 +11767,21 @@ async function generateCommand(rest) {
11722
11767
  await writeGenerateOutputs(dir, compiled.project);
11723
11768
  const { loaded, manifest } = compiled.project;
11724
11769
  console.log(
11725
- `wrote ${SCHEMA_GRAPHQL_FILE}, ${OPERATIONS_MANIFEST_FILE}, ${TYPE_POLICIES_FILE}, sql-operations.gen.ts, namespace.contract.json (${loaded.canonical.tables.length} table(s), ${loaded.canonical.views?.length ?? 0} view(s), ${Object.keys(manifest.operations).length} operation(s))`
11770
+ `wrote ${SCHEMA_GRAPHQL_FILE}, ${OPERATIONS_MANIFEST_FILE}, ${OPERATION_DOCUMENTS_FILE}, ${TYPE_POLICIES_FILE}, sql-operations.gen.ts, namespace.contract.json (${loaded.canonical.tables.length} table(s), ${loaded.canonical.views?.length ?? 0} view(s), ${Object.keys(manifest.operations).length} operation(s))`
11726
11771
  );
11727
11772
  return runConsumerCodegen(dir);
11728
11773
  }
11729
11774
  async function writeGenerateOutputs(dir, project) {
11730
- const { loaded, sdl, manifest } = project;
11775
+ const { loaded, sdl, manifest, documents } = project;
11731
11776
  await writeFile(join(dir, SCHEMA_GRAPHQL_FILE), sdl);
11732
11777
  await writeFile(join(dir, "namespace.contract.json"), `${JSON.stringify(loaded.canonical, null, 2)}
11733
11778
  `);
11779
+ const sortedDocuments = [...documents].sort((a, b) => a.path.localeCompare(b.path)).map(({ path, contents }) => ({ path, contents }));
11780
+ await writeFile(
11781
+ join(dir, OPERATION_DOCUMENTS_FILE),
11782
+ `${JSON.stringify({ documentsVersion: OPERATION_DOCUMENTS_VERSION, documents: sortedDocuments }, null, 2)}
11783
+ `
11784
+ );
11734
11785
  const operations = Object.entries(manifest.operations).map(([name2, op]) => ({
11735
11786
  name: name2,
11736
11787
  kind: op.kind,
@@ -11936,8 +11987,21 @@ function reportRegistryState(registry) {
11936
11987
  if (match && match.id === registry.activeVersionId) console.log("-- registry: this content is the ACTIVE version");
11937
11988
  else if (match) console.log(`-- registry: registered (status ${match.status}), not active`);
11938
11989
  else console.log(`-- registry: new content (active is ${active ? active.content_hash.slice(0, 12) : "none"})`);
11990
+ if (registry.activeActivation) console.log(`-- registry: ${describeActivation(registry.activeActivation)}`);
11939
11991
  return 0;
11940
11992
  }
11993
+ function describeActivation(activation) {
11994
+ const since = `live since ${activation.activated_at.slice(0, 16).replace("T", " ")}Z`;
11995
+ const by = `by ${activation.actor_sub.replace(/^google-apps\|/, "")}`;
11996
+ if (activation.source_commit && activation.source_branch) {
11997
+ const at = activation.source_path ? ` at ${activation.source_path}` : "";
11998
+ const branch = activation.source_environment ? `${activation.source_environment}:${activation.source_branch}` : activation.source_branch;
11999
+ return `${since} from ${activation.source_commit.slice(0, 12)} on ${branch}${at} (${activation.provenance_class}) ${by}`;
12000
+ }
12001
+ const failure = activation.reported_source?.verification_failure;
12002
+ const why = typeof failure === "string" ? `: ${failure}` : "";
12003
+ return `${since} from an untraced source (${activation.provenance_class}${why}) ${by}`;
12004
+ }
11941
12005
  var META_DIRNAME2 = "meta";
11942
12006
  var JOURNAL_FILENAME2 = "journal.json";
11943
12007
  var padOrdinal = (ordinal) => String(ordinal).padStart(4, "0");
@@ -12025,30 +12089,10 @@ async function checkWorkspaceV2({
12025
12089
  log(`no committed migrations for '${namespace}' \u2014 run \`seq-studio orm diff\` and commit the result`);
12026
12090
  return 1;
12027
12091
  }
12028
- let previousId3;
12029
- let previousHash = null;
12030
- for (const entry of workspace.journal.entries) {
12031
- const sql4 = await readMigrationSql(dir, entry);
12032
- if (checksumOf(sql4) !== entry.checksum) {
12033
- log(`${MIGRATIONS_DIRNAME}/${entry.name}.sql does not match its journal checksum \u2014 committed migrations must not be edited`);
12034
- return 1;
12035
- }
12036
- const snapshotText = await readSnapshotText(dir, entry.ordinal);
12037
- if (checksumOf(snapshotText) !== entry.snapshot_checksum) {
12038
- log(`the snapshot for ${entry.name} does not match its journal checksum \u2014 committed snapshots must not be edited (the next diff builds on it)`);
12039
- return 1;
12040
- }
12041
- const snapshot = JSON.parse(snapshotText);
12042
- if (entry.ordinal > 1 && snapshot.prevId !== previousId3) {
12043
- log(`snapshot chain broken at ${entry.name} \u2014 regenerate the workspace with \`seq-studio orm diff\``);
12044
- return 1;
12045
- }
12046
- if (entry.previous_content_hash !== previousHash) {
12047
- log(`content-hash chain broken at ${entry.name} \u2014 migrations must be appended in order by \`seq-studio orm diff\``);
12048
- return 1;
12049
- }
12050
- previousId3 = snapshot.id;
12051
- previousHash = entry.content_hash;
12092
+ const broken = await verifyCommittedLedger(dir, workspace.journal.entries);
12093
+ if (broken) {
12094
+ log(broken);
12095
+ return 1;
12052
12096
  }
12053
12097
  if (tip.content_hash !== loaded.hash) {
12054
12098
  log(`definitions changed since ${tip.name} \u2014 run \`seq-studio orm diff\` and commit the new migration`);
@@ -12162,8 +12206,9 @@ async function applyV2Command(rest, dir) {
12162
12206
  const applied = await apiRequestStrict("POST", `/api/orm/namespaces/${namespace}/apply`, {
12163
12207
  version_id: registered.version_id,
12164
12208
  ...allowDestructive ? { allow_destructive: true } : {},
12165
- ...schemaAlreadyActive && registered.operation_set_id ? { operation_set_id: registered.operation_set_id } : {}
12166
- });
12209
+ ...schemaAlreadyActive && registered.operation_set_id ? { operation_set_id: registered.operation_set_id } : {},
12210
+ ...applySourceFromEnv()
12211
+ }, applySourceHeaders());
12167
12212
  const live = { namespace, contentHash: registered.content_hash };
12168
12213
  if ("ops_only" in applied) {
12169
12214
  console.log(
@@ -12619,8 +12664,9 @@ async function applyCommand(rest) {
12619
12664
  if (dryRun) return dryRunRehearsal({ namespace, versionId: registered.version_id, contentHash: registered.content_hash });
12620
12665
  const applied = await apiRequestStrict("POST", `/api/orm/namespaces/${namespace}/apply`, {
12621
12666
  version_id: registered.version_id,
12622
- ...allowDestructive ? { allow_destructive: true } : {}
12623
- });
12667
+ ...allowDestructive ? { allow_destructive: true } : {},
12668
+ ...applySourceFromEnv()
12669
+ }, applySourceHeaders());
12624
12670
  if ("status" in applied) {
12625
12671
  const verdict = await awaitQueuedApply({ dir, namespace, contentHash: registered.content_hash, runId: applied.run_id, applyWait });
12626
12672
  return verdict === "live" ? writeGeneratedTypes(dir, loaded) : verdict;
@@ -12765,7 +12811,7 @@ async function apiRequest(method, path, body, options) {
12765
12811
  if (!token) throw new Error("not authenticated \u2014 run: seq-studio login");
12766
12812
  const response = await fetch(`${baseUrl}${path}`, {
12767
12813
  method,
12768
- headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
12814
+ headers: { ...options?.headers, Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
12769
12815
  body: body === void 0 ? void 0 : JSON.stringify(body)
12770
12816
  });
12771
12817
  if (response.status === 404 && options?.allow404) return null;
@@ -12792,18 +12838,20 @@ function errorDetail(payload) {
12792
12838
  const fields = details && typeof details === "object" ? Object.entries(details).filter(([, value]) => typeof value === "string").map(([key, value]) => `${key}: ${String(value)}`) : [];
12793
12839
  return `${String(first.message)}${code ? ` (${code})` : ""}${fields.length ? ` [${fields.join(", ")}]` : ""}`;
12794
12840
  }
12795
- async function apiRequestStrict(method, path, body) {
12796
- const result = await apiRequest(method, path, body);
12841
+ async function apiRequestStrict(method, path, body, headers) {
12842
+ const result = await apiRequest(method, path, body, { headers });
12797
12843
  if (result === null) throw new Error(`${method} ${path} returned no payload`);
12798
12844
  return result;
12799
12845
  }
12800
12846
  async function fetchRegistryState(namespace, contentHash) {
12801
12847
  if (!process.env[BASE_URL_ENV]?.trim() || !await resolveToken()) return { kind: "offline" };
12802
- const body = await apiRequest("GET", `/api/orm/namespaces/${namespace}?hash=${encodeURIComponent(contentHash)}`, void 0, { allow404: true });
12848
+ const body = await apiRequest("GET", `/api/orm/namespaces/${namespace}?hash=${encodeURIComponent(contentHash)}&activations_limit=1`, void 0, { allow404: true });
12803
12849
  if (!body) return { kind: "missing" };
12850
+ const latest = body.activations?.[0];
12804
12851
  return {
12805
12852
  kind: "found",
12806
12853
  activeVersionId: body.namespace.active_version_id,
12854
+ activeActivation: latest && latest.id === body.namespace.active_activation_id ? latest : null,
12807
12855
  versions: body.versions,
12808
12856
  hashVersion: body.hash_version ?? null
12809
12857
  };
@@ -12818,6 +12866,6 @@ function positional(rest) {
12818
12866
  }
12819
12867
  var firstLine2 = (sql4) => sql4.split("\n")[0] ?? sql4;
12820
12868
 
12821
- export { BASE_URL_ENV, ORM_MODULE_PATH_ENV, TOKEN_ENV, errorDetail, parseApplyWait, runCli, setTokenProvider };
12869
+ export { BASE_URL_ENV, ORM_MODULE_PATH_ENV, SOURCE_AUTHORIZATION_ENV, SOURCE_ENV, TOKEN_ENV, UNVERIFIED_SOURCE_REASON_ENV, applySourceFromEnv, applySourceHeaders, describeActivation, errorDetail, parseApplyWait, runCli, setTokenProvider };
12822
12870
  //# sourceMappingURL=cli.js.map
12823
12871
  //# sourceMappingURL=cli.js.map