@sequenceholdings/orm 2.7.1 → 2.8.1
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/README.md +18 -4
- package/dist/authoring.cjs +21 -0
- package/dist/authoring.cjs.map +1 -1
- package/dist/authoring.d.cts +1 -1
- package/dist/authoring.d.ts +1 -1
- package/dist/authoring.js +21 -0
- package/dist/authoring.js.map +1 -1
- package/dist/canonical.d.cts +71 -71
- package/dist/canonical.d.ts +71 -71
- package/dist/cli.cjs +3460 -3337
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.d.cts +13 -1
- package/dist/cli.d.ts +13 -1
- package/dist/cli.js +3459 -3338
- package/dist/cli.js.map +1 -1
- package/dist/client.cjs +24 -6
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +12 -3
- package/dist/client.d.ts +12 -3
- package/dist/client.js +24 -6
- package/dist/client.js.map +1 -1
- package/dist/compiler.cjs +844 -15
- package/dist/compiler.cjs.map +1 -1
- package/dist/compiler.d.cts +12 -2
- package/dist/compiler.d.ts +12 -2
- package/dist/compiler.js +844 -17
- package/dist/compiler.js.map +1 -1
- package/dist/index.cjs +21 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +57 -57
- package/dist/index.d.ts +57 -57
- package/dist/index.js +21 -0
- package/dist/index.js.map +1 -1
- package/dist/ir.d.cts +72 -72
- package/dist/ir.d.ts +72 -72
- package/dist/sdl.cjs +3 -3
- package/dist/sdl.cjs.map +1 -1
- package/dist/sdl.d.cts +2 -0
- package/dist/sdl.d.ts +2 -0
- package/dist/sdl.js +3 -3
- package/dist/sdl.js.map +1 -1
- package/package.json +1 -1
- package/templates/orm-namespace/graphql/example/queries.ts +52 -5
- package/templates/orm-namespace/schema/tables.ts +5 -2
- package/templates/orm-namespace/sequence.config.ts +6 -4
- package/dist/{definition-DjfC7Lll.d.cts → definition-B99BsOAO.d.cts} +4 -4
- package/dist/{definition-DjfC7Lll.d.ts → definition-B99BsOAO.d.ts} +4 -4
package/dist/compiler.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'crypto';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import { Kind, print, visit, buildSchema, parse, validate, valueFromASTUntyped } from 'graphql';
|
|
3
|
+
import { Kind, print, visit, buildSchema, parse, validate, GraphQLObjectType, valueFromASTUntyped, getNamedType, GraphQLScalarType, GraphQLEnumType, GraphQLInputObjectType, GraphQLNonNull, GraphQLList } from 'graphql';
|
|
4
4
|
import { PgParser } from '@supabase/pg-parser';
|
|
5
5
|
|
|
6
6
|
// src/compiler-gql/artifact.ts
|
|
@@ -1839,9 +1839,15 @@ function typeName(table) {
|
|
|
1839
1839
|
if (last === void 0) return pascalCase(table);
|
|
1840
1840
|
return [...segments.slice(0, -1), singularizeWord(last)].map(capitalizeFirst).join("");
|
|
1841
1841
|
}
|
|
1842
|
+
function fieldName(column) {
|
|
1843
|
+
return camelCase(column);
|
|
1844
|
+
}
|
|
1842
1845
|
function enumTypeName(name2) {
|
|
1843
1846
|
return pascalCase(name2);
|
|
1844
1847
|
}
|
|
1848
|
+
function comparisonExpName(gqlTypeName) {
|
|
1849
|
+
return `${capitalizeFirst(gqlTypeName)}ComparisonExp`;
|
|
1850
|
+
}
|
|
1845
1851
|
var SCALAR_FOR_COLUMN_TYPE = {
|
|
1846
1852
|
uuid: "uuid",
|
|
1847
1853
|
text: "String",
|
|
@@ -1865,7 +1871,19 @@ function columnTypeForScalar(scalarName) {
|
|
|
1865
1871
|
return COLUMN_TYPE_BY_SCALAR.get(scalarName);
|
|
1866
1872
|
}
|
|
1867
1873
|
var VARIABLE_SCALAR_NAMES = [...COLUMN_TYPE_BY_SCALAR.keys()];
|
|
1874
|
+
var CUSTOM_SCALARS = ["bigint", "date", "jsonb", "numeric", "timestamptz", "uuid"];
|
|
1875
|
+
var NUMERICISH_COLUMN_TYPES = ["integer", "bigint", "numeric", "double"];
|
|
1876
|
+
var ORDER_BY_ENUM = "OrderBy";
|
|
1877
|
+
var ORDER_BY_ENUM_VALUES = [
|
|
1878
|
+
"ASC",
|
|
1879
|
+
"ASC_NULLS_FIRST",
|
|
1880
|
+
"ASC_NULLS_LAST",
|
|
1881
|
+
"DESC",
|
|
1882
|
+
"DESC_NULLS_FIRST",
|
|
1883
|
+
"DESC_NULLS_LAST"
|
|
1884
|
+
];
|
|
1868
1885
|
var PLATFORM_USER_TYPENAME = "PlatformUser";
|
|
1886
|
+
var DATE_BUCKET_ENUM = "DateBucket";
|
|
1869
1887
|
function tableNames(table) {
|
|
1870
1888
|
const root = camelCase(table);
|
|
1871
1889
|
const rootPascal = pascalCase(table);
|
|
@@ -3478,8 +3496,8 @@ var COMPILER_MAJOR = 1;
|
|
|
3478
3496
|
var OP_HASH_DOMAIN = "sequence-orm-op\0";
|
|
3479
3497
|
var OPS_HASH_DOMAIN = "sequence-orm-opset\0";
|
|
3480
3498
|
function canonicalOperationText(definition) {
|
|
3481
|
-
const
|
|
3482
|
-
return print(
|
|
3499
|
+
const document2 = { kind: Kind.DOCUMENT, definitions: [definition] };
|
|
3500
|
+
return print(document2);
|
|
3483
3501
|
}
|
|
3484
3502
|
function computeOpHash({
|
|
3485
3503
|
definition,
|
|
@@ -4727,9 +4745,9 @@ function parseDocuments({
|
|
|
4727
4745
|
const parsed = [];
|
|
4728
4746
|
const diagnostics = [];
|
|
4729
4747
|
for (const source of documents) {
|
|
4730
|
-
let
|
|
4748
|
+
let document2;
|
|
4731
4749
|
try {
|
|
4732
|
-
|
|
4750
|
+
document2 = parse(source.contents);
|
|
4733
4751
|
} catch (error) {
|
|
4734
4752
|
diagnostics.push({
|
|
4735
4753
|
severity: "error",
|
|
@@ -4739,7 +4757,7 @@ function parseDocuments({
|
|
|
4739
4757
|
});
|
|
4740
4758
|
continue;
|
|
4741
4759
|
}
|
|
4742
|
-
const validationErrors = validate(schema,
|
|
4760
|
+
const validationErrors = validate(schema, document2);
|
|
4743
4761
|
if (validationErrors.length > 0) {
|
|
4744
4762
|
for (const validationError of validationErrors) {
|
|
4745
4763
|
diagnostics.push({
|
|
@@ -4753,11 +4771,11 @@ function parseDocuments({
|
|
|
4753
4771
|
}
|
|
4754
4772
|
const operations = [];
|
|
4755
4773
|
const fragments = /* @__PURE__ */ new Map();
|
|
4756
|
-
for (const definition of
|
|
4774
|
+
for (const definition of document2.definitions) {
|
|
4757
4775
|
if (definition.kind === Kind.OPERATION_DEFINITION) operations.push(definition);
|
|
4758
4776
|
if (definition.kind === Kind.FRAGMENT_DEFINITION) fragments.set(definition.name.value, definition);
|
|
4759
4777
|
}
|
|
4760
|
-
parsed.push({ path: source.path, document, operations, fragments });
|
|
4778
|
+
parsed.push({ path: source.path, document: document2, operations, fragments });
|
|
4761
4779
|
}
|
|
4762
4780
|
return { documents: parsed, diagnostics };
|
|
4763
4781
|
}
|
|
@@ -4955,12 +4973,12 @@ function compileNamespace({ canonical, sdl, documents }) {
|
|
|
4955
4973
|
diagnostics.push(...parsed.diagnostics);
|
|
4956
4974
|
const operations = {};
|
|
4957
4975
|
const operationSources = /* @__PURE__ */ new Map();
|
|
4958
|
-
for (const
|
|
4959
|
-
for (const operationNode of
|
|
4976
|
+
for (const document2 of parsed.documents) {
|
|
4977
|
+
for (const operationNode of document2.operations) {
|
|
4960
4978
|
const operationName = operationNode.name?.value;
|
|
4961
4979
|
try {
|
|
4962
4980
|
const restricted = restrictOperation({ operation: operationNode, ctx });
|
|
4963
|
-
const flattened = flattenOperation({ operation: restricted.definition, fragments:
|
|
4981
|
+
const flattened = flattenOperation({ operation: restricted.definition, fragments: document2.fragments });
|
|
4964
4982
|
const previousSource = operationSources.get(restricted.name);
|
|
4965
4983
|
if (previousSource !== void 0) {
|
|
4966
4984
|
throw new DocumentCompileError(
|
|
@@ -4992,14 +5010,14 @@ function compileNamespace({ canonical, sdl, documents }) {
|
|
|
4992
5010
|
tableSet,
|
|
4993
5011
|
roots
|
|
4994
5012
|
};
|
|
4995
|
-
operationSources.set(restricted.name,
|
|
5013
|
+
operationSources.set(restricted.name, document2.path);
|
|
4996
5014
|
} catch (error) {
|
|
4997
5015
|
if (error instanceof DocumentCompileError) {
|
|
4998
5016
|
diagnostics.push({
|
|
4999
5017
|
severity: "error",
|
|
5000
5018
|
code: error.code,
|
|
5001
5019
|
message: error.message,
|
|
5002
|
-
document:
|
|
5020
|
+
document: document2.path,
|
|
5003
5021
|
...operationName !== void 0 ? { operation: operationName } : {}
|
|
5004
5022
|
});
|
|
5005
5023
|
continue;
|
|
@@ -5427,6 +5445,815 @@ function validateOnConflictInput({
|
|
|
5427
5445
|
return finish(collector, { constraint, updateColumns });
|
|
5428
5446
|
}
|
|
5429
5447
|
|
|
5448
|
+
// src/sdl/generate.ts
|
|
5449
|
+
var SdlGenerationError = class extends Error {
|
|
5450
|
+
constructor(message) {
|
|
5451
|
+
super(message);
|
|
5452
|
+
this.name = "SdlGenerationError";
|
|
5453
|
+
}
|
|
5454
|
+
};
|
|
5455
|
+
var GRAPHQL_NAME_RE = /^[_A-Za-z][_0-9A-Za-z]*$/;
|
|
5456
|
+
var RESERVED_ENUM_VALUES = /* @__PURE__ */ new Set(["true", "false", "null"]);
|
|
5457
|
+
var NUMERICISH = new Set(NUMERICISH_COLUMN_TYPES);
|
|
5458
|
+
var PLATFORM_USER_FIELDS = [
|
|
5459
|
+
{ name: "sub", gqlType: "String", nullable: false },
|
|
5460
|
+
{ name: "email", gqlType: "String", nullable: true },
|
|
5461
|
+
{ name: "name", gqlType: "String", nullable: true },
|
|
5462
|
+
{ name: "organization", gqlType: "String", nullable: true },
|
|
5463
|
+
{ name: "role", gqlType: "String", nullable: true },
|
|
5464
|
+
{ name: "managerId", gqlType: "String", nullable: true },
|
|
5465
|
+
{ name: "accountEnabled", gqlType: "Boolean", nullable: false }
|
|
5466
|
+
];
|
|
5467
|
+
function generateSdl(canonical, options = {}) {
|
|
5468
|
+
const def = composeNamespace(normalizeCanonical(canonicalDefinitionSchema.parse(canonical))).definition;
|
|
5469
|
+
const exposed = canonicalRelations(def).filter((table) => table.internal !== true && (table.source === void 0 || options.includeSyncedReads === true && (table.primaryKey?.length ?? 0) > 0));
|
|
5470
|
+
const cellsByTable = new Map(exposed.map((table) => [table.table, trackedCells(table)]));
|
|
5471
|
+
const tables = exposed.map((table) => ({ ...table, columns: table.columns.filter((column) => column.cell === void 0) }));
|
|
5472
|
+
if (tables.length === 0) {
|
|
5473
|
+
throw new SdlGenerationError(
|
|
5474
|
+
`Namespace "${def.namespace}" exposes no relations (synced tables are excluded from SDL in v2.0) \u2014 nothing to generate.`
|
|
5475
|
+
);
|
|
5476
|
+
}
|
|
5477
|
+
for (const table of tables) {
|
|
5478
|
+
if (table.columns.length === 0) {
|
|
5479
|
+
throw new SdlGenerationError(`Relation "${table.table}" has no columns \u2014 cannot generate an object type.`);
|
|
5480
|
+
}
|
|
5481
|
+
}
|
|
5482
|
+
const namesByTable = new Map(tables.map((table) => [table.table, tableNames(table.table)]));
|
|
5483
|
+
const enumByName = new Map(def.enums.map((entry) => [entry.name, entry]));
|
|
5484
|
+
const hasPlatformUser = tables.some((table) => table.links.some((link) => link.kind === "toPlatformUser"));
|
|
5485
|
+
const usedEnums = /* @__PURE__ */ new Set();
|
|
5486
|
+
for (const table of tables) {
|
|
5487
|
+
for (const column of table.columns) {
|
|
5488
|
+
if (typeof column.type === "object") {
|
|
5489
|
+
if (!enumByName.has(column.type.enum)) {
|
|
5490
|
+
throw new SdlGenerationError(
|
|
5491
|
+
`Column "${table.table}.${column.columnName}" references undeclared enum "${column.type.enum}".`
|
|
5492
|
+
);
|
|
5493
|
+
}
|
|
5494
|
+
usedEnums.add(column.type.enum);
|
|
5495
|
+
}
|
|
5496
|
+
}
|
|
5497
|
+
}
|
|
5498
|
+
const aggregateBoolExpTargets = /* @__PURE__ */ new Set();
|
|
5499
|
+
for (const table of tables) {
|
|
5500
|
+
for (const link of table.links) {
|
|
5501
|
+
if (link.kind === "toMany" && link.target !== void 0 && namesByTable.has(link.target)) {
|
|
5502
|
+
aggregateBoolExpTargets.add(link.target);
|
|
5503
|
+
}
|
|
5504
|
+
}
|
|
5505
|
+
}
|
|
5506
|
+
const usedComparisonScalars = /* @__PURE__ */ new Set();
|
|
5507
|
+
for (const table of tables) {
|
|
5508
|
+
for (const column of table.columns) {
|
|
5509
|
+
if (typeof column.type !== "object") usedComparisonScalars.add(graphqlScalarFor(column.type));
|
|
5510
|
+
}
|
|
5511
|
+
}
|
|
5512
|
+
if (hasPlatformUser) {
|
|
5513
|
+
usedComparisonScalars.add("String");
|
|
5514
|
+
usedComparisonScalars.add("Boolean");
|
|
5515
|
+
}
|
|
5516
|
+
if (aggregateBoolExpTargets.size > 0) usedComparisonScalars.add("Int");
|
|
5517
|
+
const blocks = [];
|
|
5518
|
+
blocks.push(
|
|
5519
|
+
`# Generated by @sequenceholdings/orm \u2014 GraphQL SDL for namespace "${def.namespace}".
|
|
5520
|
+
# Do not edit: regenerate with \`seq-studio orm generate\`.`
|
|
5521
|
+
);
|
|
5522
|
+
blocks.push("directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT");
|
|
5523
|
+
blocks.push(
|
|
5524
|
+
'"""Gates an operation behind a platform capability or custom role \u2014 checked by the data plane before execution."""\ndirective @requires(capability: String, role: String) on QUERY | MUTATION'
|
|
5525
|
+
);
|
|
5526
|
+
if (!options.omitReadBudgetDirective) blocks.push(
|
|
5527
|
+
'"""Sets a document-authored ceiling of 1\u201350001 rows for one flat select root. Runtime variables cannot raise it."""\ndirective @readBudget(maxRows: Int!) on QUERY'
|
|
5528
|
+
);
|
|
5529
|
+
blocks.push("directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT");
|
|
5530
|
+
blocks.push(
|
|
5531
|
+
'"""Abort the entire mutation unless this root affects exactly the document-authored number of rows. Mutation roots only; cannot be conditional."""\ndirective @assertAffectedRows(exactly: Int!) on FIELD'
|
|
5532
|
+
);
|
|
5533
|
+
if (tables.some((table) => table.resolved !== void 0)) blocks.push(...RESOLVED_DIRECTIVE_BLOCKS);
|
|
5534
|
+
for (const scalar of CUSTOM_SCALARS) blocks.push(`scalar ${scalar}`);
|
|
5535
|
+
blocks.push(...renderSharedCellTypes([...cellsByTable.values()]));
|
|
5536
|
+
blocks.push(renderEnum(ORDER_BY_ENUM, [...ORDER_BY_ENUM_VALUES]));
|
|
5537
|
+
blocks.push(renderEnum(DATE_BUCKET_ENUM, DATE_BUCKETS.map((bucket) => bucket.toUpperCase())));
|
|
5538
|
+
for (const enumName of [...usedEnums].sort()) {
|
|
5539
|
+
const entry = enumByName.get(enumName);
|
|
5540
|
+
if (!entry) continue;
|
|
5541
|
+
for (const value of entry.values) assertEnumValue(value, `enum "${enumName}"`);
|
|
5542
|
+
blocks.push(renderEnum(enumTypeName(enumName), entry.values));
|
|
5543
|
+
}
|
|
5544
|
+
const patternTypes = options.stringOnlyPatternOperators ? STRING_PATTERN_COMPARISON_TYPES : PATTERN_COMPARISON_TYPES;
|
|
5545
|
+
for (const scalar of [...usedComparisonScalars].sort()) {
|
|
5546
|
+
blocks.push(renderComparisonExp({ gqlTypeName: scalar, patternTypes }));
|
|
5547
|
+
}
|
|
5548
|
+
for (const enumName of [...usedEnums].sort()) {
|
|
5549
|
+
blocks.push(renderComparisonExp({ gqlTypeName: enumTypeName(enumName), patternTypes }));
|
|
5550
|
+
}
|
|
5551
|
+
if (hasPlatformUser) blocks.push(...renderPlatformUserBlocks());
|
|
5552
|
+
for (const table of tables) {
|
|
5553
|
+
const names = namesByTable.get(table.table);
|
|
5554
|
+
if (!names) continue;
|
|
5555
|
+
blocks.push(
|
|
5556
|
+
...renderTableBlocks({
|
|
5557
|
+
table,
|
|
5558
|
+
names,
|
|
5559
|
+
namesByTable,
|
|
5560
|
+
emitAggregateBoolExp: aggregateBoolExpTargets.has(table.table),
|
|
5561
|
+
pinned: options.pinnedColumns?.[table.table] ?? [],
|
|
5562
|
+
cells: cellsByTable.get(table.table) ?? null
|
|
5563
|
+
})
|
|
5564
|
+
);
|
|
5565
|
+
}
|
|
5566
|
+
blocks.push(renderQueryRoot(tables, namesByTable));
|
|
5567
|
+
const mutationRoot = renderMutationRoot(tables, namesByTable);
|
|
5568
|
+
if (mutationRoot !== null) blocks.push(mutationRoot);
|
|
5569
|
+
assertNoDuplicateTypeNames(blocks);
|
|
5570
|
+
return `${blocks.join("\n\n")}
|
|
5571
|
+
`;
|
|
5572
|
+
}
|
|
5573
|
+
var TYPE_DECLARATION_RE = /^(?:type|input|enum|scalar|interface|union)\s+([_A-Za-z][_0-9A-Za-z]*)/;
|
|
5574
|
+
function assertNoDuplicateTypeNames(blocks) {
|
|
5575
|
+
const seen = /* @__PURE__ */ new Set();
|
|
5576
|
+
for (const block of blocks) {
|
|
5577
|
+
const declared = TYPE_DECLARATION_RE.exec(block)?.[1];
|
|
5578
|
+
if (declared === void 0) continue;
|
|
5579
|
+
if (seen.has(declared)) {
|
|
5580
|
+
throw new SdlGenerationError(
|
|
5581
|
+
`Generated schema declares "${declared}" twice. A canonical enum or table name is PascalCased into this GraphQL type name and collides with another declaration (for example an enum named "date_bucket" renders as "DateBucket", the shared bucket enum). Rename the offending enum or table.`
|
|
5582
|
+
);
|
|
5583
|
+
}
|
|
5584
|
+
seen.add(declared);
|
|
5585
|
+
}
|
|
5586
|
+
}
|
|
5587
|
+
function assertEnumValue(value, context) {
|
|
5588
|
+
if (!GRAPHQL_NAME_RE.test(value) || RESERVED_ENUM_VALUES.has(value)) {
|
|
5589
|
+
throw new SdlGenerationError(
|
|
5590
|
+
`Value "${value}" in ${context} is not a valid GraphQL enum value (must match ${GRAPHQL_NAME_RE} and not be true/false/null).`
|
|
5591
|
+
);
|
|
5592
|
+
}
|
|
5593
|
+
}
|
|
5594
|
+
function renderEnum(name2, values) {
|
|
5595
|
+
return `enum ${name2} {
|
|
5596
|
+
${values.map((value) => ` ${value}`).join("\n")}
|
|
5597
|
+
}`;
|
|
5598
|
+
}
|
|
5599
|
+
function descriptionLines(text, indent) {
|
|
5600
|
+
const escaped = text.replace(/"""/g, '\\"""');
|
|
5601
|
+
return [`${indent}"""`, ...escaped.split("\n").map((line) => `${indent}${line}`), `${indent}"""`];
|
|
5602
|
+
}
|
|
5603
|
+
var PATTERN_COMPARISON_TYPES = /* @__PURE__ */ new Set(["String", "Int", "bigint", "uuid"]);
|
|
5604
|
+
var STRING_PATTERN_COMPARISON_TYPES = /* @__PURE__ */ new Set(["String"]);
|
|
5605
|
+
function renderComparisonExp({ gqlTypeName, patternTypes }) {
|
|
5606
|
+
const fields = [
|
|
5607
|
+
["_eq", gqlTypeName],
|
|
5608
|
+
["_neq", gqlTypeName],
|
|
5609
|
+
["_gt", gqlTypeName],
|
|
5610
|
+
["_gte", gqlTypeName],
|
|
5611
|
+
["_lt", gqlTypeName],
|
|
5612
|
+
["_lte", gqlTypeName],
|
|
5613
|
+
["_in", `[${gqlTypeName}!]`],
|
|
5614
|
+
["_nin", `[${gqlTypeName}!]`],
|
|
5615
|
+
["_isNull", "Boolean"]
|
|
5616
|
+
];
|
|
5617
|
+
if (patternTypes.has(gqlTypeName)) {
|
|
5618
|
+
fields.push(["_like", "String"], ["_ilike", "String"]);
|
|
5619
|
+
}
|
|
5620
|
+
if (gqlTypeName === "jsonb") {
|
|
5621
|
+
fields.push(["_contains", "jsonb"], ["_hasKey", "String"]);
|
|
5622
|
+
}
|
|
5623
|
+
fields.sort(([a], [b]) => a.localeCompare(b));
|
|
5624
|
+
const body = fields.map(([field, type]) => ` ${field}: ${type}`).join("\n");
|
|
5625
|
+
return `input ${comparisonExpName(gqlTypeName)} {
|
|
5626
|
+
${body}
|
|
5627
|
+
}`;
|
|
5628
|
+
}
|
|
5629
|
+
function renderPlatformUserBlocks() {
|
|
5630
|
+
const typeFields = PLATFORM_USER_FIELDS.map(
|
|
5631
|
+
(field) => ` ${field.name}: ${field.gqlType}${field.nullable ? "" : "!"}`
|
|
5632
|
+
).join("\n");
|
|
5633
|
+
const boolExpName = `${PLATFORM_USER_TYPENAME}BoolExp`;
|
|
5634
|
+
const boolFields = [
|
|
5635
|
+
` _and: [${boolExpName}!]`,
|
|
5636
|
+
` _not: ${boolExpName}`,
|
|
5637
|
+
` _or: [${boolExpName}!]`,
|
|
5638
|
+
...PLATFORM_USER_FIELDS.map((field) => ` ${field.name}: ${comparisonExpName(field.gqlType)}`)
|
|
5639
|
+
].join("\n");
|
|
5640
|
+
const orderFields = PLATFORM_USER_FIELDS.map((field) => ` ${field.name}: ${ORDER_BY_ENUM}`).join("\n");
|
|
5641
|
+
return [
|
|
5642
|
+
`"""Curated read-only view over platform users (plan \xA74b) \u2014 reachable only through \`toPlatformUser\` links."""
|
|
5643
|
+
type ${PLATFORM_USER_TYPENAME} {
|
|
5644
|
+
${typeFields}
|
|
5645
|
+
}`,
|
|
5646
|
+
`input ${boolExpName} {
|
|
5647
|
+
${boolFields}
|
|
5648
|
+
}`,
|
|
5649
|
+
`input ${PLATFORM_USER_TYPENAME}OrderBy {
|
|
5650
|
+
${orderFields}
|
|
5651
|
+
}`
|
|
5652
|
+
];
|
|
5653
|
+
}
|
|
5654
|
+
var RESOLVED_DIRECTIVE_BLOCKS = [
|
|
5655
|
+
'"""Refresh this resolved-table root from a prefetch source before reading. Key-addressed roots only; the source must support the root shape."""\ndirective @prefetch(source: String!, maxStaleness: String) repeatable on FIELD',
|
|
5656
|
+
renderEnum("WritebackMode", ["SYNC", "ASYNC"]),
|
|
5657
|
+
'"""Write this resolved-table mutation back to a prefetch source (SYNC rolls back on failure; ASYNC queues)."""\ndirective @writeback(source: String!, mode: WritebackMode) repeatable on FIELD',
|
|
5658
|
+
'"""Declare that this mutation deliberately keeps writeback columns local to the named source."""\ndirective @localOnly(source: String!) repeatable on FIELD'
|
|
5659
|
+
];
|
|
5660
|
+
function trackedCells(table) {
|
|
5661
|
+
const columns = [...new Set(table.columns.flatMap((column) => column.cell ? [column.cell.of] : []))];
|
|
5662
|
+
if (columns.length === 0) return null;
|
|
5663
|
+
return { resolved: table.columns.some((column) => column.cell?.part === "source"), columns };
|
|
5664
|
+
}
|
|
5665
|
+
function renderSharedCellTypes(all) {
|
|
5666
|
+
const present = all.filter((cells) => cells !== null);
|
|
5667
|
+
const blocks = [];
|
|
5668
|
+
if (present.some((cells) => !cells.resolved)) {
|
|
5669
|
+
blocks.push(`"""When, and by whom, a cell was last written."""
|
|
5670
|
+
type ${CELL_TYPENAMES.stamp} {
|
|
5671
|
+
at: timestamptz!
|
|
5672
|
+
by: String
|
|
5673
|
+
}`);
|
|
5674
|
+
}
|
|
5675
|
+
if (present.some((cells) => cells.resolved)) {
|
|
5676
|
+
blocks.push(`"""A newer candidate value that lost to the column's resolve rule."""
|
|
5677
|
+
type ${CELL_TYPENAMES.conflict} {
|
|
5678
|
+
source: String!
|
|
5679
|
+
at: timestamptz
|
|
5680
|
+
value: jsonb
|
|
5681
|
+
}`);
|
|
5682
|
+
blocks.push(`"""How a resolved cell was decided: the winning source, its time and actor, and newer losers."""
|
|
5683
|
+
type ${CELL_TYPENAMES.resolved} {
|
|
5684
|
+
source: String!
|
|
5685
|
+
at: timestamptz
|
|
5686
|
+
by: String
|
|
5687
|
+
conflicts: [${CELL_TYPENAMES.conflict}!]!
|
|
5688
|
+
}`);
|
|
5689
|
+
}
|
|
5690
|
+
return blocks;
|
|
5691
|
+
}
|
|
5692
|
+
function renderCellsType(ctx) {
|
|
5693
|
+
if (!ctx.cells) return [];
|
|
5694
|
+
const cellType = ctx.cells.resolved ? CELL_TYPENAMES.resolved : CELL_TYPENAMES.stamp;
|
|
5695
|
+
const lines = ctx.cells.columns.map((column) => ` ${fieldName(column)}: ${cellType}`);
|
|
5696
|
+
return [`type ${cellsTypename(ctx.names.typename)} {
|
|
5697
|
+
${lines.join("\n")}
|
|
5698
|
+
}`];
|
|
5699
|
+
}
|
|
5700
|
+
function relationArgs(target) {
|
|
5701
|
+
return `where: ${target.boolExp}, orderBy: [${target.orderBy}!], limit: Int, offset: Int`;
|
|
5702
|
+
}
|
|
5703
|
+
function rootArgs(names) {
|
|
5704
|
+
return `where: ${names.boolExp}, orderBy: [${names.orderBy}!], distinctOn: [${names.selectColumn}!], limit: Int, offset: Int`;
|
|
5705
|
+
}
|
|
5706
|
+
function primaryKeyColumns(table) {
|
|
5707
|
+
if (!table.primaryKey || table.primaryKey.length === 0) return [];
|
|
5708
|
+
return table.primaryKey.map((identifier3) => {
|
|
5709
|
+
const column = table.columns.find((c) => c.columnName === identifier3 || c.name === identifier3);
|
|
5710
|
+
if (!column) {
|
|
5711
|
+
throw new SdlGenerationError(`Primary key column "${identifier3}" not found on table "${table.table}".`);
|
|
5712
|
+
}
|
|
5713
|
+
return column;
|
|
5714
|
+
});
|
|
5715
|
+
}
|
|
5716
|
+
function renderTableBlocks(ctx) {
|
|
5717
|
+
const { table, names } = ctx;
|
|
5718
|
+
const blocks = [
|
|
5719
|
+
renderObjectType(ctx),
|
|
5720
|
+
...renderCellsType(ctx),
|
|
5721
|
+
renderSelectColumnEnum(ctx),
|
|
5722
|
+
renderOrderByInput(ctx),
|
|
5723
|
+
renderBoolExp(ctx),
|
|
5724
|
+
...renderAggregateTypes(ctx),
|
|
5725
|
+
...renderGroupByTypes(ctx)
|
|
5726
|
+
];
|
|
5727
|
+
if (ctx.emitAggregateBoolExp) blocks.push(...renderAggregateBoolExp(ctx));
|
|
5728
|
+
const pk = primaryKeyColumns(table);
|
|
5729
|
+
if (pk.length > 0) {
|
|
5730
|
+
const fields = pk.map((column) => ` ${fieldName(column.name)}: ${columnGqlTypeOf(table, column)}!`).join("\n");
|
|
5731
|
+
blocks.push(`input ${names.pkColumnsInput} {
|
|
5732
|
+
${fields}
|
|
5733
|
+
}`);
|
|
5734
|
+
}
|
|
5735
|
+
if (ctx.table.resolved !== void 0) {
|
|
5736
|
+
blocks.push(...renderResolvedWriteBlocks(ctx));
|
|
5737
|
+
} else if (allowsInsert(table) || allowsUpdate(table)) {
|
|
5738
|
+
blocks.push(...renderWriteBlocks(ctx, { insert: allowsInsert(table), update: allowsUpdate(table) }));
|
|
5739
|
+
}
|
|
5740
|
+
return blocks;
|
|
5741
|
+
}
|
|
5742
|
+
function resolvedWritableColumns(table) {
|
|
5743
|
+
const system = new Set(Object.values(RESOLVED_VIEW_SYSTEM_COLUMNS).map((column) => column.name));
|
|
5744
|
+
return table.columns.filter((column) => !system.has(column.name));
|
|
5745
|
+
}
|
|
5746
|
+
function renderResolvedWriteBlocks(ctx) {
|
|
5747
|
+
const { table, names } = ctx;
|
|
5748
|
+
const writable = resolvedWritableColumns(table);
|
|
5749
|
+
const pk = new Set(table.primaryKey ?? []);
|
|
5750
|
+
const settable = writable.filter((column) => !pk.has(column.columnName));
|
|
5751
|
+
const line = (column) => ` ${fieldName(column.name)}: ${columnGqlTypeOf(table, column)}`;
|
|
5752
|
+
const blocks = [];
|
|
5753
|
+
if (allowsInsert(table)) blocks.push(`input ${names.insertInput} {
|
|
5754
|
+
${writable.map(line).join("\n")}
|
|
5755
|
+
}`);
|
|
5756
|
+
if (allowsUpdate(table)) {
|
|
5757
|
+
blocks.push(`input ${names.setInput} {
|
|
5758
|
+
${settable.map(line).join("\n")}
|
|
5759
|
+
}`);
|
|
5760
|
+
blocks.push(renderEnum(names.updateColumnEnum, settable.map((column) => fieldName(column.name))));
|
|
5761
|
+
}
|
|
5762
|
+
return blocks;
|
|
5763
|
+
}
|
|
5764
|
+
function renderResolvedMutationFields(table, names) {
|
|
5765
|
+
const lines = [];
|
|
5766
|
+
const pk = primaryKeyColumns(table);
|
|
5767
|
+
if (allowsInsert(table)) lines.push(` ${names.insertOneRoot}(object: ${names.insertInput}!): ${names.typename}`);
|
|
5768
|
+
if (allowsUpdate(table) && pk.length > 0) {
|
|
5769
|
+
lines.push(` ${names.updateByPkRoot}(pkColumns: ${names.pkColumnsInput}!, _set: ${names.setInput}!): ${names.typename}`);
|
|
5770
|
+
lines.push(` ${names.revertByPkRoot}(pkColumns: ${names.pkColumnsInput}!, columns: [${names.updateColumnEnum}!]!): ${names.typename}`);
|
|
5771
|
+
if ((table.resolved?.deletedBy.length ?? 0) > 0) {
|
|
5772
|
+
const args = pk.map((column) => `${fieldName(column.name)}: ${columnGqlTypeOf(table, column)}!`).join(", ");
|
|
5773
|
+
lines.push(` ${names.deleteByPkRoot}(${args}): ${names.typename}`);
|
|
5774
|
+
}
|
|
5775
|
+
}
|
|
5776
|
+
return lines;
|
|
5777
|
+
}
|
|
5778
|
+
function allowsInsert(table) {
|
|
5779
|
+
return table.source === void 0 && (table.policies.write !== void 0 || table.policies.insert !== void 0);
|
|
5780
|
+
}
|
|
5781
|
+
function allowsUpdate(table) {
|
|
5782
|
+
return table.source === void 0 && (table.policies.write !== void 0 || table.policies.update !== void 0);
|
|
5783
|
+
}
|
|
5784
|
+
function allowsOnConflict(table) {
|
|
5785
|
+
if (!allowsInsert(table) || !allowsUpdate(table)) return false;
|
|
5786
|
+
if (table.policies.insert !== void 0 || table.policies.update !== void 0) return false;
|
|
5787
|
+
return table.policies.write !== void 0;
|
|
5788
|
+
}
|
|
5789
|
+
function renderObjectType(ctx) {
|
|
5790
|
+
const { table, names } = ctx;
|
|
5791
|
+
const lines = [];
|
|
5792
|
+
if (table.description !== void 0) lines.push(...descriptionLines(table.description, ""));
|
|
5793
|
+
lines.push(`type ${names.typename} {`);
|
|
5794
|
+
for (const column of table.columns) {
|
|
5795
|
+
if (column.description !== void 0) lines.push(...descriptionLines(column.description, " "));
|
|
5796
|
+
const bang = column.nullable ? "" : "!";
|
|
5797
|
+
const deprecated = column.deprecated ? " @deprecated" : "";
|
|
5798
|
+
lines.push(` ${fieldName(column.name)}: ${columnGqlTypeOf(table, column)}${bang}${deprecated}`);
|
|
5799
|
+
}
|
|
5800
|
+
if (ctx.cells) {
|
|
5801
|
+
lines.push(' """Per-cell metadata: when each field was last written, and by whom."""');
|
|
5802
|
+
lines.push(` ${CELLS_FIELD}: ${cellsTypename(names.typename)}!`);
|
|
5803
|
+
}
|
|
5804
|
+
for (const link of table.links) {
|
|
5805
|
+
if (link.description !== void 0) lines.push(...descriptionLines(link.description, " "));
|
|
5806
|
+
if (link.kind === "toPlatformUser") {
|
|
5807
|
+
lines.push(` ${fieldName(link.name)}: ${PLATFORM_USER_TYPENAME}`);
|
|
5808
|
+
continue;
|
|
5809
|
+
}
|
|
5810
|
+
const target = link.target !== void 0 ? ctx.namesByTable.get(link.target) : void 0;
|
|
5811
|
+
if (!target) continue;
|
|
5812
|
+
if (link.kind === "toOne") {
|
|
5813
|
+
lines.push(` ${fieldName(link.name)}: ${target.typename}`);
|
|
5814
|
+
} else {
|
|
5815
|
+
lines.push(` ${fieldName(link.name)}(${relationArgs(target)}): [${target.typename}!]!`);
|
|
5816
|
+
lines.push(` ${fieldName(link.name)}Aggregate(${relationArgs(target)}): ${target.aggregate}!`);
|
|
5817
|
+
}
|
|
5818
|
+
}
|
|
5819
|
+
lines.push("}");
|
|
5820
|
+
return lines.join("\n");
|
|
5821
|
+
}
|
|
5822
|
+
function renderSelectColumnEnum(ctx) {
|
|
5823
|
+
const values = ctx.table.columns.map((column) => fieldName(column.name));
|
|
5824
|
+
for (const value of values) assertEnumValue(value, `select-column enum for table "${ctx.table.table}"`);
|
|
5825
|
+
return renderEnum(ctx.names.selectColumn, values);
|
|
5826
|
+
}
|
|
5827
|
+
function renderOrderByInput(ctx) {
|
|
5828
|
+
const { table, names } = ctx;
|
|
5829
|
+
const lines = table.columns.map((column) => ` ${fieldName(column.name)}: ${ORDER_BY_ENUM}`);
|
|
5830
|
+
for (const link of table.links) {
|
|
5831
|
+
if (link.kind === "toPlatformUser") {
|
|
5832
|
+
lines.push(` ${fieldName(link.name)}: ${PLATFORM_USER_TYPENAME}OrderBy`);
|
|
5833
|
+
} else if (link.kind === "toOne" && link.target !== void 0) {
|
|
5834
|
+
const target = ctx.namesByTable.get(link.target);
|
|
5835
|
+
if (target) lines.push(` ${fieldName(link.name)}: ${target.orderBy}`);
|
|
5836
|
+
}
|
|
5837
|
+
}
|
|
5838
|
+
return `input ${names.orderBy} {
|
|
5839
|
+
${lines.join("\n")}
|
|
5840
|
+
}`;
|
|
5841
|
+
}
|
|
5842
|
+
function renderBoolExp(ctx) {
|
|
5843
|
+
const { table, names } = ctx;
|
|
5844
|
+
const lines = [
|
|
5845
|
+
` _and: [${names.boolExp}!]`,
|
|
5846
|
+
` _not: ${names.boolExp}`,
|
|
5847
|
+
` _or: [${names.boolExp}!]`
|
|
5848
|
+
];
|
|
5849
|
+
for (const column of table.columns) {
|
|
5850
|
+
lines.push(` ${fieldName(column.name)}: ${comparisonExpName(columnGqlTypeOf(table, column))}`);
|
|
5851
|
+
}
|
|
5852
|
+
for (const link of table.links) {
|
|
5853
|
+
if (link.kind === "toPlatformUser") {
|
|
5854
|
+
lines.push(` ${fieldName(link.name)}: ${PLATFORM_USER_TYPENAME}BoolExp`);
|
|
5855
|
+
continue;
|
|
5856
|
+
}
|
|
5857
|
+
const target = link.target !== void 0 ? ctx.namesByTable.get(link.target) : void 0;
|
|
5858
|
+
if (!target) continue;
|
|
5859
|
+
lines.push(` ${fieldName(link.name)}: ${target.boolExp}`);
|
|
5860
|
+
if (link.kind === "toMany") {
|
|
5861
|
+
lines.push(` ${fieldName(link.name)}Aggregate: ${target.aggregateBoolExp}`);
|
|
5862
|
+
}
|
|
5863
|
+
}
|
|
5864
|
+
return `input ${names.boolExp} {
|
|
5865
|
+
${lines.join("\n")}
|
|
5866
|
+
}`;
|
|
5867
|
+
}
|
|
5868
|
+
function numericishColumns(table) {
|
|
5869
|
+
return table.columns.filter((column) => typeof column.type !== "object" && NUMERICISH.has(column.type));
|
|
5870
|
+
}
|
|
5871
|
+
function jsonbColumns(table) {
|
|
5872
|
+
return clientWritableColumns(table).filter((column) => column.type === "jsonb");
|
|
5873
|
+
}
|
|
5874
|
+
function clientWritableColumns(table) {
|
|
5875
|
+
return table.columns;
|
|
5876
|
+
}
|
|
5877
|
+
function renderAggregateTypes(ctx) {
|
|
5878
|
+
const { table, names } = ctx;
|
|
5879
|
+
const numericish = numericishColumns(table);
|
|
5880
|
+
const blocks = [];
|
|
5881
|
+
blocks.push(
|
|
5882
|
+
`type ${names.aggregate} {
|
|
5883
|
+
aggregate: ${names.aggregateFields}
|
|
5884
|
+
nodes: [${names.typename}!]!
|
|
5885
|
+
}`
|
|
5886
|
+
);
|
|
5887
|
+
const fieldLines = [` count(columns: [${names.selectColumn}!], distinct: Boolean): Int!`];
|
|
5888
|
+
if (numericish.length > 0) {
|
|
5889
|
+
fieldLines.push(` avg: ${names.avgFields}`);
|
|
5890
|
+
fieldLines.push(` sum: ${names.sumFields}`);
|
|
5891
|
+
}
|
|
5892
|
+
blocks.push(`type ${names.aggregateFields} {
|
|
5893
|
+
${fieldLines.join("\n")}
|
|
5894
|
+
}`);
|
|
5895
|
+
if (numericish.length > 0) {
|
|
5896
|
+
const avgLines = numericish.map((column) => ` ${fieldName(column.name)}: Float`);
|
|
5897
|
+
blocks.push(`type ${names.avgFields} {
|
|
5898
|
+
${avgLines.join("\n")}
|
|
5899
|
+
}`);
|
|
5900
|
+
const sumLines = numericish.map((column) => ` ${fieldName(column.name)}: ${columnGqlTypeOf(table, column)}`);
|
|
5901
|
+
blocks.push(`type ${names.sumFields} {
|
|
5902
|
+
${sumLines.join("\n")}
|
|
5903
|
+
}`);
|
|
5904
|
+
}
|
|
5905
|
+
return blocks;
|
|
5906
|
+
}
|
|
5907
|
+
function groupableColumns(table) {
|
|
5908
|
+
return table.columns.filter((column) => column.type !== "jsonb");
|
|
5909
|
+
}
|
|
5910
|
+
function renderGroupByTypes(ctx) {
|
|
5911
|
+
const { table, names } = ctx;
|
|
5912
|
+
const groupable = groupableColumns(table);
|
|
5913
|
+
if (groupable.length === 0) return [];
|
|
5914
|
+
const keyLines = groupable.map((column) => ` ${fieldName(column.name)}: ${columnGqlTypeOf(table, column)}`);
|
|
5915
|
+
return [
|
|
5916
|
+
renderEnum(names.groupByColumnEnum, groupable.map((column) => fieldName(column.name))),
|
|
5917
|
+
`input ${names.groupByInput} {
|
|
5918
|
+
column: ${names.groupByColumnEnum}!
|
|
5919
|
+
bucket: ${DATE_BUCKET_ENUM}
|
|
5920
|
+
}`,
|
|
5921
|
+
`type ${names.groupKey} {
|
|
5922
|
+
${keyLines.join("\n")}
|
|
5923
|
+
}`,
|
|
5924
|
+
`type ${names.aggregateGroup} {
|
|
5925
|
+
key: ${names.groupKey}!
|
|
5926
|
+
aggregate: ${names.aggregateFields}
|
|
5927
|
+
}`
|
|
5928
|
+
];
|
|
5929
|
+
}
|
|
5930
|
+
function groupRootArgs(names) {
|
|
5931
|
+
return `where: ${names.boolExp}, groupBy: [${names.groupByInput}!]!, limit: Int, offset: Int`;
|
|
5932
|
+
}
|
|
5933
|
+
function renderAggregateBoolExp(ctx) {
|
|
5934
|
+
const { names } = ctx;
|
|
5935
|
+
return [
|
|
5936
|
+
`input ${names.aggregateBoolExp} {
|
|
5937
|
+
count: ${names.aggregateBoolExpCount}
|
|
5938
|
+
}`,
|
|
5939
|
+
`input ${names.aggregateBoolExpCount} {
|
|
5940
|
+
arguments: [${names.selectColumn}!]
|
|
5941
|
+
distinct: Boolean
|
|
5942
|
+
filter: ${names.boolExp}
|
|
5943
|
+
predicate: IntComparisonExp!
|
|
5944
|
+
}`
|
|
5945
|
+
];
|
|
5946
|
+
}
|
|
5947
|
+
function constraintValues(table) {
|
|
5948
|
+
const values = /* @__PURE__ */ new Set();
|
|
5949
|
+
if (table.primaryKey && table.primaryKey.length > 0) values.add(`${table.table}_pkey`);
|
|
5950
|
+
for (const column of table.columns) {
|
|
5951
|
+
if (column.unique) values.add(`${table.table}_${column.columnName}_unique`);
|
|
5952
|
+
}
|
|
5953
|
+
for (const index of table.indexes) {
|
|
5954
|
+
if (index.unique) values.add(index.name ?? `${table.table}_${index.columns.join("_")}_key`);
|
|
5955
|
+
}
|
|
5956
|
+
return [...values].sort();
|
|
5957
|
+
}
|
|
5958
|
+
function clientSettableColumns(table) {
|
|
5959
|
+
return clientWritableColumns(table).filter((column) => column.onUpdateFn === void 0);
|
|
5960
|
+
}
|
|
5961
|
+
function renderWriteBlocks(ctx, access) {
|
|
5962
|
+
const { table, names, pinned } = ctx;
|
|
5963
|
+
const blocks = [];
|
|
5964
|
+
if (access.insert) {
|
|
5965
|
+
const pinnedSet = new Set(pinned);
|
|
5966
|
+
const insertable = clientWritableColumns(table).filter(
|
|
5967
|
+
(column) => !pinnedSet.has(column.name) && !pinnedSet.has(column.columnName)
|
|
5968
|
+
);
|
|
5969
|
+
if (insertable.length === 0) {
|
|
5970
|
+
throw new SdlGenerationError(
|
|
5971
|
+
`Table "${table.table}" has no insertable columns after excluding pinned columns [${pinned.join(", ")}].`
|
|
5972
|
+
);
|
|
5973
|
+
}
|
|
5974
|
+
const insertLines = insertable.map(
|
|
5975
|
+
(column) => ` ${fieldName(column.name)}: ${columnGqlTypeOf(table, column)}`
|
|
5976
|
+
);
|
|
5977
|
+
blocks.push(`input ${names.insertInput} {
|
|
5978
|
+
${insertLines.join("\n")}
|
|
5979
|
+
}`);
|
|
5980
|
+
}
|
|
5981
|
+
if (access.update) {
|
|
5982
|
+
const setLines = clientSettableColumns(table).map(
|
|
5983
|
+
(column) => ` ${fieldName(column.name)}: ${columnGqlTypeOf(table, column)}`
|
|
5984
|
+
);
|
|
5985
|
+
blocks.push(`input ${names.setInput} {
|
|
5986
|
+
${setLines.join("\n")}
|
|
5987
|
+
}`);
|
|
5988
|
+
}
|
|
5989
|
+
const jsonb = jsonbColumns(table);
|
|
5990
|
+
if (access.update && jsonb.length > 0) {
|
|
5991
|
+
const appendLines = jsonb.map((column) => ` ${fieldName(column.name)}: jsonb`);
|
|
5992
|
+
blocks.push(`input ${names.appendInput} {
|
|
5993
|
+
${appendLines.join("\n")}
|
|
5994
|
+
}`);
|
|
5995
|
+
}
|
|
5996
|
+
const numericish = numericishColumns(table);
|
|
5997
|
+
if (access.update && numericish.length > 0) {
|
|
5998
|
+
const incLines = numericish.map(
|
|
5999
|
+
(column) => ` ${fieldName(column.name)}: ${columnGqlTypeOf(table, column)}`
|
|
6000
|
+
);
|
|
6001
|
+
blocks.push(`input ${names.incInput} {
|
|
6002
|
+
${incLines.join("\n")}
|
|
6003
|
+
}`);
|
|
6004
|
+
}
|
|
6005
|
+
const constraints = constraintValues(table);
|
|
6006
|
+
if (access.insert && allowsOnConflict(table) && constraints.length > 0) {
|
|
6007
|
+
for (const value of constraints) assertEnumValue(value, `constraint enum for table "${table.table}"`);
|
|
6008
|
+
blocks.push(renderEnum(names.constraintEnum, constraints));
|
|
6009
|
+
const updateColumns = clientSettableColumns(table).map((column) => fieldName(column.name));
|
|
6010
|
+
blocks.push(renderEnum(names.updateColumnEnum, updateColumns));
|
|
6011
|
+
blocks.push(
|
|
6012
|
+
`input ${names.onConflict} {
|
|
6013
|
+
constraint: ${names.constraintEnum}!
|
|
6014
|
+
updateColumns: [${names.updateColumnEnum}!]! = []
|
|
6015
|
+
where: ${names.boolExp}
|
|
6016
|
+
}`
|
|
6017
|
+
);
|
|
6018
|
+
}
|
|
6019
|
+
if (access.insert || access.update) {
|
|
6020
|
+
blocks.push(
|
|
6021
|
+
`type ${names.mutationResponse} {
|
|
6022
|
+
affectedRows: Int!
|
|
6023
|
+
returning: [${names.typename}!]!
|
|
6024
|
+
}`
|
|
6025
|
+
);
|
|
6026
|
+
}
|
|
6027
|
+
return blocks;
|
|
6028
|
+
}
|
|
6029
|
+
function renderQueryRoot(tables, namesByTable) {
|
|
6030
|
+
const lines = ["type Query {"];
|
|
6031
|
+
for (const table of tables) {
|
|
6032
|
+
const names = namesByTable.get(table.table);
|
|
6033
|
+
if (!names) continue;
|
|
6034
|
+
lines.push(` ${names.selectRoot}(${rootArgs(names)}): [${names.typename}!]!`);
|
|
6035
|
+
const pk = primaryKeyColumns(table);
|
|
6036
|
+
if (pk.length > 0) {
|
|
6037
|
+
const args = pk.map((column) => `${fieldName(column.name)}: ${columnGqlTypeOf(table, column)}!`).join(", ");
|
|
6038
|
+
lines.push(` ${names.byPkRoot}(${args}): ${names.typename}`);
|
|
6039
|
+
}
|
|
6040
|
+
lines.push(` ${names.aggregateRoot}(${rootArgs(names)}): ${names.aggregate}!`);
|
|
6041
|
+
if (groupableColumns(table).length > 0) {
|
|
6042
|
+
lines.push(` ${names.aggregateGroupsRoot}(${groupRootArgs(names)}): [${names.aggregateGroup}!]!`);
|
|
6043
|
+
}
|
|
6044
|
+
}
|
|
6045
|
+
lines.push("}");
|
|
6046
|
+
return lines.join("\n");
|
|
6047
|
+
}
|
|
6048
|
+
function columnGqlTypeOf(table, column) {
|
|
6049
|
+
return typeof column.type === "object" ? enumTypeName(column.type.enum) : graphqlScalarFor(column.type);
|
|
6050
|
+
}
|
|
6051
|
+
function renderMutationRoot(tables, namesByTable) {
|
|
6052
|
+
const lines = [];
|
|
6053
|
+
for (const table of tables) {
|
|
6054
|
+
const canInsert = allowsInsert(table);
|
|
6055
|
+
const canUpdate = allowsUpdate(table);
|
|
6056
|
+
if (!canInsert && !canUpdate) continue;
|
|
6057
|
+
const names = namesByTable.get(table.table);
|
|
6058
|
+
if (!names) continue;
|
|
6059
|
+
if (table.resolved !== void 0) {
|
|
6060
|
+
lines.push(...renderResolvedMutationFields(table, names));
|
|
6061
|
+
continue;
|
|
6062
|
+
}
|
|
6063
|
+
const constraints = constraintValues(table);
|
|
6064
|
+
const onConflictArg = canInsert && allowsOnConflict(table) && constraints.length > 0 ? `, onConflict: ${names.onConflict}` : "";
|
|
6065
|
+
const updateArgs = [`_set: ${names.setInput}`];
|
|
6066
|
+
if (jsonbColumns(table).length > 0) updateArgs.push(`_append: ${names.appendInput}`);
|
|
6067
|
+
if (numericishColumns(table).length > 0) updateArgs.push(`_inc: ${names.incInput}`);
|
|
6068
|
+
const updateArgList = updateArgs.join(", ");
|
|
6069
|
+
if (canInsert) {
|
|
6070
|
+
lines.push(` ${names.insertRoot}(objects: [${names.insertInput}!]!${onConflictArg}): ${names.mutationResponse}`);
|
|
6071
|
+
lines.push(` ${names.insertOneRoot}(object: ${names.insertInput}!${onConflictArg}): ${names.typename}`);
|
|
6072
|
+
}
|
|
6073
|
+
if (canUpdate) {
|
|
6074
|
+
lines.push(` ${names.updateRoot}(where: ${names.boolExp}!, ${updateArgList}): ${names.mutationResponse}`);
|
|
6075
|
+
const pk = primaryKeyColumns(table);
|
|
6076
|
+
if (pk.length > 0) {
|
|
6077
|
+
lines.push(
|
|
6078
|
+
` ${names.updateByPkRoot}(pkColumns: ${names.pkColumnsInput}!, ${updateArgList}): ${names.typename}`
|
|
6079
|
+
);
|
|
6080
|
+
}
|
|
6081
|
+
lines.push(` ${names.deleteRoot}(where: ${names.boolExp}!): ${names.mutationResponse}`);
|
|
6082
|
+
if (pk.length > 0) {
|
|
6083
|
+
const args = pk.map((column) => `${fieldName(column.name)}: ${columnGqlTypeOf(table, column)}!`).join(", ");
|
|
6084
|
+
lines.push(` ${names.deleteByPkRoot}(${args}): ${names.typename}`);
|
|
6085
|
+
}
|
|
6086
|
+
}
|
|
6087
|
+
lines.push(...renderCowMutationFields(table, names));
|
|
6088
|
+
}
|
|
6089
|
+
if (lines.length === 0) return null;
|
|
6090
|
+
return `type Mutation {
|
|
6091
|
+
${lines.join("\n")}
|
|
6092
|
+
}`;
|
|
6093
|
+
}
|
|
6094
|
+
function renderCowMutationFields(table, names) {
|
|
6095
|
+
const lines = [];
|
|
6096
|
+
const columnByName = new Map(table.columns.map((column) => [column.name, column]));
|
|
6097
|
+
for (const mutation of table.cowMutations ?? []) {
|
|
6098
|
+
const pkColumn = columnByName.get(mutation.pk);
|
|
6099
|
+
if (!pkColumn) continue;
|
|
6100
|
+
const pkArg = `${fieldName(mutation.pk)}: ${columnGqlTypeOf(table, pkColumn)}!`;
|
|
6101
|
+
if (mutation.kind === "revert") {
|
|
6102
|
+
lines.push(` ${fieldName(mutation.name)}(${pkArg}): ${names.typename}`);
|
|
6103
|
+
continue;
|
|
6104
|
+
}
|
|
6105
|
+
const setArgs = mutation.set.map((property) => {
|
|
6106
|
+
const column = columnByName.get(property);
|
|
6107
|
+
if (!column) return null;
|
|
6108
|
+
const bang = column.nullable === false ? "!" : "";
|
|
6109
|
+
return `${fieldName(property)}: ${columnGqlTypeOf(table, column)}${bang}`;
|
|
6110
|
+
});
|
|
6111
|
+
if (setArgs.some((arg) => arg === null)) continue;
|
|
6112
|
+
lines.push(` ${fieldName(mutation.name)}(${pkArg}, ${setArgs.join(", ")}): ${names.typename}`);
|
|
6113
|
+
}
|
|
6114
|
+
return lines;
|
|
6115
|
+
}
|
|
6116
|
+
|
|
6117
|
+
// src/compiler-gql/default-operations.ts
|
|
6118
|
+
var DEFAULT_LIST_LIMIT = 100;
|
|
6119
|
+
function typeReference(type, nonNull = false) {
|
|
6120
|
+
if (type instanceof GraphQLNonNull) return `${typeReference(type.ofType, true)}`;
|
|
6121
|
+
const inner = type instanceof GraphQLList ? `[${typeReference(type.ofType)}]` : getNamedType(type).name;
|
|
6122
|
+
return `${inner}${nonNull ? "!" : ""}`;
|
|
6123
|
+
}
|
|
6124
|
+
function objectScalarFields(type) {
|
|
6125
|
+
return Object.entries(type.getFields()).filter(([, field]) => {
|
|
6126
|
+
const named = getNamedType(field.type);
|
|
6127
|
+
return named instanceof GraphQLScalarType || named instanceof GraphQLEnumType;
|
|
6128
|
+
}).map(([name2]) => name2);
|
|
6129
|
+
}
|
|
6130
|
+
function argumentInputFields(arg) {
|
|
6131
|
+
if (arg.name !== "pkColumns") return void 0;
|
|
6132
|
+
const input = getNamedType(arg.type);
|
|
6133
|
+
if (!(input instanceof GraphQLInputObjectType)) return void 0;
|
|
6134
|
+
return Object.values(input.getFields()).map((field) => ({
|
|
6135
|
+
name: field.name,
|
|
6136
|
+
type: field.type,
|
|
6137
|
+
variable: field.name
|
|
6138
|
+
}));
|
|
6139
|
+
}
|
|
6140
|
+
function inputFieldsTouchWriteback(root, writebackColumns) {
|
|
6141
|
+
if (!root) return false;
|
|
6142
|
+
const input = getNamedType(root.type);
|
|
6143
|
+
return input instanceof GraphQLInputObjectType && Object.keys(input.getFields()).some((field) => writebackColumns.has(field));
|
|
6144
|
+
}
|
|
6145
|
+
function variableDefinitions(args, defaults = {}) {
|
|
6146
|
+
return args.flatMap((arg) => {
|
|
6147
|
+
const inputFields = argumentInputFields(arg);
|
|
6148
|
+
if (inputFields) {
|
|
6149
|
+
return inputFields.map((field) => `$${field.variable}: ${typeReference(field.type)}`);
|
|
6150
|
+
}
|
|
6151
|
+
const defaultValue = defaults[arg.name];
|
|
6152
|
+
return [`$${arg.name}: ${typeReference(arg.type)}${defaultValue === void 0 ? "" : ` = ${defaultValue}`}`];
|
|
6153
|
+
}).join(", ");
|
|
6154
|
+
}
|
|
6155
|
+
function argumentUses(args) {
|
|
6156
|
+
return args.map((arg) => {
|
|
6157
|
+
const inputFields = argumentInputFields(arg);
|
|
6158
|
+
if (inputFields) {
|
|
6159
|
+
const values = inputFields.map((field) => `${field.name}: $${field.variable}`);
|
|
6160
|
+
return `${arg.name}: { ${values.join(", ")} }`;
|
|
6161
|
+
}
|
|
6162
|
+
return `${arg.name}: $${arg.name}`;
|
|
6163
|
+
}).join(", ");
|
|
6164
|
+
}
|
|
6165
|
+
function callArguments(args) {
|
|
6166
|
+
const uses = argumentUses(args);
|
|
6167
|
+
return uses ? `(${uses})` : "";
|
|
6168
|
+
}
|
|
6169
|
+
function listOperationName(table) {
|
|
6170
|
+
const raw = pascalCase(table);
|
|
6171
|
+
if (raw !== typeName(table)) return `List${raw}`;
|
|
6172
|
+
if (raw.endsWith("y") && !/[aeiou]y$/.test(raw)) return `List${raw.slice(0, -1)}ies`;
|
|
6173
|
+
if (/(s|x|ch|sh)$/.test(raw)) return `List${raw}es`;
|
|
6174
|
+
return `List${raw}s`;
|
|
6175
|
+
}
|
|
6176
|
+
function document(path, operation) {
|
|
6177
|
+
return { path: `graphql/__generated__/${path}.graphql`, contents: `${operation.trim()}
|
|
6178
|
+
` };
|
|
6179
|
+
}
|
|
6180
|
+
function generateDefaultOperations(canonical, sdl, existingOperationNames = []) {
|
|
6181
|
+
const definition = canonicalDefinitionSchema.parse(canonical);
|
|
6182
|
+
const composed = composeNamespace(definition).definition;
|
|
6183
|
+
const schema = parseSdl(sdl);
|
|
6184
|
+
const query = schema.getQueryType();
|
|
6185
|
+
const mutation = schema.getMutationType();
|
|
6186
|
+
if (!query) return [];
|
|
6187
|
+
const owned = /* @__PURE__ */ new Set([
|
|
6188
|
+
...definition.tables.map((table) => table.table),
|
|
6189
|
+
...(definition.views ?? []).map((view) => view.view)
|
|
6190
|
+
]);
|
|
6191
|
+
const occupied = new Set(existingOperationNames);
|
|
6192
|
+
const generatedNames = /* @__PURE__ */ new Map();
|
|
6193
|
+
const results = [];
|
|
6194
|
+
const claimName = (name2, relation) => {
|
|
6195
|
+
if (occupied.has(name2)) return false;
|
|
6196
|
+
const previous = generatedNames.get(name2);
|
|
6197
|
+
if (previous !== void 0) throw new Error(`default operation name collision '${name2}' for '${previous}' and '${relation}'`);
|
|
6198
|
+
generatedNames.set(name2, relation);
|
|
6199
|
+
return true;
|
|
6200
|
+
};
|
|
6201
|
+
for (const relation of canonicalRelations(composed).filter((item) => owned.has(item.table) && item.internal !== true)) {
|
|
6202
|
+
const names = tableNames(relation.table);
|
|
6203
|
+
const fields = query.getFields();
|
|
6204
|
+
const objectType = schema.getType(names.typename);
|
|
6205
|
+
const scalarSelection = objectType instanceof GraphQLObjectType ? objectScalarFields(objectType) : [];
|
|
6206
|
+
const selection = scalarSelection.length > 0 ? `{ ${scalarSelection.join(" ")} }` : "";
|
|
6207
|
+
const byPk = fields[names.byPkRoot];
|
|
6208
|
+
const getName = `Get${names.typename}`;
|
|
6209
|
+
if (byPk && selection && claimName(getName, relation.table)) {
|
|
6210
|
+
const vars = variableDefinitions(byPk.args);
|
|
6211
|
+
results.push(document(getName, `query ${getName}${vars ? `(${vars})` : ""} { record: ${names.byPkRoot}${callArguments(byPk.args)} ${selection} }`));
|
|
6212
|
+
}
|
|
6213
|
+
const list2 = fields[names.selectRoot];
|
|
6214
|
+
const listName = listOperationName(relation.table);
|
|
6215
|
+
if (list2 && selection && claimName(listName, relation.table)) {
|
|
6216
|
+
const listArgs = list2.args.filter((arg) => ["where", "orderBy", "limit", "offset"].includes(arg.name));
|
|
6217
|
+
const defaults = { limit: String(DEFAULT_LIST_LIMIT), offset: "0" };
|
|
6218
|
+
const vars = variableDefinitions(listArgs, defaults);
|
|
6219
|
+
results.push(document(listName, `query ${listName}${vars ? `(${vars})` : ""} { records: ${names.selectRoot}${callArguments(listArgs)} ${selection} }`));
|
|
6220
|
+
}
|
|
6221
|
+
if (!mutation || !selection) continue;
|
|
6222
|
+
const mutationFields = mutation.getFields();
|
|
6223
|
+
const mutationOps = [
|
|
6224
|
+
{ root: names.insertOneRoot, name: `Create${names.typename}`, args: ["object"], writeArg: "object" },
|
|
6225
|
+
{ root: names.updateByPkRoot, name: `Update${names.typename}`, args: ["pkColumns", "_set"], writeArg: "_set" },
|
|
6226
|
+
{ root: names.deleteByPkRoot, name: `Delete${names.typename}` }
|
|
6227
|
+
];
|
|
6228
|
+
const writebackColumns = new Set((relation.resolved?.sources.flatMap((source) => source.writeback?.columns ?? []) ?? []).map(fieldName));
|
|
6229
|
+
for (const item of mutationOps) {
|
|
6230
|
+
const root = mutationFields[item.root];
|
|
6231
|
+
if (root && item.writeArg && inputFieldsTouchWriteback(root.args.find((arg) => arg.name === item.writeArg), writebackColumns)) continue;
|
|
6232
|
+
if (!root || !claimName(item.name, relation.table)) continue;
|
|
6233
|
+
const args = item.args ? root.args.filter((arg) => item.args?.includes(arg.name)) : root.args;
|
|
6234
|
+
const vars = variableDefinitions(args);
|
|
6235
|
+
results.push(document(item.name, `mutation ${item.name}${vars ? `(${vars})` : ""} { record: ${item.root}${callArguments(args)} ${selection} }`));
|
|
6236
|
+
}
|
|
6237
|
+
}
|
|
6238
|
+
return results.map((source) => {
|
|
6239
|
+
try {
|
|
6240
|
+
return { ...source, contents: `${print(parse(source.contents)).trim()}
|
|
6241
|
+
` };
|
|
6242
|
+
} catch (error) {
|
|
6243
|
+
throw new Error(`${error instanceof Error ? error.message : String(error)}
|
|
6244
|
+
${source.contents}`);
|
|
6245
|
+
}
|
|
6246
|
+
});
|
|
6247
|
+
}
|
|
6248
|
+
function compileDefaultOperations(canonical, existingOperationNames = []) {
|
|
6249
|
+
const pinnedColumns = Object.fromEntries(
|
|
6250
|
+
Object.entries(computePinnedColumnsByTable(canonical)).map(([table, pins]) => [table, pins.map((pin) => pin.column)])
|
|
6251
|
+
);
|
|
6252
|
+
const sdl = generateSdl(canonical, { pinnedColumns, includeSyncedReads: true });
|
|
6253
|
+
const documents = generateDefaultOperations(canonical, sdl, existingOperationNames);
|
|
6254
|
+
return compileNamespaceOrThrow({ canonical, sdl, documents }).artifact;
|
|
6255
|
+
}
|
|
6256
|
+
|
|
5430
6257
|
// src/engine/types.ts
|
|
5431
6258
|
var SQL_TYPE_NAMES = {
|
|
5432
6259
|
double: "double precision",
|
|
@@ -6157,9 +6984,9 @@ async function compileOperationDocuments(input) {
|
|
|
6157
6984
|
return compileSqlDocuments({ input, result, sqlDocuments });
|
|
6158
6985
|
}
|
|
6159
6986
|
async function compileSqlDocuments({ input, result, sqlDocuments }) {
|
|
6160
|
-
for (const
|
|
6987
|
+
for (const document2 of sqlDocuments) {
|
|
6161
6988
|
try {
|
|
6162
|
-
const definition = defineOperation(JSON.parse(
|
|
6989
|
+
const definition = defineOperation(JSON.parse(document2.contents));
|
|
6163
6990
|
if (definition.namespace !== input.canonical.namespace) throw new Error("SQL operation namespace does not match its owner");
|
|
6164
6991
|
if (result.artifact.operations[definition.name]) throw new Error("duplicate operation name");
|
|
6165
6992
|
const roles = input.canonical.capabilities.roles;
|
|
@@ -6239,7 +7066,7 @@ async function compileSqlDocuments({ input, result, sqlDocuments }) {
|
|
|
6239
7066
|
return { ok: false, diagnostics: [...result.diagnostics, {
|
|
6240
7067
|
severity: "error",
|
|
6241
7068
|
code: "invalid-sql-operation",
|
|
6242
|
-
document:
|
|
7069
|
+
document: document2.path,
|
|
6243
7070
|
message: error instanceof Error ? error.message : "invalid SQL operation"
|
|
6244
7071
|
}] };
|
|
6245
7072
|
}
|
|
@@ -6420,6 +7247,6 @@ function compileDatasetPreviews(input) {
|
|
|
6420
7247
|
return operations;
|
|
6421
7248
|
}
|
|
6422
7249
|
|
|
6423
|
-
export { COMPILER_MAJOR, CONST_TRUE, DATASET_PREVIEW_LIMIT, DEFAULT_LIMIT, DocumentCompileError, MAX_LIMIT, NamespaceCompileError, ORDER_BY_DIRECTIONS, OperationLowerer, PLATFORM_USER_TABLE, PLATFORM_USER_TYPENAME, POLICY_COMPARE_OPS, POLICY_MAX_LINK_DEPTH, PolicyCompileError, PolicyParseError, allowedBoolExpOperators, allowedOps, andPred, assertClaimsOnlyWhen, bindNamedParameters, buildCompileContext, camelCase, canonicalOperationText, classifyInputType, collectPinnedColumns, compileBuiltinSqlArtifact, compileDatasetPreviews, compileNamespace, compileNamespaceOrThrow, compileOperationDocuments, compilePolicy, compilePredicateV2, compileRuntimeSql, computeOpHash, computeOpsHash, computePinnedColumnsByTable, computeSchemaHash, defineOperation, enumTypeName, flattenOperation, injectPolicies, inputTypeName, isSqlOperationDocument, lowerOperation, mutationRootNames, parseDocuments, parsePolicy, parseSdl, pascalCase, platformUserMeta, predContains, predContainsExists, queryRootNames, referencedSqlObjects, restrictOperation, sqlOperationDefinitionSchema, sqlQueryInputSchema, typeNameForTable, validateAppendInput, validateBoolExpInput, validateInsertInput, validateManagedViewQueries, validateOnConflictInput, validateOrderByInput, validateSelectColumns, validateSetInput, walkPolicy, walkPred };
|
|
7250
|
+
export { COMPILER_MAJOR, CONST_TRUE, DATASET_PREVIEW_LIMIT, DEFAULT_LIMIT, DocumentCompileError, MAX_LIMIT, NamespaceCompileError, ORDER_BY_DIRECTIONS, OperationLowerer, PLATFORM_USER_TABLE, PLATFORM_USER_TYPENAME, POLICY_COMPARE_OPS, POLICY_MAX_LINK_DEPTH, PolicyCompileError, PolicyParseError, allowedBoolExpOperators, allowedOps, andPred, assertClaimsOnlyWhen, bindNamedParameters, buildCompileContext, camelCase, canonicalOperationText, classifyInputType, collectPinnedColumns, compileBuiltinSqlArtifact, compileDatasetPreviews, compileDefaultOperations, compileNamespace, compileNamespaceOrThrow, compileOperationDocuments, compilePolicy, compilePredicateV2, compileRuntimeSql, computeOpHash, computeOpsHash, computePinnedColumnsByTable, computeSchemaHash, defineOperation, enumTypeName, flattenOperation, generateDefaultOperations, injectPolicies, inputTypeName, isSqlOperationDocument, lowerOperation, mutationRootNames, parseDocuments, parsePolicy, parseSdl, pascalCase, platformUserMeta, predContains, predContainsExists, queryRootNames, referencedSqlObjects, restrictOperation, sqlOperationDefinitionSchema, sqlQueryInputSchema, typeNameForTable, validateAppendInput, validateBoolExpInput, validateInsertInput, validateManagedViewQueries, validateOnConflictInput, validateOrderByInput, validateSelectColumns, validateSetInput, walkPolicy, walkPred };
|
|
6424
7251
|
//# sourceMappingURL=compiler.js.map
|
|
6425
7252
|
//# sourceMappingURL=compiler.js.map
|