@prisma-next/target-postgres 0.9.0 → 0.10.0-dev.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.
Files changed (124) hide show
  1. package/dist/{codec-ids-RvYfmUmi.d.mts → codec-ids-D9fJ4HP5.d.mts} +1 -1
  2. package/dist/{codec-ids-RvYfmUmi.d.mts.map → codec-ids-D9fJ4HP5.d.mts.map} +1 -1
  3. package/dist/codec-ids.d.mts +1 -1
  4. package/dist/{codec-types-667FxIW8.d.mts → codec-types-CRlHq7Cz.d.mts} +2 -2
  5. package/dist/{codec-types-667FxIW8.d.mts.map → codec-types-CRlHq7Cz.d.mts.map} +1 -1
  6. package/dist/codec-types.d.mts +1 -1
  7. package/dist/{codecs-DXeDABSO.d.mts → codecs-Dud5KDNk.d.mts} +2 -2
  8. package/dist/{codecs-DXeDABSO.d.mts.map → codecs-Dud5KDNk.d.mts.map} +1 -1
  9. package/dist/codecs.d.mts +1 -1
  10. package/dist/control.d.mts +1 -1
  11. package/dist/control.mjs +5 -5
  12. package/dist/{data-transform-COkGR6Ns.mjs → data-transform-CdtGUWp2.mjs} +1 -1
  13. package/dist/{data-transform-COkGR6Ns.mjs.map → data-transform-CdtGUWp2.mjs.map} +1 -1
  14. package/dist/{data-transform-B6p02mFJ.d.mts → data-transform-bmOKkygi.d.mts} +2 -2
  15. package/dist/{data-transform-B6p02mFJ.d.mts.map → data-transform-bmOKkygi.d.mts.map} +1 -1
  16. package/dist/data-transform.d.mts +1 -1
  17. package/dist/data-transform.mjs +1 -1
  18. package/dist/descriptor-meta-zrZzWmJF.mjs +91 -0
  19. package/dist/{descriptor-meta-DFUCClk_.mjs.map → descriptor-meta-zrZzWmJF.mjs.map} +1 -1
  20. package/dist/enum-planning.d.mts +1 -1
  21. package/dist/{errors-BiOloWUh.mjs → errors--zafB5_n.mjs} +1 -1
  22. package/dist/{errors-BiOloWUh.mjs.map → errors--zafB5_n.mjs.map} +1 -1
  23. package/dist/errors.mjs +1 -1
  24. package/dist/{issue-planner-BhWVYyE1.mjs → issue-planner-qalHRCI2.mjs} +179 -77
  25. package/dist/issue-planner-qalHRCI2.mjs.map +1 -0
  26. package/dist/issue-planner.d.mts +2 -2
  27. package/dist/issue-planner.d.mts.map +1 -1
  28. package/dist/issue-planner.mjs +1 -1
  29. package/dist/migration.d.mts +3 -3
  30. package/dist/migration.mjs +3 -3
  31. package/dist/{op-factory-call-c1zELk3U.d.mts → op-factory-call-Drccm_JD.d.mts} +3 -3
  32. package/dist/{op-factory-call-c1zELk3U.d.mts.map → op-factory-call-Drccm_JD.d.mts.map} +1 -1
  33. package/dist/{op-factory-call-DerP9BoT.mjs → op-factory-call-Zsrdty3k.mjs} +2 -2
  34. package/dist/{op-factory-call-DerP9BoT.mjs.map → op-factory-call-Zsrdty3k.mjs.map} +1 -1
  35. package/dist/op-factory-call.d.mts +1 -1
  36. package/dist/op-factory-call.mjs +1 -1
  37. package/dist/pack.d.mts +2 -2
  38. package/dist/pack.mjs +1 -1
  39. package/dist/{planner-DnzPpv1j.mjs → planner-C8yhbXOq.mjs} +84 -5
  40. package/dist/planner-C8yhbXOq.mjs.map +1 -0
  41. package/dist/{planner-ddl-builders-5QIyhBUF.mjs → planner-ddl-builders-DINYrbJ3.mjs} +4 -4
  42. package/dist/{planner-ddl-builders-5QIyhBUF.mjs.map → planner-ddl-builders-DINYrbJ3.mjs.map} +1 -1
  43. package/dist/planner-ddl-builders.d.mts +1 -1
  44. package/dist/planner-ddl-builders.mjs +1 -1
  45. package/dist/{planner-identity-values-BUYNOCwb.mjs → planner-identity-values-ojX-6cPV.mjs} +1 -1
  46. package/dist/{planner-identity-values-BUYNOCwb.mjs.map → planner-identity-values-ojX-6cPV.mjs.map} +1 -1
  47. package/dist/planner-identity-values.mjs +1 -1
  48. package/dist/{planner-produced-postgres-migration-FYsKh26I.mjs → planner-produced-postgres-migration-BqGLw7VT.mjs} +4 -4
  49. package/dist/{planner-produced-postgres-migration-FYsKh26I.mjs.map → planner-produced-postgres-migration-BqGLw7VT.mjs.map} +1 -1
  50. package/dist/{planner-produced-postgres-migration-D34ftfEK.d.mts → planner-produced-postgres-migration-c9lpjPv1.d.mts} +3 -3
  51. package/dist/{planner-produced-postgres-migration-D34ftfEK.d.mts.map → planner-produced-postgres-migration-c9lpjPv1.d.mts.map} +1 -1
  52. package/dist/planner-produced-postgres-migration.d.mts +1 -1
  53. package/dist/planner-produced-postgres-migration.mjs +1 -1
  54. package/dist/planner-schema-lookup-BGyukuzG.mjs +57 -0
  55. package/dist/planner-schema-lookup-BGyukuzG.mjs.map +1 -0
  56. package/dist/planner-schema-lookup.d.mts.map +1 -1
  57. package/dist/planner-schema-lookup.mjs +1 -1
  58. package/dist/{planner-sql-checks-Cd016Ycs.mjs → planner-sql-checks-BM4sD6Xc.mjs} +28 -11
  59. package/dist/planner-sql-checks-BM4sD6Xc.mjs.map +1 -0
  60. package/dist/planner-sql-checks.d.mts +11 -0
  61. package/dist/planner-sql-checks.d.mts.map +1 -1
  62. package/dist/planner-sql-checks.mjs +1 -1
  63. package/dist/{planner-target-details-iYJwzFHP.d.mts → planner-target-details-CIj61DUj.d.mts} +1 -1
  64. package/dist/{planner-target-details-iYJwzFHP.d.mts.map → planner-target-details-CIj61DUj.d.mts.map} +1 -1
  65. package/dist/planner-target-details.d.mts +1 -1
  66. package/dist/planner.d.mts +1 -1
  67. package/dist/planner.d.mts.map +1 -1
  68. package/dist/planner.mjs +1 -1
  69. package/dist/postgres-contract-serializer-CcZO9ukP.mjs +83 -0
  70. package/dist/postgres-contract-serializer-CcZO9ukP.mjs.map +1 -0
  71. package/dist/{postgres-enum-type-CrKq8au9.d.mts → postgres-enum-type-CNhPTDhy.d.mts} +1 -1
  72. package/dist/{postgres-enum-type-CrKq8au9.d.mts.map → postgres-enum-type-CNhPTDhy.d.mts.map} +1 -1
  73. package/dist/{postgres-migration-q4oWyNJE.mjs → postgres-migration-C5os-tkl.mjs} +3 -3
  74. package/dist/{postgres-migration-q4oWyNJE.mjs.map → postgres-migration-C5os-tkl.mjs.map} +1 -1
  75. package/dist/{postgres-migration-CiQzhcMe.d.mts → postgres-migration-jvsKgUDM.d.mts} +3 -3
  76. package/dist/{postgres-migration-CiQzhcMe.d.mts.map → postgres-migration-jvsKgUDM.d.mts.map} +1 -1
  77. package/dist/postgres-schema-BosNxhWq.mjs +163 -0
  78. package/dist/postgres-schema-BosNxhWq.mjs.map +1 -0
  79. package/dist/{render-ops-CkiuHSNj.mjs → render-ops-BC2PtCkj.mjs} +1 -1
  80. package/dist/{render-ops-CkiuHSNj.mjs.map → render-ops-BC2PtCkj.mjs.map} +1 -1
  81. package/dist/render-ops.d.mts +1 -1
  82. package/dist/render-ops.mjs +1 -1
  83. package/dist/{render-typescript-C9XWI8Ld.mjs → render-typescript-nRHbqLbI.mjs} +1 -1
  84. package/dist/{render-typescript-C9XWI8Ld.mjs.map → render-typescript-nRHbqLbI.mjs.map} +1 -1
  85. package/dist/render-typescript.mjs +1 -1
  86. package/dist/runtime.d.mts +7 -17
  87. package/dist/runtime.d.mts.map +1 -1
  88. package/dist/runtime.mjs +2 -2
  89. package/dist/{shared-DLYdmYo-.d.mts → shared-ByhSooBS.d.mts} +6 -4
  90. package/dist/shared-ByhSooBS.d.mts.map +1 -0
  91. package/dist/{statement-builders-BSIQMClE.mjs → statement-builders-vImtdfmM.mjs} +1 -1
  92. package/dist/{statement-builders-BSIQMClE.mjs.map → statement-builders-vImtdfmM.mjs.map} +1 -1
  93. package/dist/statement-builders.mjs +1 -1
  94. package/dist/{tables-Ce_Q0I8B.mjs → tables-DZ-5Yxi0.mjs} +3 -3
  95. package/dist/tables-DZ-5Yxi0.mjs.map +1 -0
  96. package/dist/{types-Dq74Z3eu.d.mts → types-D-XIpzHA.d.mts} +1 -1
  97. package/dist/types-D-XIpzHA.d.mts.map +1 -0
  98. package/dist/types.d.mts +125 -3
  99. package/dist/types.d.mts.map +1 -0
  100. package/dist/types.mjs +2 -1
  101. package/package.json +17 -17
  102. package/src/core/migrations/issue-planner.ts +131 -41
  103. package/src/core/migrations/operations/constraints.ts +1 -1
  104. package/src/core/migrations/operations/shared.ts +4 -2
  105. package/src/core/migrations/planner-ddl-builders.ts +2 -2
  106. package/src/core/migrations/planner-schema-lookup.ts +30 -6
  107. package/src/core/migrations/planner-sql-checks.ts +29 -11
  108. package/src/core/migrations/planner-strategies.ts +138 -43
  109. package/src/core/migrations/planner.ts +14 -1
  110. package/src/core/migrations/verify-postgres-namespaces.ts +87 -0
  111. package/src/core/postgres-contract-serializer.ts +139 -60
  112. package/src/core/postgres-schema.ts +208 -0
  113. package/src/exports/types.ts +5 -0
  114. package/dist/descriptor-meta-DFUCClk_.mjs +0 -124
  115. package/dist/issue-planner-BhWVYyE1.mjs.map +0 -1
  116. package/dist/planner-DnzPpv1j.mjs.map +0 -1
  117. package/dist/planner-schema-lookup--u9whY_Y.mjs +0 -29
  118. package/dist/planner-schema-lookup--u9whY_Y.mjs.map +0 -1
  119. package/dist/planner-sql-checks-Cd016Ycs.mjs.map +0 -1
  120. package/dist/postgres-contract-serializer-D5VJk6lo.mjs +0 -61
  121. package/dist/postgres-contract-serializer-D5VJk6lo.mjs.map +0 -1
  122. package/dist/shared-DLYdmYo-.d.mts.map +0 -1
  123. package/dist/tables-Ce_Q0I8B.mjs.map +0 -1
  124. package/dist/types-Dq74Z3eu.d.mts.map +0 -1
@@ -1,70 +1,149 @@
1
1
  import type { Contract } from '@prisma-next/contract/types';
2
+ import { SqlContractSerializerBase } from '@prisma-next/family-sql/ir';
2
3
  import {
3
- SqlContractSerializerBase,
4
- type SqlEntityHydrationFactory,
5
- } from '@prisma-next/family-sql/ir';
6
- import type { AuthoringEntityContext } from '@prisma-next/framework-components/authoring';
7
- import type { SqlStorage, SqlStorageTypeEntry } from '@prisma-next/sql-contract/types';
8
- import { postgresAuthoringEntityTypes } from './authoring';
9
- import { PostgresEnumType } from './postgres-enum-type';
4
+ type Namespace,
5
+ NamespaceBase,
6
+ UNBOUND_NAMESPACE_ID,
7
+ } from '@prisma-next/framework-components/ir';
8
+ import {
9
+ type SqlNamespaceTablesInput,
10
+ type SqlStorage,
11
+ StorageTable,
12
+ type StorageTableInput,
13
+ } from '@prisma-next/sql-contract/types';
14
+ import type { JsonObject } from '@prisma-next/utils/json';
15
+ import { PostgresEnumType, type PostgresEnumTypeInput } from './postgres-enum-type';
16
+ import { isPostgresSchema, PostgresSchema } from './postgres-schema';
17
+
18
+ export class PostgresContractSerializer extends SqlContractSerializerBase<Contract<SqlStorage>> {
19
+ constructor() {
20
+ // Postgres entity types (enums) are namespace-level and hydrated in
21
+ // hydrateSqlNamespaceEntry; there are no storage-level codec alias entities
22
+ // specific to Postgres, so the registry is empty.
23
+ super(new Map());
24
+ }
10
25
 
11
- /**
12
- * Build the hydration registry from this target pack's literal
13
- * `postgresAuthoringEntityTypes`. Extension-pack-contributed entity
14
- * types do not reach this registry today; the surface is honest for
15
- * in-tree consumers (Postgres pack only) and the slot stays
16
- * deserializable because the family-layer validator's
17
- * `StorageTypeEntrySchema` only admits kinds whose factory the
18
- * Postgres pack already ships.
19
- *
20
- * Future open (F14): lift the registry build to descriptor-composition
21
- * time, threading the composed `AuthoringContributions.entityTypes`
22
- * from extension packs, so a real extension pack shipping a
23
- * round-trip-needing entity type can be deserialized end-to-end.
24
- * Earned by the first such extension pack in tree.
25
- */
26
- function buildPostgresEntityTypeRegistry(): ReadonlyMap<string, SqlEntityHydrationFactory> {
27
- const ctx: AuthoringEntityContext = { family: 'sql', target: 'postgres' };
28
- const registry = new Map<string, SqlEntityHydrationFactory>();
29
- for (const descriptor of Object.values(postgresAuthoringEntityTypes)) {
30
- if (descriptor.kind !== 'entity') {
31
- continue;
26
+ protected override hydrateSqlNamespaceEntry(
27
+ nsId: string,
28
+ raw: Namespace | Record<string, unknown>,
29
+ ): Namespace | SqlNamespaceTablesInput {
30
+ if (raw instanceof NamespaceBase) {
31
+ return raw;
32
32
  }
33
- if (!('factory' in descriptor.output)) {
34
- continue;
33
+ const obj = raw as {
34
+ id?: string;
35
+ tables?: Record<string, unknown>;
36
+ types?: Record<string, unknown>;
37
+ };
38
+ const id = obj.id ?? nsId;
39
+ const tables = Object.fromEntries(
40
+ Object.entries(obj.tables ?? {}).map(([tableName, table]) => [
41
+ tableName,
42
+ table instanceof StorageTable ? table : new StorageTable(table as StorageTableInput),
43
+ ]),
44
+ );
45
+ const typeEntries = obj.types;
46
+ const hydratedNsTypes =
47
+ typeEntries !== undefined && Object.keys(typeEntries).length > 0
48
+ ? Object.fromEntries(
49
+ Object.entries(typeEntries).map(([typeName, entry]) => {
50
+ if (entry instanceof PostgresEnumType) {
51
+ return [typeName, entry];
52
+ }
53
+ const plain = entry as Record<string, unknown>;
54
+ const name = typeof plain['name'] === 'string' ? plain['name'] : typeName;
55
+ const nativeType =
56
+ typeof plain['nativeType'] === 'string' ? plain['nativeType'] : name;
57
+ const values = Array.isArray(plain['values']) ? (plain['values'] as string[]) : [];
58
+ return [
59
+ typeName,
60
+ new PostgresEnumType({ name, nativeType, values } as PostgresEnumTypeInput),
61
+ ];
62
+ }),
63
+ )
64
+ : undefined;
65
+
66
+ const emptyTables = Object.keys(tables).length === 0;
67
+ const emptyTypes = !hydratedNsTypes || Object.keys(hydratedNsTypes).length === 0;
68
+ if (id === UNBOUND_NAMESPACE_ID && emptyTables && emptyTypes) {
69
+ return PostgresSchema.unbound;
35
70
  }
36
- const factory = descriptor.output.factory as (
37
- input: never,
38
- ctx: AuthoringEntityContext,
39
- ) => SqlStorageTypeEntry;
40
- registry.set(descriptor.discriminator, (entry) => {
41
- if (entry instanceof PostgresEnumType) {
42
- return entry;
43
- }
44
- return factory(entry as never, ctx);
71
+ return new PostgresSchema({
72
+ id,
73
+ tables,
74
+ ...(hydratedNsTypes !== undefined ? { types: hydratedNsTypes } : {}),
45
75
  });
46
76
  }
47
- return registry;
48
- }
49
77
 
50
- /**
51
- * Postgres target `ContractSerializer` concretion. Inherits the full
52
- * SQL-family deserialization pipeline (structural validation +
53
- * hydration walker that materialises the SQL Contract IR class
54
- * hierarchy from the validated JSON envelope). Polymorphic
55
- * `storage.types` entries hydrate through the pack contribution registry
56
- * keyed by each entity type's declared `discriminator` (matching the
57
- * enumerable `kind` on the persisted JSON envelope).
58
- *
59
- * `serializeContract` falls through to the family-base default —
60
- * Postgres' contract is JSON-clean today (`PostgresEnumType`
61
- * instances are frozen with enumerable own properties, so
62
- * `JSON.stringify` produces the canonical envelope shape). Once
63
- * target-only fields land (e.g. per-target derived storage fields)
64
- * this is the home for stripping them from the persisted envelope.
65
- */
66
- export class PostgresContractSerializer extends SqlContractSerializerBase<Contract<SqlStorage>> {
67
- constructor() {
68
- super(buildPostgresEntityTypeRegistry());
78
+ override serializeContract(contract: Contract<SqlStorage>): JsonObject {
79
+ const { storage, ...rest } = contract;
80
+ const namespacesJson: Record<string, JsonObject> = {};
81
+ for (const [nsId, ns] of Object.entries(storage.namespaces)) {
82
+ if (isPostgresSchema(ns)) {
83
+ namespacesJson[nsId] = this.serializePostgresNamespace(ns, ns.id === UNBOUND_NAMESPACE_ID);
84
+ } else {
85
+ // Family-level SqlNamespacePayload / SqlUnboundNamespace haven't
86
+ // been promoted to a PostgresSchema instance yet (e.g. they came
87
+ // straight from the TS builder, which uses the family-shared
88
+ // SqlStorage constructor). Serialise them as postgres-schema /
89
+ // postgres-unbound-schema so the round-trip through
90
+ // deserializeContract hydrates them back into PostgresSchema
91
+ // instances.
92
+ const isUnboundSlot = nsId === UNBOUND_NAMESPACE_ID;
93
+ const nsTypes = (ns as { readonly types?: Readonly<Record<string, unknown>> }).types ?? {};
94
+ namespacesJson[nsId] = {
95
+ id: nsId,
96
+ kind: isUnboundSlot ? 'postgres-unbound-schema' : 'postgres-schema',
97
+ tables: Object.fromEntries(
98
+ Object.entries(ns.tables).map(([tableName, table]) => [
99
+ tableName,
100
+ this.serializeJsonValue(table) as JsonObject,
101
+ ]),
102
+ ),
103
+ types: Object.fromEntries(
104
+ Object.entries(nsTypes).map(([typeName, entry]) => [
105
+ typeName,
106
+ this.serializeJsonValue(entry) as JsonObject,
107
+ ]),
108
+ ),
109
+ };
110
+ }
111
+ }
112
+ const storageOut: Record<string, unknown> = {
113
+ storageHash: String(storage.storageHash),
114
+ namespaces: namespacesJson,
115
+ };
116
+ if (storage.types !== undefined) {
117
+ const typesOut: Record<string, JsonObject> = {};
118
+ for (const [name, entry] of Object.entries(storage.types)) {
119
+ typesOut[name] = this.serializeJsonValue(entry) as JsonObject;
120
+ }
121
+ storageOut['types'] = typesOut;
122
+ }
123
+ return {
124
+ ...rest,
125
+ storage: storageOut,
126
+ } as unknown as JsonObject;
127
+ }
128
+
129
+ private serializePostgresNamespace(ns: PostgresSchema, isUnboundSlot: boolean): JsonObject {
130
+ const tablesOut: Record<string, JsonObject> = {};
131
+ for (const [tableName, table] of Object.entries(ns.tables)) {
132
+ tablesOut[tableName] = this.serializeJsonValue(table) as JsonObject;
133
+ }
134
+ const typesOut: Record<string, JsonObject> = {};
135
+ for (const [typeName, ty] of Object.entries(ns.types)) {
136
+ typesOut[typeName] = this.serializeJsonValue(ty) as JsonObject;
137
+ }
138
+ return {
139
+ id: ns.id,
140
+ kind: isUnboundSlot ? 'postgres-unbound-schema' : 'postgres-schema',
141
+ tables: tablesOut,
142
+ types: typesOut,
143
+ };
144
+ }
145
+
146
+ private serializeJsonValue(value: unknown): unknown {
147
+ return JSON.parse(JSON.stringify(value)) as unknown;
69
148
  }
70
149
  }
@@ -0,0 +1,208 @@
1
+ import {
2
+ freezeNode,
3
+ NamespaceBase,
4
+ UNBOUND_NAMESPACE_ID,
5
+ } from '@prisma-next/framework-components/ir';
6
+ import {
7
+ type SqlNamespaceTablesInput,
8
+ type SqlStorage,
9
+ StorageTable,
10
+ type StorageTableInput,
11
+ } from '@prisma-next/sql-contract/types';
12
+ import { PostgresEnumType, type PostgresEnumTypeInput } from './postgres-enum-type';
13
+ import { escapeLiteral } from './sql-utils';
14
+
15
+ export interface PostgresSchemaInput {
16
+ readonly id: string;
17
+ readonly tables?: Record<string, StorageTable | StorageTableInput>;
18
+ readonly types?: Record<string, PostgresEnumType | PostgresEnumTypeInput>;
19
+ }
20
+
21
+ /**
22
+ * Postgres target `Namespace` concretion — a Postgres schema (`CREATE
23
+ * SCHEMA …`). Each Postgres `SqlStorage` carries a
24
+ * `namespaces: Record<NamespaceId, PostgresSchema>` map populated by
25
+ * the Postgres PSL interpreter from `namespace { … }` AST buckets.
26
+ *
27
+ * Qualifier emission is the rendering seam: DDL / SQL emission asks the
28
+ * namespace for its qualifier (`"<schema>"`) or for a qualified table
29
+ * name (`"<schema>"."<table>"`) and consumes the result polymorphically.
30
+ * The unbound singleton below overrides these methods to elide the
31
+ * prefix entirely — call sites stay polymorphic and never branch on
32
+ * `id === UNBOUND_NAMESPACE_ID`.
33
+ */
34
+ export class PostgresSchema extends NamespaceBase {
35
+ /**
36
+ * Stable singleton reference for the late-bound slot. Materialised
37
+ * lazily below the singleton subclass declaration so the static
38
+ * initialiser sees the subclass before assigning. Consumers always
39
+ * reach for `PostgresSchema.unbound` (or `PostgresUnboundSchema.instance`
40
+ * — same identity).
41
+ */
42
+ static unbound: PostgresUnboundSchema;
43
+
44
+ readonly kind = 'schema' as const;
45
+ readonly id: string;
46
+ readonly tables: Readonly<Record<string, StorageTable>>;
47
+ readonly types: Readonly<Record<string, PostgresEnumType>>;
48
+
49
+ constructor(input: PostgresSchemaInput) {
50
+ super();
51
+ this.id = input.id;
52
+ this.tables = Object.freeze(
53
+ Object.fromEntries(
54
+ Object.entries(input.tables ?? {}).map(([name, t]) => [
55
+ name,
56
+ t instanceof StorageTable ? t : new StorageTable(t),
57
+ ]),
58
+ ),
59
+ );
60
+ this.types = Object.freeze(
61
+ Object.fromEntries(
62
+ Object.entries(input.types ?? {}).map(([name, ty]) => [
63
+ name,
64
+ ty instanceof PostgresEnumType ? ty : new PostgresEnumType(ty),
65
+ ]),
66
+ ),
67
+ );
68
+ freezeNode(this);
69
+ }
70
+
71
+ /**
72
+ * The bare schema qualifier as it would appear in a rendered SQL
73
+ * fragment (already quoted). The unbound-schema singleton overrides
74
+ * this to return `''`.
75
+ */
76
+ qualifier(): string {
77
+ return `"${this.id}"`;
78
+ }
79
+
80
+ /**
81
+ * Qualify a table name with the schema prefix
82
+ * (`"<schema>"."<table>"`). The unbound-schema singleton overrides
83
+ * this to emit just `"<table>"` so the resolved DDL is unqualified
84
+ * and `search_path` decides where the object lands at runtime.
85
+ */
86
+ qualifyTable(tableName: string): string {
87
+ return `"${this.id}"."${tableName}"`;
88
+ }
89
+
90
+ /**
91
+ * Render a SQL string-literal containing the qualified-name form
92
+ * suitable for `to_regclass(...)` arguments (e.g. `'"public"."user"'`).
93
+ * The unbound singleton overrides this to elide the schema prefix
94
+ * (`'"user"'`) so `search_path` resolves the object at runtime.
95
+ */
96
+ regclassLiteral(name: string): string {
97
+ return `'${escapeLiteral(this.qualifyTable(name))}'`;
98
+ }
99
+
100
+ /**
101
+ * Render a SQL expression that evaluates to this namespace's schema
102
+ * name at runtime, ready to drop into a `WHERE table_schema = …` /
103
+ * `WHERE n.nspname = …` clause. Named schemas emit a quoted SQL
104
+ * literal (`'public'`); the unbound singleton overrides this to emit
105
+ * `current_schema()` so catalog queries match whichever schema the
106
+ * connection's `search_path` resolved at runtime.
107
+ */
108
+ schemaSqlExpression(): string {
109
+ return `'${escapeLiteral(this.id)}'`;
110
+ }
111
+
112
+ /**
113
+ * The bare schema name a DDL planner should target when emitting
114
+ * statements that need to identify this namespace in the live
115
+ * database (e.g. `CREATE TABLE "<ddlSchemaName>"."<table>" …`,
116
+ * catalog filters, planner conflict lookups). Named schemas resolve
117
+ * to their own id; the unbound singleton overrides this to project
118
+ * to `'public'` when a sibling public namespace exists in the same
119
+ * contract — and falls back to the framework sentinel otherwise so
120
+ * the planner can detect the missing-projection case explicitly.
121
+ */
122
+ ddlSchemaName(_storage: SqlStorage): string {
123
+ return this.id;
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Singleton subclass for the reserved sentinel namespace id
129
+ * (`UNBOUND_NAMESPACE_ID`) — the late-bound Postgres slot whose binding
130
+ * the connection's `search_path` resolves at runtime. Overrides
131
+ * qualifier emission to elide the schema prefix; call sites that consume
132
+ * `qualifier()` / `qualifyTable()` get unqualified output without
133
+ * branching on the namespace id.
134
+ *
135
+ * This is the target-side materialization of "the framework provides
136
+ * affordances; targets implement specifics": the framework names the
137
+ * sentinel; Postgres decides what late-bound means here (the table
138
+ * name, naked — the schema is supplied by the live connection's
139
+ * `search_path`).
140
+ */
141
+ export class PostgresUnboundSchema extends PostgresSchema {
142
+ static readonly instance: PostgresUnboundSchema = new PostgresUnboundSchema();
143
+
144
+ constructor(input?: PostgresSchemaInput) {
145
+ super(input ?? { id: UNBOUND_NAMESPACE_ID });
146
+ }
147
+
148
+ override qualifier(): string {
149
+ return '';
150
+ }
151
+
152
+ override qualifyTable(tableName: string): string {
153
+ return `"${tableName}"`;
154
+ }
155
+
156
+ override schemaSqlExpression(): string {
157
+ return 'current_schema()';
158
+ }
159
+
160
+ /**
161
+ * The unbound slot has no schema name of its own, so DDL emission
162
+ * projects it onto a sibling when one is available: if the contract
163
+ * carries a `public` namespace, the late-bound slot resolves to
164
+ * `'public'` (the default Postgres landing schema); otherwise it
165
+ * resolves to the framework sentinel `UNBOUND_NAMESPACE_ID` so the
166
+ * planner can recognise the unprojected case and route accordingly
167
+ * (e.g. emit a conflict instead of silently picking a schema).
168
+ */
169
+ override ddlSchemaName(storage: SqlStorage): string {
170
+ if (storage.namespaces['public'] !== undefined) {
171
+ return 'public';
172
+ }
173
+ return UNBOUND_NAMESPACE_ID;
174
+ }
175
+ }
176
+
177
+ PostgresSchema.unbound = PostgresUnboundSchema.instance;
178
+
179
+ /**
180
+ * Narrow an arbitrary namespace (or `undefined`) to `PostgresSchema`
181
+ * so callers can dispatch to the polymorphic emission methods without
182
+ * branching at the call site. Uses the structural `kind` discriminator
183
+ * (`'schema'`) rather than `instanceof` so the check survives realm /
184
+ * bundle / hot-reload boundaries — matching the rest of the IR's
185
+ * narrowing convention. `PostgresUnboundSchema` passes through because
186
+ * it inherits the same `kind: 'schema'` from `PostgresSchema`.
187
+ */
188
+ export function isPostgresSchema(ns: unknown): ns is PostgresSchema {
189
+ return (ns as { kind?: unknown } | null | undefined)?.kind === 'schema';
190
+ }
191
+
192
+ /**
193
+ * Target-supplied `Namespace` factory the Postgres target plumbs
194
+ * through `defineContract({ createNamespace })` and the SQL PSL
195
+ * interpreter. Returns the unbound singleton for the framework
196
+ * sentinel and a fresh `PostgresSchema` for any other coordinate.
197
+ *
198
+ * The factory has no per-call state — every named id deterministically
199
+ * maps to a distinct schema instance — so callers can pass it through
200
+ * by reference and trust the resulting `SqlStorage.namespaces` map to
201
+ * be value-stable for a given input set.
202
+ */
203
+ export function postgresCreateNamespace(input: SqlNamespaceTablesInput): PostgresSchema {
204
+ if (input.id === UNBOUND_NAMESPACE_ID) {
205
+ return new PostgresUnboundSchema(input);
206
+ }
207
+ return new PostgresSchema(input);
208
+ }
@@ -1,2 +1,7 @@
1
1
  export { PostgresEnumType, type PostgresEnumTypeInput } from '../core/postgres-enum-type';
2
+ export {
3
+ PostgresSchema,
4
+ PostgresUnboundSchema,
5
+ postgresCreateNamespace,
6
+ } from '../core/postgres-schema';
2
7
  export type { PostgresColumnDefault } from '../core/types';
@@ -1,124 +0,0 @@
1
- import { t as PostgresEnumType } from "./postgres-enum-type-DS-KLVRH.mjs";
2
- import { temporalAuthoringPresets } from "@prisma-next/family-sql/control";
3
- //#region src/core/authoring.ts
4
- const postgresAuthoringTypes = {};
5
- /**
6
- * Entity type contributions surface as top-level helpers on the
7
- * composed-helpers shape (e.g. `helpers.enum({...})`), flattened
8
- * alongside the built-in `model` / `rel` helpers. Pack contributions
9
- * still ship via the contribution data structure
10
- * `authoring.entityTypes.<name>`; the composed-helpers template
11
- * performs the rename in the type system.
12
- *
13
- * `enum` is the first real consumer of the entities-namespace mechanism:
14
- * the factory constructs a `PostgresEnumType` IR-class instance from
15
- * the user-supplied input. Both authoring runtimes (TS DSL and PSL)
16
- * dispatch through this single contribution — PSL `enum Status { … }`
17
- * declarations are lowered by the interpreter into a factory call
18
- * with the parsed name + value list; TS DSL `helpers.enum({...})`
19
- * resolves through the same path. Removing this contribution makes
20
- * both surfaces fail with a "no entity helper named `enum`" type
21
- * error at the contract-definition site.
22
- */
23
- /**
24
- * The factory constructs a `PostgresEnumType` instance natively — the
25
- * `SqlStorage.types` slot accepts polymorphic IR (the framework
26
- * `StorageType` alphabet), so no cast is needed at the contribution
27
- * surface. The declared return type is the structural
28
- * `PostgresEnumStorageEntry` so the inferred contract type stays
29
- * portable (it names a type exported from
30
- * `@prisma-next/sql-contract/types`, a public surface every consumer
31
- * already imports). Sharpening the inferred contract type to surface
32
- * enum-specific narrowing through `EntityHelperFunction` is a
33
- * separable refinement and lives outside this PR.
34
- */
35
- const postgresAuthoringEntityTypes = { enum: {
36
- kind: "entity",
37
- discriminator: "postgres-enum",
38
- output: { factory: (input) => new PostgresEnumType(input) }
39
- } };
40
- const postgresTargetDescriptorMeta = {
41
- kind: "target",
42
- familyId: "sql",
43
- targetId: "postgres",
44
- id: "postgres",
45
- version: "0.0.1",
46
- capabilities: {},
47
- authoring: {
48
- type: postgresAuthoringTypes,
49
- field: {
50
- text: {
51
- kind: "fieldPreset",
52
- output: {
53
- codecId: "pg/text@1",
54
- nativeType: "text"
55
- }
56
- },
57
- int: {
58
- kind: "fieldPreset",
59
- output: {
60
- codecId: "pg/int4@1",
61
- nativeType: "int4"
62
- }
63
- },
64
- bigint: {
65
- kind: "fieldPreset",
66
- output: {
67
- codecId: "pg/int8@1",
68
- nativeType: "int8"
69
- }
70
- },
71
- float: {
72
- kind: "fieldPreset",
73
- output: {
74
- codecId: "pg/float8@1",
75
- nativeType: "float8"
76
- }
77
- },
78
- decimal: {
79
- kind: "fieldPreset",
80
- output: {
81
- codecId: "pg/numeric@1",
82
- nativeType: "numeric"
83
- }
84
- },
85
- boolean: {
86
- kind: "fieldPreset",
87
- output: {
88
- codecId: "pg/bool@1",
89
- nativeType: "bool"
90
- }
91
- },
92
- json: {
93
- kind: "fieldPreset",
94
- output: {
95
- codecId: "pg/jsonb@1",
96
- nativeType: "jsonb"
97
- }
98
- },
99
- bytes: {
100
- kind: "fieldPreset",
101
- output: {
102
- codecId: "pg/bytea@1",
103
- nativeType: "bytea"
104
- }
105
- },
106
- dateTime: {
107
- kind: "fieldPreset",
108
- output: {
109
- codecId: "pg/timestamptz@1",
110
- nativeType: "timestamptz"
111
- }
112
- },
113
- temporal: temporalAuthoringPresets({
114
- codecId: "pg/timestamptz@1",
115
- nativeType: "timestamptz"
116
- })
117
- },
118
- entityTypes: postgresAuthoringEntityTypes
119
- }
120
- };
121
- //#endregion
122
- export { postgresAuthoringEntityTypes as n, postgresTargetDescriptorMeta as t };
123
-
124
- //# sourceMappingURL=descriptor-meta-DFUCClk_.mjs.map