@sequenceholdings/orm 2.4.0 → 2.5.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 (59) hide show
  1. package/dist/authoring.cjs +53 -7
  2. package/dist/authoring.cjs.map +1 -1
  3. package/dist/authoring.d.cts +11 -7
  4. package/dist/authoring.d.ts +11 -7
  5. package/dist/authoring.js +53 -7
  6. package/dist/authoring.js.map +1 -1
  7. package/dist/canonical.cjs +48 -2
  8. package/dist/canonical.cjs.map +1 -1
  9. package/dist/canonical.d.cts +72 -72
  10. package/dist/canonical.d.ts +72 -72
  11. package/dist/canonical.js +48 -2
  12. package/dist/canonical.js.map +1 -1
  13. package/dist/cli.cjs +237 -20
  14. package/dist/cli.cjs.map +1 -1
  15. package/dist/cli.js +237 -20
  16. package/dist/cli.js.map +1 -1
  17. package/dist/client.cjs +1 -1
  18. package/dist/client.cjs.map +1 -1
  19. package/dist/client.d.cts +1 -1
  20. package/dist/client.d.ts +1 -1
  21. package/dist/client.js +1 -1
  22. package/dist/client.js.map +1 -1
  23. package/dist/compiler.cjs +386 -14
  24. package/dist/compiler.cjs.map +1 -1
  25. package/dist/compiler.d.cts +16 -3
  26. package/dist/compiler.d.ts +16 -3
  27. package/dist/compiler.js +386 -15
  28. package/dist/compiler.js.map +1 -1
  29. package/dist/{constants-C6EG6Wq7.d.cts → constants-Dou1RkTo.d.cts} +7 -2
  30. package/dist/{constants-C6EG6Wq7.d.ts → constants-Dou1RkTo.d.ts} +7 -2
  31. package/dist/dynamic-input.d.cts +1 -1
  32. package/dist/dynamic-input.d.ts +1 -1
  33. package/dist/index.cjs +329 -130
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.cts +66 -58
  36. package/dist/index.d.ts +66 -58
  37. package/dist/index.js +329 -131
  38. package/dist/index.js.map +1 -1
  39. package/dist/ir.cjs +50 -3
  40. package/dist/ir.cjs.map +1 -1
  41. package/dist/ir.d.cts +71 -71
  42. package/dist/ir.d.ts +71 -71
  43. package/dist/ir.js +50 -3
  44. package/dist/ir.js.map +1 -1
  45. package/dist/predicate-v2.cjs +50 -3
  46. package/dist/predicate-v2.cjs.map +1 -1
  47. package/dist/predicate-v2.d.cts +1 -1
  48. package/dist/predicate-v2.d.ts +1 -1
  49. package/dist/predicate-v2.js +50 -3
  50. package/dist/predicate-v2.js.map +1 -1
  51. package/dist/sdl.cjs +53 -3
  52. package/dist/sdl.cjs.map +1 -1
  53. package/dist/sdl.d.cts +3 -1
  54. package/dist/sdl.d.ts +3 -1
  55. package/dist/sdl.js +53 -3
  56. package/dist/sdl.js.map +1 -1
  57. package/package.json +1 -1
  58. package/dist/{definition-DjfC7Lll.d.cts → definition-B99BsOAO.d.cts} +4 -4
  59. package/dist/{definition-DjfC7Lll.d.ts → definition-B99BsOAO.d.ts} +4 -4
@@ -5,8 +5,8 @@ export { DynamicInputIssue, DynamicInputResult, NormalizedBoolExp, NormalizedOnC
5
5
  export { CompilePolicyInput, CompilePredicateInput, POLICY_COMPARE_OPS, POLICY_MAX_LINK_DEPTH, ParsedPolicy, PolicyAccess, PolicyColumnInfo, PolicyCompareOp, PolicyCompileError, PolicyCompileErrorCode, PolicyLinkInfo, PolicyNode, PolicyOperand, PolicyParseError, PolicyResolver, PolicyTableInfo, assertClaimsOnlyWhen, compilePolicy, compilePredicateV2, parsePolicy, walkPolicy } from './predicate-v2.cjs';
6
6
  import { z } from 'zod';
7
7
  export { P as PLATFORM_USER_TYPENAME, c as camelCase, g as enumTypeName, p as pascalCase } from './naming-CU9ThP_E.cjs';
8
- export { S as SqlOperationDefinition, d as defineOperation, s as sqlOperationDefinitionSchema } from './definition-DjfC7Lll.cjs';
9
- import './constants-C6EG6Wq7.cjs';
8
+ export { S as SqlOperationDefinition, d as defineOperation, s as sqlOperationDefinitionSchema } from './definition-B99BsOAO.cjs';
9
+ import './constants-Dou1RkTo.cjs';
10
10
  import './policy-1iZbGnZZ.cjs';
11
11
 
12
12
  type DiagnosticSeverity = 'error' | 'warning';
@@ -318,6 +318,8 @@ interface RestrictedOperation {
318
318
  name: string;
319
319
  kind: 'query' | 'mutation';
320
320
  requires: CompiledOperation['requires'];
321
+ /** Literal namespace-author ceiling; never taken from execution variables. */
322
+ readBudget?: number;
321
323
  variables: VariableIr[];
322
324
  definition: OperationDefinitionNode;
323
325
  }
@@ -339,6 +341,7 @@ declare const MAX_LIMIT = 1000;
339
341
  declare class OperationLowerer {
340
342
  private readonly ctx;
341
343
  private readonly variables;
344
+ private readonly maxRows;
342
345
  constructor(ctx: CompileContext, restricted: RestrictedOperation);
343
346
  private meta;
344
347
  /** A data column — cell-metadata columns are reachable only through `cells { … }`. */
@@ -495,4 +498,14 @@ declare const DATASET_PREVIEW_LIMIT = 100;
495
498
  */
496
499
  declare function compileDatasetPreviews(input: CanonicalDefinition): Record<string, CompiledOperation>;
497
500
 
498
- export { type BuildContextResult, COMPILER_MAJOR, CONST_TRUE, type ClientManifest, type ClientManifestOperation, type CompileContext, type CompileNamespaceInput, type CompileNamespaceResult, type CompilerDiagnostic, DATASET_PREVIEW_LIMIT, DEFAULT_LIMIT, type DiagnosticSeverity, DocumentCompileError, MAX_LIMIT, type MutationOp, type MutationRootInfo, NamespaceCompileError, OperationLowerer, PLATFORM_USER_TABLE, type ParseDocumentsResult, type ParsedDocument, type PinnedColumn, type QueryRootInfo, type RestrictedOperation, type SourceDocument, type SqlQueryInput, andPred, buildCompileContext, canonicalOperationText, classifyInputType, collectPinnedColumns, compileDatasetPreviews, compileNamespace, compileNamespaceOrThrow, compileOperationDocuments, compileRuntimeSql, computeOpHash, computeOpsHash, computePinnedColumnsByTable, computeSchemaHash, flattenOperation, injectPolicies, inputTypeName, isSqlOperationDocument, lowerOperation, mutationRootNames, parseDocuments, parseSdl, platformUserMeta, predContains, predContainsExists, queryRootNames, restrictOperation, sqlQueryInputSchema, typeNameForTable, walkPred };
501
+ /**
502
+ * PostgreSQL omits pinned builtins from pg_depend, so catalog checks cannot
503
+ * enforce the function boundary alone. Parse every expression before either
504
+ * admission or DDL, including nested queries and aggregate/window clauses.
505
+ * Catalog validation remains responsible for resolved functions/operators.
506
+ */
507
+ declare function validateManagedViewQueries({ canonical }: {
508
+ canonical: CanonicalDefinition;
509
+ }): Promise<void>;
510
+
511
+ export { type BuildContextResult, COMPILER_MAJOR, CONST_TRUE, type ClientManifest, type ClientManifestOperation, type CompileContext, type CompileNamespaceInput, type CompileNamespaceResult, type CompilerDiagnostic, DATASET_PREVIEW_LIMIT, DEFAULT_LIMIT, type DiagnosticSeverity, DocumentCompileError, MAX_LIMIT, type MutationOp, type MutationRootInfo, NamespaceCompileError, OperationLowerer, PLATFORM_USER_TABLE, type ParseDocumentsResult, type ParsedDocument, type PinnedColumn, type QueryRootInfo, type RestrictedOperation, type SourceDocument, type SqlQueryInput, andPred, buildCompileContext, canonicalOperationText, classifyInputType, collectPinnedColumns, compileDatasetPreviews, compileNamespace, compileNamespaceOrThrow, compileOperationDocuments, compileRuntimeSql, computeOpHash, computeOpsHash, computePinnedColumnsByTable, computeSchemaHash, flattenOperation, injectPolicies, inputTypeName, isSqlOperationDocument, lowerOperation, mutationRootNames, parseDocuments, parseSdl, platformUserMeta, predContains, predContainsExists, queryRootNames, restrictOperation, sqlQueryInputSchema, typeNameForTable, validateManagedViewQueries, walkPred };
@@ -5,8 +5,8 @@ export { DynamicInputIssue, DynamicInputResult, NormalizedBoolExp, NormalizedOnC
5
5
  export { CompilePolicyInput, CompilePredicateInput, POLICY_COMPARE_OPS, POLICY_MAX_LINK_DEPTH, ParsedPolicy, PolicyAccess, PolicyColumnInfo, PolicyCompareOp, PolicyCompileError, PolicyCompileErrorCode, PolicyLinkInfo, PolicyNode, PolicyOperand, PolicyParseError, PolicyResolver, PolicyTableInfo, assertClaimsOnlyWhen, compilePolicy, compilePredicateV2, parsePolicy, walkPolicy } from './predicate-v2.js';
6
6
  import { z } from 'zod';
7
7
  export { P as PLATFORM_USER_TYPENAME, c as camelCase, g as enumTypeName, p as pascalCase } from './naming-CPfx7EfF.js';
8
- export { S as SqlOperationDefinition, d as defineOperation, s as sqlOperationDefinitionSchema } from './definition-DjfC7Lll.js';
9
- import './constants-C6EG6Wq7.js';
8
+ export { S as SqlOperationDefinition, d as defineOperation, s as sqlOperationDefinitionSchema } from './definition-B99BsOAO.js';
9
+ import './constants-Dou1RkTo.js';
10
10
  import './policy-1iZbGnZZ.js';
11
11
 
12
12
  type DiagnosticSeverity = 'error' | 'warning';
@@ -318,6 +318,8 @@ interface RestrictedOperation {
318
318
  name: string;
319
319
  kind: 'query' | 'mutation';
320
320
  requires: CompiledOperation['requires'];
321
+ /** Literal namespace-author ceiling; never taken from execution variables. */
322
+ readBudget?: number;
321
323
  variables: VariableIr[];
322
324
  definition: OperationDefinitionNode;
323
325
  }
@@ -339,6 +341,7 @@ declare const MAX_LIMIT = 1000;
339
341
  declare class OperationLowerer {
340
342
  private readonly ctx;
341
343
  private readonly variables;
344
+ private readonly maxRows;
342
345
  constructor(ctx: CompileContext, restricted: RestrictedOperation);
343
346
  private meta;
344
347
  /** A data column — cell-metadata columns are reachable only through `cells { … }`. */
@@ -495,4 +498,14 @@ declare const DATASET_PREVIEW_LIMIT = 100;
495
498
  */
496
499
  declare function compileDatasetPreviews(input: CanonicalDefinition): Record<string, CompiledOperation>;
497
500
 
498
- export { type BuildContextResult, COMPILER_MAJOR, CONST_TRUE, type ClientManifest, type ClientManifestOperation, type CompileContext, type CompileNamespaceInput, type CompileNamespaceResult, type CompilerDiagnostic, DATASET_PREVIEW_LIMIT, DEFAULT_LIMIT, type DiagnosticSeverity, DocumentCompileError, MAX_LIMIT, type MutationOp, type MutationRootInfo, NamespaceCompileError, OperationLowerer, PLATFORM_USER_TABLE, type ParseDocumentsResult, type ParsedDocument, type PinnedColumn, type QueryRootInfo, type RestrictedOperation, type SourceDocument, type SqlQueryInput, andPred, buildCompileContext, canonicalOperationText, classifyInputType, collectPinnedColumns, compileDatasetPreviews, compileNamespace, compileNamespaceOrThrow, compileOperationDocuments, compileRuntimeSql, computeOpHash, computeOpsHash, computePinnedColumnsByTable, computeSchemaHash, flattenOperation, injectPolicies, inputTypeName, isSqlOperationDocument, lowerOperation, mutationRootNames, parseDocuments, parseSdl, platformUserMeta, predContains, predContainsExists, queryRootNames, restrictOperation, sqlQueryInputSchema, typeNameForTable, walkPred };
501
+ /**
502
+ * PostgreSQL omits pinned builtins from pg_depend, so catalog checks cannot
503
+ * enforce the function boundary alone. Parse every expression before either
504
+ * admission or DDL, including nested queries and aggregate/window clauses.
505
+ * Catalog validation remains responsible for resolved functions/operators.
506
+ */
507
+ declare function validateManagedViewQueries({ canonical }: {
508
+ canonical: CanonicalDefinition;
509
+ }): Promise<void>;
510
+
511
+ export { type BuildContextResult, COMPILER_MAJOR, CONST_TRUE, type ClientManifest, type ClientManifestOperation, type CompileContext, type CompileNamespaceInput, type CompileNamespaceResult, type CompilerDiagnostic, DATASET_PREVIEW_LIMIT, DEFAULT_LIMIT, type DiagnosticSeverity, DocumentCompileError, MAX_LIMIT, type MutationOp, type MutationRootInfo, NamespaceCompileError, OperationLowerer, PLATFORM_USER_TABLE, type ParseDocumentsResult, type ParsedDocument, type PinnedColumn, type QueryRootInfo, type RestrictedOperation, type SourceDocument, type SqlQueryInput, andPred, buildCompileContext, canonicalOperationText, classifyInputType, collectPinnedColumns, compileDatasetPreviews, compileNamespace, compileNamespaceOrThrow, compileOperationDocuments, compileRuntimeSql, computeOpHash, computeOpsHash, computePinnedColumnsByTable, computeSchemaHash, flattenOperation, injectPolicies, inputTypeName, isSqlOperationDocument, lowerOperation, mutationRootNames, parseDocuments, parseSdl, platformUserMeta, predContains, predContainsExists, queryRootNames, restrictOperation, sqlQueryInputSchema, typeNameForTable, validateManagedViewQueries, walkPred };
package/dist/compiler.js CHANGED
@@ -6,12 +6,17 @@ import { PgParser } from '@supabase/pg-parser';
6
6
  // src/compiler-gql/artifact.ts
7
7
 
8
8
  // src/constants.ts
9
+ var NAMESPACE_PATTERN = /^(?!(?:events|native_sources)$)[a-z][a-z0-9_]{0,40}$/;
9
10
  var COLUMN_IDENTIFIER_PATTERN = /^_?[a-z][a-z0-9_]*$/;
10
11
  var MAX_IDENTIFIER_LENGTH = 63;
11
12
  var CAPABILITY_KEY_PATTERN = /^(?:org\/|app\/[a-z][a-z0-9-]{0,63}\/)?[a-z0-9-]{1,64}$/;
13
+ var MAX_PERSISTED_READ_ROWS = 50001;
12
14
  var QUALIFIED_TABLE_PATTERN = /^[a-z][a-z0-9_]{0,62}\.[a-z][a-z0-9_]{0,62}$/;
13
15
  var ORM_SCHEMA_PREFIX = "orm_";
14
- var ormSchemaName = (namespace) => `${ORM_SCHEMA_PREFIX}${namespace}`;
16
+ var ormSchemaName = (namespace) => {
17
+ if (!NAMESPACE_PATTERN.test(namespace)) throw new Error(`invalid or reserved ORM namespace '${namespace}'`);
18
+ return `${ORM_SCHEMA_PREFIX}${namespace}`;
19
+ };
15
20
  var CELL_PARTS = ["at", "by", "rev", "source", "conflicts"];
16
21
  var CELL_PART_SUFFIX = {
17
22
  at: "__at",
@@ -387,6 +392,7 @@ function validateResolvedView({ view, tables, views = [] }) {
387
392
  function validateSource({ scope, source, table }) {
388
393
  const { fail: fail4 } = scope;
389
394
  if (source.kind === "etl" && table.source === void 0) fail4(`etl source '${source.name}' must reference a synced table`);
395
+ if (source.kind === "etl" && table.primaryKey?.length === 0) fail4(`etl source '${source.name}' cannot use an unkeyed synced table for resolved row identity`);
390
396
  if (source.kind !== "etl" && (table.internal !== true || table.cellMetadata !== true)) {
391
397
  fail4(`${source.kind} source '${source.name}' must reference an internal table with cell metadata`);
392
398
  }
@@ -618,6 +624,163 @@ function isSingleViewQuerySql(value) {
618
624
  }
619
625
  return state === "normal" && !/\$[a-z_0-9]*\$/i.test(query);
620
626
  }
627
+ var VIEW_QUERY_FUNCTION_ALLOWLIST = [
628
+ "abs",
629
+ "age",
630
+ "array_agg",
631
+ "array_append",
632
+ "array_cat",
633
+ "array_dims",
634
+ "array_length",
635
+ "array_lower",
636
+ "array_ndims",
637
+ "array_position",
638
+ "array_positions",
639
+ "array_prepend",
640
+ "array_remove",
641
+ "array_replace",
642
+ "array_to_string",
643
+ "array_trim",
644
+ "array_upper",
645
+ "ascii",
646
+ "avg",
647
+ "bit_and",
648
+ "bit_or",
649
+ "bit_xor",
650
+ "bool_and",
651
+ "bool_or",
652
+ "btrim",
653
+ "cardinality",
654
+ "cbrt",
655
+ "ceil",
656
+ "ceiling",
657
+ "char_length",
658
+ "character_length",
659
+ "chr",
660
+ "concat",
661
+ "concat_ws",
662
+ "count",
663
+ "cume_dist",
664
+ "date_bin",
665
+ "date_part",
666
+ "date_trunc",
667
+ "decode",
668
+ "degrees",
669
+ "dense_rank",
670
+ "encode",
671
+ "enum_first",
672
+ "enum_last",
673
+ "enum_range",
674
+ "every",
675
+ "exp",
676
+ "first_value",
677
+ "floor",
678
+ "format",
679
+ "grouping",
680
+ "initcap",
681
+ "json_agg",
682
+ "json_array_length",
683
+ "json_build_array",
684
+ "json_build_object",
685
+ "json_extract_path",
686
+ "json_extract_path_text",
687
+ "json_object_agg",
688
+ "json_object_keys",
689
+ "json_strip_nulls",
690
+ "json_typeof",
691
+ "jsonb_agg",
692
+ "jsonb_array_length",
693
+ "jsonb_build_array",
694
+ "jsonb_build_object",
695
+ "jsonb_extract_path",
696
+ "jsonb_extract_path_text",
697
+ "jsonb_insert",
698
+ "jsonb_object_agg",
699
+ "jsonb_object_keys",
700
+ "jsonb_path_exists",
701
+ "jsonb_path_match",
702
+ "jsonb_path_query",
703
+ "jsonb_path_query_array",
704
+ "jsonb_path_query_first",
705
+ "jsonb_pretty",
706
+ "jsonb_set",
707
+ "jsonb_set_lax",
708
+ "jsonb_strip_nulls",
709
+ "jsonb_typeof",
710
+ "justify_days",
711
+ "justify_hours",
712
+ "justify_interval",
713
+ "lag",
714
+ "last_value",
715
+ "lead",
716
+ "left",
717
+ "length",
718
+ "ln",
719
+ "log",
720
+ "lower",
721
+ "lpad",
722
+ "ltrim",
723
+ "make_date",
724
+ "make_interval",
725
+ "make_time",
726
+ "make_timestamp",
727
+ "make_timestamptz",
728
+ "max",
729
+ "md5",
730
+ "min",
731
+ "mod",
732
+ "nth_value",
733
+ "ntile",
734
+ "octet_length",
735
+ "percent_rank",
736
+ "pi",
737
+ "power",
738
+ "quote_ident",
739
+ "quote_literal",
740
+ "quote_nullable",
741
+ "radians",
742
+ "rank",
743
+ "regexp_count",
744
+ "regexp_instr",
745
+ "regexp_like",
746
+ "regexp_match",
747
+ "regexp_matches",
748
+ "regexp_replace",
749
+ "regexp_split_to_array",
750
+ "regexp_split_to_table",
751
+ "repeat",
752
+ "replace",
753
+ "reverse",
754
+ "right",
755
+ "round",
756
+ "row_number",
757
+ "rpad",
758
+ "rtrim",
759
+ "sign",
760
+ "split_part",
761
+ "sqrt",
762
+ "starts_with",
763
+ "string_agg",
764
+ "string_to_array",
765
+ "strpos",
766
+ "substr",
767
+ "substring",
768
+ "sum",
769
+ "timezone",
770
+ "to_ascii",
771
+ "to_char",
772
+ "to_date",
773
+ "to_hex",
774
+ "to_json",
775
+ "to_jsonb",
776
+ "to_number",
777
+ "to_timestamp",
778
+ "translate",
779
+ "trunc",
780
+ "unnest",
781
+ "upper",
782
+ "width_bucket"
783
+ ];
621
784
  var canonicalActionSchema = z.object({
622
785
  name: identifier2,
623
786
  requires: z.string().max(1e3).optional(),
@@ -678,7 +841,7 @@ var canonicalTableSchema = z.object({
678
841
  /** Synced/reflected read-only tables (`source: schema.table`) — v1 semantics. */
679
842
  source: z.string().regex(QUALIFIED_TABLE_PATTERN).optional(),
680
843
  columns: z.array(canonicalColumnSchema).default([]),
681
- primaryKey: z.array(columnIdentifier).min(1).max(8).optional(),
844
+ primaryKey: z.array(columnIdentifier).max(8).optional(),
682
845
  links: z.array(canonicalLinkSchema).default([]),
683
846
  indexes: z.array(canonicalIndexSchema).default([]),
684
847
  /** Omitted by old definitions, preserving their existing content hashes. */
@@ -714,6 +877,29 @@ var canonicalTableSchema = z.object({
714
877
  * data plane. Omitted otherwise.
715
878
  */
716
879
  materializes: identifier2.optional()
880
+ }).superRefine((table, ctx) => {
881
+ if (table.primaryKey?.length !== 0) return;
882
+ const fail4 = (message, path = ["primaryKey"]) => ctx.addIssue({ code: "custom", path, message: `unkeyed table '${table.table}' ${message}` });
883
+ if (table.source === void 0 || table.columns.length === 0) {
884
+ fail4("requires a typed synced source");
885
+ }
886
+ if ([table.policies.write, table.policies.insert, table.policies.update].some((policy) => policy !== void 0)) {
887
+ fail4("is read-only and cannot declare write policies", ["policies"]);
888
+ }
889
+ const ownedCollections = [table.indexes, table.actions, table.checks, table.foreignKeys, table.cowMutations];
890
+ const ownedMetadata = [table.cellMetadata, table.internal, table.materializes, table.renamedFrom];
891
+ if (ownedCollections.some((items) => items !== void 0 && items.length > 0) || ownedMetadata.some((value) => value !== void 0)) {
892
+ fail4("cannot declare owned storage, constraints or identity operations");
893
+ }
894
+ for (const [index, column] of table.columns.entries()) {
895
+ const ownedColumnMetadata = [column.default, column.defaultFn, column.onUpdateFn, column.renamedFrom, column.cast, column.cell];
896
+ if (column.unique || ownedColumnMetadata.some((value) => value !== void 0)) {
897
+ fail4("cannot declare column uniqueness, mutation defaults or migration metadata", ["columns", index]);
898
+ }
899
+ }
900
+ for (const [index, link] of table.links.entries()) {
901
+ if (link.enforce || link.kind === "toMany") fail4("cannot provide an identity for this link", ["links", index]);
902
+ }
717
903
  });
718
904
  var MAX_VIEW_QUERY = 1e5;
719
905
  var MAX_RESOLVED_VIEW_QUERY = 5e6;
@@ -770,6 +956,26 @@ var canonicalViewSchema = z.object({
770
956
  }
771
957
  }
772
958
  });
959
+ function validateUnkeyedLinkIdentities({ definition, ctx }) {
960
+ const tables = new Map(definition.tables.map((table) => [table.table, table]));
961
+ const relations = [
962
+ ...definition.tables.map((table, index) => ({ kind: "tables", index, links: table.links })),
963
+ ...(definition.views ?? []).map((view, index) => ({ kind: "views", index, links: view.links }))
964
+ ];
965
+ for (const relation of relations) {
966
+ for (const [linkIndex, link] of relation.links.entries()) {
967
+ if (link.kind !== "toOne" || link.namespace !== void 0 && link.namespace !== definition.namespace) continue;
968
+ const target = link.target === void 0 ? void 0 : tables.get(link.target);
969
+ if (target?.source !== void 0 && target.primaryKey?.length === 0) {
970
+ ctx.addIssue({
971
+ code: "custom",
972
+ path: [relation.kind, relation.index, "links", linkIndex],
973
+ message: `link '${link.name}' cannot use unkeyed table '${target.table}' as a to-one identity`
974
+ });
975
+ }
976
+ }
977
+ }
978
+ }
773
979
  var CELL_PART_TYPES = {
774
980
  at: "timestamptz",
775
981
  by: "text",
@@ -833,7 +1039,7 @@ var exportGateSchema = z.union([z.literal(true), z.object({ capability: z.string
833
1039
  var relationExportSchema = z.object({ read: exportGateSchema, write: exportGateSchema.optional() }).strict();
834
1040
  var localDefinitionSchema = z.object({
835
1041
  definition_format: z.literal(CANONICAL_FORMAT),
836
- namespace: identifier2.max(41),
1042
+ namespace: identifier2.max(41).regex(NAMESPACE_PATTERN, "invalid or reserved ORM namespace"),
837
1043
  description: z.string().max(2e3).optional(),
838
1044
  enums: z.array(canonicalEnumSchema).default([]),
839
1045
  tables: z.array(canonicalTableSchema).min(0).max(500),
@@ -867,6 +1073,7 @@ var localDefinitionSchema = z.object({
867
1073
  }
868
1074
  }
869
1075
  const fkTables = new Map(definition.tables.map((table) => [table.table, table]));
1076
+ validateUnkeyedLinkIdentities({ definition, ctx });
870
1077
  const relations = /* @__PURE__ */ new Set([...fkTables.keys(), ...(definition.views ?? []).map((view) => view.view)]);
871
1078
  for (const name2 of Object.keys(definition.exports ?? {})) {
872
1079
  if (!relations.has(name2)) ctx.addIssue({ code: "custom", path: ["exports", name2], message: `export '${name2}' is not a local object` });
@@ -1189,7 +1396,7 @@ var limitIrSchema = z.object({
1189
1396
  variable: z.string().optional(),
1190
1397
  value: z.number().int().positive().optional(),
1191
1398
  default: z.number().int().positive(),
1192
- max: z.number().int().positive()
1399
+ max: z.number().int().positive().max(MAX_PERSISTED_READ_ROWS)
1193
1400
  });
1194
1401
  var selectionIrSchema = z.lazy(
1195
1402
  () => z.object({
@@ -2826,6 +3033,7 @@ function buildCompileContext(rawCanonical) {
2826
3033
  const platformMeta = usesPlatformUsers ? platformUserMeta() : void 0;
2827
3034
  if (platformMeta) tables[PLATFORM_USER_TABLE] = platformMeta;
2828
3035
  const syncedTableNames = new Set(canonical.tables.filter((t) => t.source !== void 0).map((t) => t.table));
3036
+ const unkeyedSyncedTableNames = new Set(relations.filter((table) => table.source !== void 0 && table.primaryKey?.length === 0).map((table) => table.table));
2829
3037
  const viewDependencies = new Map((canonical.views ?? []).map((view) => [view.view, view.dependencies]));
2830
3038
  const claimsGatedMemo = /* @__PURE__ */ new Map();
2831
3039
  const relationIsClaimsGated = (name2) => {
@@ -2839,6 +3047,7 @@ function buildCompileContext(rawCanonical) {
2839
3047
  };
2840
3048
  for (const table of relations) {
2841
3049
  const isView = viewNames.has(table.table);
3050
+ const readOnly = isView || unkeyedSyncedTableNames.has(table.table);
2842
3051
  const owner = owners.get(table.table);
2843
3052
  const entries = resolveColumnEntries(table, enums, diagnostics);
2844
3053
  columnEntries.set(table.table, entries);
@@ -2886,13 +3095,13 @@ function buildCompileContext(rawCanonical) {
2886
3095
  // are claims-gated — see tableMetaIrSchema.claimsGated.
2887
3096
  claimsGated: relationIsClaimsGated(table.table),
2888
3097
  columns: entries.map(({ deprecated: _deprecated, onUpdateFn: _onUpdateFn, ...column }) => column),
2889
- primaryKey: isView ? primaryKey : primaryKey.length > 0 ? primaryKey : ["id"],
3098
+ primaryKey: isView || unkeyedSyncedTableNames.has(table.table) ? primaryKey : primaryKey.length > 0 ? primaryKey : ["id"],
2890
3099
  links: [],
2891
3100
  filterable: data.map((c) => c.name),
2892
3101
  orderable: data.filter((c) => c.type !== "jsonb").map((c) => c.name),
2893
- insertable: resolved ? resolvedWritable(data) : isView ? [] : active.map((c) => c.name),
2894
- settable: resolved ? resolvedWritable(data).filter((name2) => !primaryKey.includes(name2)) : isView ? [] : active.filter((c) => !c.onUpdateFn).map((c) => c.name),
2895
- appendable: isView ? [] : active.filter((c) => c.type === "jsonb").map((c) => c.name),
3102
+ insertable: resolved ? resolvedWritable(data) : readOnly ? [] : active.map((c) => c.name),
3103
+ settable: resolved ? resolvedWritable(data).filter((name2) => !primaryKey.includes(name2)) : readOnly ? [] : active.filter((c) => !c.onUpdateFn).map((c) => c.name),
3104
+ appendable: readOnly ? [] : active.filter((c) => c.type === "jsonb").map((c) => c.name),
2896
3105
  ...touchOnUpdate.length > 0 ? { touchOnUpdate } : {},
2897
3106
  ...cells ? { cells } : {},
2898
3107
  ...resolved ? { resolved: resolvedMeta({ resolved, pk: primaryKey[0] }) } : {},
@@ -2916,6 +3125,16 @@ function buildCompileContext(rawCanonical) {
2916
3125
  });
2917
3126
  continue;
2918
3127
  }
3128
+ const identityTable = link.kind === "toOne" ? targetName : link.kind === "toMany" ? table.table : void 0;
3129
+ if (identityTable !== void 0 && unkeyedSyncedTableNames.has(identityTable)) {
3130
+ diagnostics.push({
3131
+ severity: "error",
3132
+ code: "unkeyed-link-identity",
3133
+ table: table.table,
3134
+ message: `link '${link.name}' cannot use unkeyed table '${identityTable}' as a row identity`
3135
+ });
3136
+ continue;
3137
+ }
2919
3138
  const targetEntries = targetName === PLATFORM_USER_TABLE ? target.columns.map((c) => ({ ...c, deprecated: false, onUpdateFn: false })) : columnEntries.get(targetName);
2920
3139
  const onEntries = link.kind === "toMany" ? targetEntries : entries;
2921
3140
  const referencesEntries = link.kind === "toMany" ? entries : targetEntries;
@@ -2981,7 +3200,7 @@ function buildCompileContext(rawCanonical) {
2981
3200
  compilePolicyFor("write", writePolicies);
2982
3201
  compilePolicyFor("insert", insertPolicies);
2983
3202
  compilePolicyFor("update", updatePolicies);
2984
- if (isView && table.resolved === void 0) {
3203
+ if (isView && table.resolved === void 0 || unkeyedSyncedTableNames.has(table.table)) {
2985
3204
  writePolicies.set(table.table, CONST_FALSE);
2986
3205
  insertPolicies.set(table.table, CONST_FALSE);
2987
3206
  updatePolicies.set(table.table, CONST_FALSE);
@@ -3056,7 +3275,7 @@ function buildCompileContext(rawCanonical) {
3056
3275
  const queryRoots = /* @__PURE__ */ new Map();
3057
3276
  const mutationRoots = /* @__PURE__ */ new Map();
3058
3277
  for (const table of relations) {
3059
- if (table.internal === true) continue;
3278
+ if (table.internal === true || unkeyedSyncedTableNames.has(table.table)) continue;
3060
3279
  const roots = queryRootNames(table.table);
3061
3280
  queryRoots.set(roots.select, { kind: "select", table: table.table });
3062
3281
  if ((tables[table.table]?.primaryKey.length ?? 0) > 0) {
@@ -3436,8 +3655,10 @@ var OperationLowerer = class {
3436
3655
  constructor(ctx, restricted) {
3437
3656
  this.ctx = ctx;
3438
3657
  this.variables = new Map(restricted.variables.map((v) => [v.name, v]));
3658
+ this.maxRows = restricted.readBudget ?? MAX_LIMIT;
3439
3659
  }
3440
3660
  variables;
3661
+ maxRows;
3441
3662
  meta(table) {
3442
3663
  return this.ctx.tables[table];
3443
3664
  }
@@ -3641,13 +3862,13 @@ var OperationLowerer = class {
3641
3862
  });
3642
3863
  }
3643
3864
  lowerLimit(node) {
3644
- const base2 = { default: DEFAULT_LIMIT, max: MAX_LIMIT };
3865
+ const base2 = { default: DEFAULT_LIMIT, max: this.maxRows };
3645
3866
  if (!node) return base2;
3646
3867
  if (node.kind === Kind.VARIABLE) return { ...base2, variable: node.name.value };
3647
3868
  if (node.kind === Kind.INT) {
3648
3869
  const value = Number(node.value);
3649
3870
  if (value < 1) fail2("limit must be a positive integer", "invalid-limit");
3650
- return { ...base2, value: Math.min(value, MAX_LIMIT) };
3871
+ return { ...base2, value: Math.min(value, this.maxRows) };
3651
3872
  }
3652
3873
  fail2("limit must be an integer literal or a variable", "invalid-limit");
3653
3874
  }
@@ -4446,7 +4667,13 @@ var OperationLowerer = class {
4446
4667
  };
4447
4668
  function lowerOperation({ restricted, ctx }) {
4448
4669
  const lowerer = new OperationLowerer(ctx, restricted);
4449
- return lowerer.lowerRoots(restricted.definition, restricted.kind);
4670
+ const roots = lowerer.lowerRoots(restricted.definition, restricted.kind);
4671
+ if (restricted.readBudget === void 0) return roots;
4672
+ const root = roots[0];
4673
+ if (restricted.kind !== "query" || restricted.definition.selectionSet.selections.length !== 1 || roots.length !== 1 || root?.kind !== "select" || root.selection.relations.length > 0 || root.selection.aggregates.length > 0 || root.selection.cells !== void 0 || root.prefetch?.length) {
4674
+ fail2("@readBudget requires one flat select root without related selections, aggregates, cells, or prefetch", "invalid-read-budget");
4675
+ }
4676
+ return roots;
4450
4677
  }
4451
4678
  function isCellColumn(meta, name2) {
4452
4679
  if (!meta.cells) return false;
@@ -4505,6 +4732,19 @@ function parseDocuments({
4505
4732
  }
4506
4733
  return { documents: parsed, diagnostics };
4507
4734
  }
4735
+ function parseReadBudget(operation) {
4736
+ const directives = operation.directives?.filter((directive) => directive.name.value === "readBudget") ?? [];
4737
+ if (directives.length === 0) return void 0;
4738
+ const value = directives[0]?.arguments?.find((argument) => argument.name.value === "maxRows")?.value;
4739
+ if (operation.operation !== "query" || directives.length !== 1 || value?.kind !== Kind.INT) {
4740
+ throw new DocumentCompileError("@readBudget requires a literal maxRows on a query", "invalid-read-budget");
4741
+ }
4742
+ const maxRows = Number(value.value);
4743
+ if (!Number.isSafeInteger(maxRows) || maxRows < 1 || maxRows > MAX_PERSISTED_READ_ROWS) {
4744
+ throw new DocumentCompileError(`@readBudget(maxRows:) must be between 1 and ${MAX_PERSISTED_READ_ROWS}`, "invalid-read-budget");
4745
+ }
4746
+ return maxRows;
4747
+ }
4508
4748
  function unwrapType(node, variable) {
4509
4749
  let nullable = true;
4510
4750
  let list2 = false;
@@ -4665,6 +4905,7 @@ function restrictOperation({
4665
4905
  name: name2,
4666
4906
  kind: operation.operation === "mutation" ? "mutation" : "query",
4667
4907
  requires: parseRequires(operation),
4908
+ readBudget: parseReadBudget(operation),
4668
4909
  variables,
4669
4910
  definition: operation
4670
4911
  };
@@ -5705,6 +5946,118 @@ function bindNamedParameters({ sql, names }) {
5705
5946
  }
5706
5947
  return result;
5707
5948
  }
5949
+ var parser2;
5950
+ var functions = new Set(VIEW_QUERY_FUNCTION_ALLOWLIST);
5951
+ var scalarTypes = /* @__PURE__ */ new Set([
5952
+ "bool",
5953
+ "boolean",
5954
+ "int2",
5955
+ "int4",
5956
+ "int8",
5957
+ "integer",
5958
+ "bigint",
5959
+ "smallint",
5960
+ "numeric",
5961
+ "decimal",
5962
+ "float4",
5963
+ "float8",
5964
+ "real",
5965
+ "text",
5966
+ "varchar",
5967
+ "bpchar",
5968
+ "uuid",
5969
+ "date",
5970
+ "timestamp",
5971
+ "timestamptz",
5972
+ "time",
5973
+ "timetz",
5974
+ "interval",
5975
+ "json",
5976
+ "jsonb",
5977
+ "bytea"
5978
+ ]);
5979
+ var sqlValues = /* @__PURE__ */ new Set([
5980
+ "SVFOP_CURRENT_DATE",
5981
+ "SVFOP_CURRENT_TIME",
5982
+ "SVFOP_CURRENT_TIME_N",
5983
+ "SVFOP_CURRENT_TIMESTAMP",
5984
+ "SVFOP_CURRENT_TIMESTAMP_N",
5985
+ "SVFOP_LOCALTIME",
5986
+ "SVFOP_LOCALTIME_N",
5987
+ "SVFOP_LOCALTIMESTAMP",
5988
+ "SVFOP_LOCALTIMESTAMP_N",
5989
+ "SVFOP_CURRENT_ROLE",
5990
+ "SVFOP_CURRENT_USER",
5991
+ "SVFOP_USER",
5992
+ "SVFOP_SESSION_USER",
5993
+ "SVFOP_CURRENT_CATALOG",
5994
+ "SVFOP_CURRENT_SCHEMA"
5995
+ ]);
5996
+ async function validateManagedViewQueries({ canonical }) {
5997
+ for (const definition of [canonical, ...canonical.imports ?? []]) {
5998
+ for (const view of definition.views ?? []) {
5999
+ const fail4 = (message) => {
6000
+ throw new OrmDefinitionError(`managed view '${definition.namespace}/${view.view}': ${message}`);
6001
+ };
6002
+ parser2 ??= new PgParser({ version: 17 });
6003
+ const parsed = await parser2.parse(view.query);
6004
+ if (parsed.error || !parsed.tree) fail4("invalid SELECT query");
6005
+ const statements = list(obj(parsed.tree).stmts);
6006
+ if (statements.length !== 1 || !obj(obj(statements[0]).stmt).SelectStmt) fail4("one SELECT query is required");
6007
+ validateTree({ tree: parsed.tree, schema: ormSchemaName(definition.namespace), enums: definition.enums.map((entry) => entry.name), fail: fail4 });
6008
+ }
6009
+ }
6010
+ }
6011
+ function pgCatalogName(parts) {
6012
+ if (parts.length === 1) return parts[0];
6013
+ if (parts.length === 2 && parts[0] === "pg_catalog") return parts[1];
6014
+ return void 0;
6015
+ }
6016
+ function reviewedFunctionName({ parts, format }) {
6017
+ return format === "COERCE_SQL_SYNTAX" && parts.length === 2 && parts[0] === "pg_catalog" && parts[1] === "position" ? "strpos" : pgCatalogName(parts);
6018
+ }
6019
+ function validateTree({ tree, ...scope }) {
6020
+ const pending = [tree];
6021
+ while (pending.length) {
6022
+ const value = pending.pop();
6023
+ if (Array.isArray(value)) {
6024
+ pending.push(...value);
6025
+ continue;
6026
+ }
6027
+ if (!value || typeof value !== "object") continue;
6028
+ const node = obj(value);
6029
+ for (const [kind, child] of Object.entries(node)) {
6030
+ validateNode({ kind, child, scope });
6031
+ pending.push(child);
6032
+ }
6033
+ }
6034
+ }
6035
+ function validateNode({ kind, child, scope }) {
6036
+ const { fail: fail4 } = scope;
6037
+ if (kind.endsWith("Stmt") && kind !== "SelectStmt") fail4("only SELECT statements are permitted");
6038
+ if (kind === "FuncCall") {
6039
+ const call = obj(child);
6040
+ const parts = strings(call.funcname);
6041
+ const name2 = reviewedFunctionName({ parts, format: call.funcformat });
6042
+ if (!name2 || !functions.has(name2)) fail4(`only explicitly allowlisted pg_catalog functions are permitted (found '${parts.join(".")}')`);
6043
+ }
6044
+ if (kind === "typeName" || kind === "TypeName") validateType({ node: obj(child), ...scope });
6045
+ if (kind === "A_Expr" && pgCatalogName(strings(obj(child).name)) === void 0) fail4("only pg_catalog operators are permitted");
6046
+ if (kind === "SortBy" && list(obj(child).useOp).length && pgCatalogName(strings(obj(child).useOp)) === void 0) fail4("only pg_catalog sort operators are permitted");
6047
+ if (kind === "SQLValueFunction" && !sqlValues.has(String(obj(child).op))) fail4("unsupported SQL value expression");
6048
+ if (kind === "ParamRef") fail4("view queries cannot contain parameters");
6049
+ if (kind === "A_Indirection" && list(obj(child).indirection).some((entry) => !obj(entry).A_Indices)) {
6050
+ fail4("composite field notation is not permitted in managed views");
6051
+ }
6052
+ }
6053
+ function validateType({ node, schema, enums, fail: fail4 }) {
6054
+ const parts = strings(node.names);
6055
+ const builtin = pgCatalogName(parts);
6056
+ const managedEnum = parts.length === 1 && enums.includes(parts[0]) || parts.length === 2 && parts[0] === schema && enums.includes(parts[1]);
6057
+ if (node.setof || node.pct_type || !managedEnum && (!builtin || !scalarTypes.has(builtin))) {
6058
+ fail4(`casts may only use supported pg_catalog scalar types or declared enums (found '${parts.join(".")}')`);
6059
+ }
6060
+ }
5708
6061
 
5709
6062
  // src/sql/operations.ts
5710
6063
  function isSqlOperationDocument(path, contents) {
@@ -5720,7 +6073,13 @@ function isSqlOperationDocument(path, contents) {
5720
6073
  async function compileOperationDocuments(input) {
5721
6074
  const sqlDocuments = input.documents.filter((d) => isSqlOperationDocument(d.path, d.contents));
5722
6075
  const result = compileNamespace({ ...input, documents: input.documents.filter((d) => !isSqlOperationDocument(d.path, d.contents)) });
5723
- if (!result.ok || !sqlDocuments.length) return result;
6076
+ if (!result.ok) return result;
6077
+ const viewDiagnostics = await managedViewDiagnostics(input.canonical);
6078
+ if (viewDiagnostics.length) return { ok: false, diagnostics: [...result.diagnostics, ...viewDiagnostics] };
6079
+ if (!sqlDocuments.length) return result;
6080
+ return compileSqlDocuments({ input, result, sqlDocuments });
6081
+ }
6082
+ async function compileSqlDocuments({ input, result, sqlDocuments }) {
5724
6083
  for (const document of sqlDocuments) {
5725
6084
  try {
5726
6085
  const definition = defineOperation(JSON.parse(document.contents));
@@ -5814,6 +6173,18 @@ async function compileOperationDocuments(input) {
5814
6173
  result.manifest.opsHash = result.artifact.opsHash;
5815
6174
  return { ...result, artifact: normalizeIr(result.artifact) };
5816
6175
  }
6176
+ async function managedViewDiagnostics(canonical) {
6177
+ try {
6178
+ await validateManagedViewQueries({ canonical });
6179
+ return [];
6180
+ } catch (error) {
6181
+ return [{
6182
+ severity: "error",
6183
+ code: "invalid-view-query",
6184
+ message: error instanceof Error ? error.message : "invalid managed view query"
6185
+ }];
6186
+ }
6187
+ }
5817
6188
  var parameterName = z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/).max(63).refine((value) => !["__proto__", "prototype", "constructor"].includes(value), "reserved parameter name");
5818
6189
  var parameter = z.discriminatedUnion("type", [
5819
6190
  z.object({ type: z.literal("text"), value: z.string().nullable() }).strict(),
@@ -5895,6 +6266,6 @@ function compileDatasetPreviews(input) {
5895
6266
  return operations;
5896
6267
  }
5897
6268
 
5898
- 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, buildCompileContext, camelCase, canonicalOperationText, classifyInputType, collectPinnedColumns, 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, restrictOperation, sqlOperationDefinitionSchema, sqlQueryInputSchema, typeNameForTable, validateAppendInput, validateBoolExpInput, validateInsertInput, validateOnConflictInput, validateOrderByInput, validateSelectColumns, validateSetInput, walkPolicy, walkPred };
6269
+ 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, buildCompileContext, camelCase, canonicalOperationText, classifyInputType, collectPinnedColumns, 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, restrictOperation, sqlOperationDefinitionSchema, sqlQueryInputSchema, typeNameForTable, validateAppendInput, validateBoolExpInput, validateInsertInput, validateManagedViewQueries, validateOnConflictInput, validateOrderByInput, validateSelectColumns, validateSetInput, walkPolicy, walkPred };
5899
6270
  //# sourceMappingURL=compiler.js.map
5900
6271
  //# sourceMappingURL=compiler.js.map