@sequenceholdings/orm 2.8.1 → 2.8.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.
package/dist/cli.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { readdir, readFile, mkdir, writeFile, stat, rm, realpath } from 'fs/promises';
2
2
  import { createRequire } from 'module';
3
3
  import { basename, resolve, join, dirname, sep, extname } from 'path';
4
+ import { text } from 'stream/consumers';
4
5
  import { fileURLToPath, pathToFileURL } from 'url';
5
6
  import { createJiti } from 'jiti';
6
7
  import { createHash } from 'crypto';
@@ -8,7 +9,7 @@ import { z } from 'zod';
8
9
  import { buildSchema, parse, validate, Kind, valueFromASTUntyped, visit, print } from 'graphql';
9
10
  import { PgParser } from '@supabase/pg-parser';
10
11
  import { sql } from 'drizzle-orm';
11
- import { pgSchema, PgDialect, QueryBuilder, getViewConfig, extractUsedTable, check, foreignKey, uniqueIndex, index, primaryKey, jsonb, date, timestamp, boolean, doublePrecision, numeric, bigint, integer, text, uuid } from 'drizzle-orm/pg-core';
12
+ import { pgSchema, PgDialect, QueryBuilder, getViewConfig, extractUsedTable, check, foreignKey, uniqueIndex, index, primaryKey, jsonb, date, timestamp, boolean, doublePrecision, numeric, bigint, integer, text as text$1, uuid } from 'drizzle-orm/pg-core';
12
13
  import { generateDrizzleJson, upPgSnapshot, generateMigration } from 'drizzle-kit/api';
13
14
  import { load } from 'js-yaml';
14
15
  import { capabilityPinsSchema, customRolesBlockSchema, validateCapabilityPins } from '@sequenceholdings/custom-roles-manifest';
@@ -5461,8 +5462,8 @@ var SqlEmitter = class {
5461
5462
  }
5462
5463
  const result = node.op === "SETOP_UNION" ? this.union(node, scope) : this.projection(node, scope);
5463
5464
  const { columns } = result;
5464
- let text4 = `${ctes.length ? `WITH ${ctes.join(", ")} ` : ""}${result.text}`;
5465
- if (list(node.sortClause).length) text4 += ` ORDER BY ${list(node.sortClause).map((n) => {
5465
+ let text5 = `${ctes.length ? `WITH ${ctes.join(", ")} ` : ""}${result.text}`;
5466
+ if (list(node.sortClause).length) text5 += ` ORDER BY ${list(node.sortClause).map((n) => {
5466
5467
  const sort = obj(obj(n).SortBy);
5467
5468
  only(sort, ["node", "sortby_dir", "sortby_nulls", "location"]);
5468
5469
  const direction = sort.sortby_dir === "SORTBY_DESC" ? "DESC" : sort.sortby_dir === "SORTBY_ASC" || sort.sortby_dir === "SORTBY_DEFAULT" ? "ASC" : fail3("unsupported SQL sort");
@@ -5473,10 +5474,10 @@ var SqlEmitter = class {
5473
5474
  return `${projected.length === 1 ? quoteIdentifier(projected[0].name) : this.expr(value, scope).text} ${direction}${nulls}`;
5474
5475
  }).join(", ")}`;
5475
5476
  for (const [key, keyword] of [["limitCount", "LIMIT"], ["limitOffset", "OFFSET"]]) {
5476
- if (node[key]) text4 += ` ${keyword} ${this.expr(obj(node[key]), scope).text}`;
5477
+ if (node[key]) text5 += ` ${keyword} ${this.expr(obj(node[key]), scope).text}`;
5477
5478
  }
5478
5479
  this.depth--;
5479
- return { text: text4, columns: resolveUnknown ? columns.map((c) => c.type === "unknown" ? { ...c, type: "text" } : c) : columns };
5480
+ return { text: text5, columns: resolveUnknown ? columns.map((c) => c.type === "unknown" ? { ...c, type: "text" } : c) : columns };
5480
5481
  }
5481
5482
  union(node, scope) {
5482
5483
  for (const key of ["targetList", "fromClause", "whereClause", "groupClause", "havingClause"]) {
@@ -5517,11 +5518,11 @@ var SqlEmitter = class {
5517
5518
  }
5518
5519
  }
5519
5520
  if (!targets.length || targets.length > 256) fail3("invalid SQL projection");
5520
- let text4 = `SELECT ${targets.join(", ")}${from ? ` FROM ${from}` : ""}`;
5521
- if (node.whereClause) text4 += ` WHERE ${this.expr(obj(node.whereClause), scope).text}`;
5522
- if (list(node.groupClause).length) text4 += ` GROUP BY ${list(node.groupClause).map((n) => this.expr(obj(n), scope).text).join(", ")}`;
5523
- if (node.havingClause) text4 += ` HAVING ${this.expr(obj(node.havingClause), scope).text}`;
5524
- return { text: text4, columns };
5521
+ let text5 = `SELECT ${targets.join(", ")}${from ? ` FROM ${from}` : ""}`;
5522
+ if (node.whereClause) text5 += ` WHERE ${this.expr(obj(node.whereClause), scope).text}`;
5523
+ if (list(node.groupClause).length) text5 += ` GROUP BY ${list(node.groupClause).map((n) => this.expr(obj(n), scope).text).join(", ")}`;
5524
+ if (node.havingClause) text5 += ` HAVING ${this.expr(obj(node.havingClause), scope).text}`;
5525
+ return { text: text5, columns };
5525
5526
  }
5526
5527
  from(node, scope) {
5527
5528
  this.budget();
@@ -6004,7 +6005,7 @@ function generateSqlOperations({ artifact }) {
6004
6005
  lines.push(
6005
6006
  `export type ${prefix}Variables = SqlQueryVariables`,
6006
6007
  `export type ${prefix}Result = SqlQueryResult`,
6007
- `export const ${prefix}: OperationReference<${prefix}Variables, ${prefix}Result> = ${JSON.stringify({ namespace: artifact.namespace, hash: operation.opHash })}`,
6008
+ `export const ${prefix}: OperationReference<${prefix}Variables, ${prefix}Result> = ${JSON.stringify({ namespace: artifact.namespace, name: operation.name, kind: operation.kind })}`,
6008
6009
  ""
6009
6010
  );
6010
6011
  continue;
@@ -6019,7 +6020,7 @@ function generateSqlOperations({ artifact }) {
6019
6020
  ${variables}
6020
6021
  }`,
6021
6022
  `export type ${prefix}Result = { columns: Array<{ name: string; type: string; nullable: boolean }>; rows: Array<[${row}]>; truncated: boolean }`,
6022
- `export const ${prefix}: OperationReference<${prefix}Variables, ${prefix}Result> = ${JSON.stringify({ namespace: artifact.namespace, hash: operation.opHash })}`,
6023
+ `export const ${prefix}: OperationReference<${prefix}Variables, ${prefix}Result> = ${JSON.stringify({ namespace: artifact.namespace, name: operation.name, kind: operation.kind })}`,
6023
6024
  ""
6024
6025
  );
6025
6026
  }
@@ -6080,7 +6081,7 @@ function isSeqView(value) {
6080
6081
  }
6081
6082
  var QUERY_COLUMN_FACTORIES = {
6082
6083
  uuid: (name2) => uuid(name2),
6083
- text: (name2) => text(name2),
6084
+ text: (name2) => text$1(name2),
6084
6085
  integer: (name2) => integer(name2),
6085
6086
  bigint: (name2) => bigint(name2, { mode: "bigint" }),
6086
6087
  numeric: (name2) => numeric(name2),
@@ -6809,29 +6810,23 @@ var CodegenGqlError = class extends Error {
6809
6810
  this.name = "CodegenGqlError";
6810
6811
  }
6811
6812
  };
6812
- var MANIFEST_VERSION = 1;
6813
- var OP_HASH_RE = /^sha256:[0-9a-f]{64}$/;
6813
+ var MANIFEST_VERSION = 2;
6814
6814
  var GRAPHQL_NAME_RE = /^[_A-Za-z][_0-9A-Za-z]*$/;
6815
6815
  var manifestRequiresSchema = z.object({
6816
6816
  capability: z.string().min(1).optional(),
6817
6817
  role: z.string().min(1).optional()
6818
6818
  }).nullable();
6819
6819
  var manifestOperationSchema = z.object({
6820
- hash: z.string().regex(OP_HASH_RE),
6821
6820
  kind: z.enum(["query", "mutation"]),
6822
6821
  requires: manifestRequiresSchema
6823
6822
  });
6824
6823
  var operationsManifestSchema = z.object({
6825
- manifestVersion: z.literal(MANIFEST_VERSION),
6826
- schemaHash: z.string().min(1),
6827
- opsHash: z.string().regex(OP_HASH_RE),
6824
+ manifestVersion: z.union([z.literal(1), z.literal(MANIFEST_VERSION)]),
6828
6825
  operations: z.record(z.string().regex(GRAPHQL_NAME_RE), manifestOperationSchema)
6829
6826
  });
6830
6827
  function buildOperationsManifest({
6831
- schemaHash,
6832
6828
  operations
6833
6829
  }) {
6834
- if (schemaHash.length === 0) throw new CodegenGqlError("schemaHash must be a non-empty string.");
6835
6830
  const sorted = [...operations].sort((a, b) => a.name.localeCompare(b.name));
6836
6831
  const seen = /* @__PURE__ */ new Set();
6837
6832
  const entries = sorted.map((op) => {
@@ -6842,20 +6837,14 @@ function buildOperationsManifest({
6842
6837
  throw new CodegenGqlError(`Duplicate operation name "${op.name}" \u2014 operation names must be unique per namespace.`);
6843
6838
  }
6844
6839
  seen.add(op.name);
6845
- if (!OP_HASH_RE.test(op.hash)) {
6846
- throw new CodegenGqlError(`Operation "${op.name}" has malformed hash "${op.hash}" (expected sha256:<64 hex>).`);
6847
- }
6848
- return { name: op.name, hash: op.hash, kind: op.kind, requires: normalizeRequires(op.requires) };
6840
+ return { name: op.name, kind: op.kind, requires: normalizeRequires(op.requires) };
6849
6841
  });
6850
- const opsHash = `sha256:${createHash("sha256").update(stableJson(entries)).digest("hex")}`;
6851
6842
  const operationsRecord = {};
6852
6843
  for (const entry of entries) {
6853
- operationsRecord[entry.name] = { hash: entry.hash, kind: entry.kind, requires: entry.requires };
6844
+ operationsRecord[entry.name] = { kind: entry.kind, requires: entry.requires };
6854
6845
  }
6855
6846
  return {
6856
6847
  manifestVersion: MANIFEST_VERSION,
6857
- schemaHash,
6858
- opsHash,
6859
6848
  operations: operationsRecord
6860
6849
  };
6861
6850
  }
@@ -7130,8 +7119,8 @@ function renderEnum(name2, values) {
7130
7119
  ${values.map((value) => ` ${value}`).join("\n")}
7131
7120
  }`;
7132
7121
  }
7133
- function descriptionLines(text4, indent) {
7134
- const escaped = text4.replace(/"""/g, '\\"""');
7122
+ function descriptionLines(text5, indent) {
7123
+ const escaped = text5.replace(/"""/g, '\\"""');
7135
7124
  return [`${indent}"""`, ...escaped.split("\n").map((line) => `${indent}${line}`), `${indent}"""`];
7136
7125
  }
7137
7126
  var PATTERN_COMPARISON_TYPES = /* @__PURE__ */ new Set(["String", "Int", "bigint", "uuid"]);
@@ -8466,7 +8455,7 @@ function comparisonTypeIssue(left, right, columns) {
8466
8455
  }
8467
8456
  var FACTORIES = {
8468
8457
  uuid: (name2) => uuid(name2),
8469
- text: (name2) => text(name2),
8458
+ text: (name2) => text$1(name2),
8470
8459
  integer: (name2) => integer(name2),
8471
8460
  // Mode 'bigint' keeps int8 lossless — mode 'number' silently rounds past
8472
8461
  // 2^53. The read executor serializes BigInt to string for JSON.
@@ -8850,7 +8839,7 @@ function refreshFunctionStatements({ namespace, previous, current }) {
8850
8839
  // src/engine/canonical2.ts
8851
8840
  var FACTORIES2 = {
8852
8841
  uuid: (name2) => uuid(name2),
8853
- text: (name2) => text(name2),
8842
+ text: (name2) => text$1(name2),
8854
8843
  integer: (name2) => integer(name2),
8855
8844
  // Mode 'bigint' keeps int8 lossless — mode 'number' silently rounds past
8856
8845
  // 2^53. The read executor serializes BigInt to string for JSON.
@@ -10302,7 +10291,7 @@ async function readMigrationSql(dir, entry) {
10302
10291
  throw new OrmDefinitionError("missing migration file", path);
10303
10292
  });
10304
10293
  }
10305
- var checksumOf = (text4) => createHash("sha256").update(text4).digest("hex");
10294
+ var checksumOf = (text5) => createHash("sha256").update(text5).digest("hex");
10306
10295
  var migrationName = (ordinal, contentHash) => `${pad(ordinal)}_${contentHash.slice(0, 12)}`;
10307
10296
  function renderMigrationSql({
10308
10297
  name: name2,
@@ -10671,10 +10660,10 @@ function createOrmMigrationExport({ schemaHash, payload }) {
10671
10660
  return { ...contents, checksum: checksum(contents) };
10672
10661
  }
10673
10662
  function renderOrmMigrationExport(exported) {
10674
- const text4 = `${JSON.stringify(exported, null, 2)}
10663
+ const text5 = `${JSON.stringify(exported, null, 2)}
10675
10664
  `;
10676
- if (Buffer.byteLength(text4, "utf8") > ORM_MIGRATION_EXPORT_MAX_BYTES) throw new Error("ORM migration export exceeds the size limit");
10677
- return text4;
10665
+ if (Buffer.byteLength(text5, "utf8") > ORM_MIGRATION_EXPORT_MAX_BYTES) throw new Error("ORM migration export exceeds the size limit");
10666
+ return text5;
10678
10667
  }
10679
10668
 
10680
10669
  // src/compiler-gql/role-warnings.ts
@@ -10894,7 +10883,7 @@ function renderImportScript({
10894
10883
  const procedure = renderProcedure({ targetSql, sourceSql, mapped, keys, onConflict, batchSize });
10895
10884
  const call = `CALL pg_temp.seq_orm_import(${resume.join(", ")});`;
10896
10885
  const omittedLine = omitted.length > 0 ? `-- Omitted columns keep their database default or NULL: ${omitted.join(", ")}` : "-- Every column is mapped.";
10897
- const text4 = [
10886
+ const text5 = [
10898
10887
  "-- Sequence ORM data import",
10899
10888
  `-- namespace: ${namespace}`,
10900
10889
  `-- target: ${schemaName}.${table.table}`,
@@ -10918,7 +10907,7 @@ function renderImportScript({
10918
10907
  call,
10919
10908
  ""
10920
10909
  ].join("\n");
10921
- return { probeSql, text: text4 };
10910
+ return { probeSql, text: text5 };
10922
10911
  }
10923
10912
  function renderProbe({
10924
10913
  sourceSql,
@@ -11291,10 +11280,10 @@ async function readSpec(flags) {
11291
11280
  if (flags["--table"] || flags["--from"] || flags["--map"]) {
11292
11281
  return { error: "--spec replaces --table, --from, and --map" };
11293
11282
  }
11294
- const text4 = await readFile(resolve(flags["--spec"]), "utf8");
11283
+ const text5 = await readFile(resolve(flags["--spec"]), "utf8");
11295
11284
  let parsed;
11296
11285
  try {
11297
- parsed = JSON.parse(text4);
11286
+ parsed = JSON.parse(text5);
11298
11287
  } catch {
11299
11288
  return { error: `--spec ${flags["--spec"]} is not valid JSON` };
11300
11289
  }
@@ -11374,6 +11363,7 @@ var USAGE = `usage:
11374
11363
  --no-wait returns once the apply is queued
11375
11364
  seq-studio orm execute <operation> [dir] --variables-file <file> -e <env>
11376
11365
  execute one named persisted operation from this checkout's manifest as the signed-in caller
11366
+ variables-file accepts - or /dev/stdin to read JSON from stdin until EOF
11377
11367
  seq-studio orm execute <namespace> <operation> -e <env> [--variables-file <file>] [--var <name>=<value>]...
11378
11368
  execute the deployed version's operation by name (no checkout; seq-studio only)
11379
11369
  seq-studio orm diff [dir] write/verify the committed migration standalone (--check is the offline CI gate; --allow-destructive consents; --rebase records a v1 -> v2 conversion as a verified no-op)
@@ -11654,21 +11644,21 @@ async function generateCommand(rest) {
11654
11644
  schema_snapshot: workspace.tipSnapshot,
11655
11645
  ...migration ? { migration } : {}
11656
11646
  } });
11657
- const text4 = renderOrmMigrationExport(exported);
11647
+ const text5 = renderOrmMigrationExport(exported);
11658
11648
  const path = join(dir, ORM_MIGRATION_EXPORT_FILE);
11659
11649
  if (check) {
11660
11650
  const committed = await readFile(path, "utf8").catch((error) => {
11661
11651
  if (error instanceof Error && "code" in error && error.code === "ENOENT") return void 0;
11662
11652
  throw error;
11663
11653
  });
11664
- if (committed !== text4) {
11654
+ if (committed !== text5) {
11665
11655
  console.error(`${ORM_MIGRATION_EXPORT_FILE} is missing or stale; run seq-studio orm generate ${dir} --migration-export and commit it`);
11666
11656
  return 1;
11667
11657
  }
11668
11658
  console.log(`${ORM_MIGRATION_EXPORT_FILE} matches the current definitions, operations, and committed migration`);
11669
11659
  return 0;
11670
11660
  }
11671
- await writeFile(path, text4);
11661
+ await writeFile(path, text5);
11672
11662
  console.log(`wrote ${ORM_MIGRATION_EXPORT_FILE} (${exported.checksum.slice(0, 12)})`);
11673
11663
  }
11674
11664
  await writeGenerateOutputs(dir, compiled.project);
@@ -11685,11 +11675,10 @@ async function writeGenerateOutputs(dir, project) {
11685
11675
  `);
11686
11676
  const operations = Object.entries(manifest.operations).map(([name2, op]) => ({
11687
11677
  name: name2,
11688
- hash: op.opHash,
11689
11678
  kind: op.kind,
11690
11679
  requires: op.requires
11691
11680
  }));
11692
- const manifestFile = buildOperationsManifest({ schemaHash: manifest.schemaHash, operations });
11681
+ const manifestFile = buildOperationsManifest({ operations });
11693
11682
  await writeFile(join(dir, OPERATIONS_MANIFEST_FILE), `${JSON.stringify(manifestFile, null, 2)}
11694
11683
  `);
11695
11684
  await writeFile(join(dir, TYPE_POLICIES_FILE), generateTypePolicies(loaded.canonical));
@@ -11760,7 +11749,8 @@ function parseExecuteArgs(rest) {
11760
11749
  async function readExecuteVariables(variablesFile) {
11761
11750
  let parsed;
11762
11751
  try {
11763
- parsed = JSON.parse(await readFile(resolve(variablesFile), "utf8"));
11752
+ const contents = variablesFile === "-" || variablesFile === "/dev/stdin" ? await text(process.stdin) : await readFile(resolve(variablesFile), "utf8");
11753
+ parsed = JSON.parse(contents);
11764
11754
  } catch (error) {
11765
11755
  throw new OrmDefinitionError(
11766
11756
  `could not read JSON variables from ${variablesFile}: ${error instanceof Error ? error.message : String(error)}`
@@ -11802,13 +11792,12 @@ async function executeV2Command(rest) {
11802
11792
  const namespace = loaded.canonical.namespace;
11803
11793
  let response;
11804
11794
  try {
11805
- response = await apiRequestStrict("POST", `${DATA_ENDPOINT_PREFIX}/${namespace}`, { op: selected.hash, variables });
11795
+ response = await apiRequestStrict("POST", `${DATA_ENDPOINT_PREFIX}/${namespace}`, { op: `${selected.kind}:${operation}`, expectedKind: selected.kind, variables });
11806
11796
  } catch (error) {
11807
11797
  if (!(error instanceof AtlasRequestError) || error.status !== 404) throw error;
11808
11798
  const diagnosis = await diagnoseUnknownOperation({
11809
11799
  namespace,
11810
11800
  operation,
11811
- localHash: selected.hash,
11812
11801
  retry: `seq-studio orm execute ${namespace} ${operation}${env ? ` -e ${env}` : ""} --variables-file ${variablesFile}`
11813
11802
  }).catch(() => null);
11814
11803
  throw diagnosis ? new OrmDefinitionError(diagnosis) : error;
@@ -11816,7 +11805,7 @@ async function executeV2Command(rest) {
11816
11805
  console.log(JSON.stringify(response, null, 2));
11817
11806
  return 0;
11818
11807
  }
11819
- async function diagnoseUnknownOperation({ namespace, operation, localHash, retry }) {
11808
+ async function diagnoseUnknownOperation({ namespace, operation, retry }) {
11820
11809
  const encoded = encodeURIComponent(namespace);
11821
11810
  const listing = await apiRequest(
11822
11811
  "GET",
@@ -11839,15 +11828,10 @@ async function diagnoseUnknownOperation({ namespace, operation, localHash, retry
11839
11828
  if (!deployed) {
11840
11829
  return `operation ${operation} is not in ${namespace}'s deployed version ${listing.schema_version_id}. Apply this checkout to deploy it (seq-studio orm apply), or list the deployed operations: seq-studio orm describe ${namespace} --operations`;
11841
11830
  }
11842
- if (deployed.op_hash === localHash) return null;
11843
11831
  return [
11844
- `operation ${operation}: this checkout's ${OPERATIONS_MANIFEST_FILE} does not match ${namespace}'s deployed version.`,
11845
- ` local hash: ${localHash}`,
11846
- ` deployed hash: ${deployed.op_hash}`,
11847
- ` deployed version: ${listing.schema_version_id}`,
11848
- "Run the deployed operation by name (no checkout needed):",
11849
- ` ${retry}`,
11850
- "Or bring the checkout up to date (git pull, then seq-studio orm generate) and retry."
11832
+ `operation ${operation} exists in ${namespace}'s deployed version ${listing.schema_version_id}, but its guarded name was refused.`,
11833
+ "Update Atlas to a runtime that supports guarded operation names, or retry if the operation set changed during lookup.",
11834
+ ` ${retry}`
11851
11835
  ].join("\n");
11852
11836
  }
11853
11837
  async function validateV2Command(dir) {