@prisma-next/family-sql 0.0.1 → 0.1.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.
- package/README.md +1 -0
- package/dist/exports/chunk-3HYKCN35.js +89 -0
- package/dist/exports/chunk-3HYKCN35.js.map +1 -0
- package/dist/exports/control.d.ts +102 -15
- package/dist/exports/control.js +61 -124
- package/dist/exports/control.js.map +1 -1
- package/dist/exports/runtime.d.ts +2 -238
- package/dist/exports/verify.d.ts +28 -0
- package/dist/exports/verify.js +11 -0
- package/dist/exports/verify.js.map +1 -0
- package/package.json +21 -14
- package/dist/exports/index-Bi3Sr19r.d.ts +0 -28
package/README.md
CHANGED
|
@@ -76,6 +76,7 @@ The descriptor is "pure data + factory" - it only provides the hook and factory
|
|
|
76
76
|
- **`./control`**: Control plane entry point for CLI/config usage (exports `SqlFamilyDescriptor`)
|
|
77
77
|
- **`./control-adapter`**: SQL control adapter interface (`SqlControlAdapter`, `SqlControlAdapterDescriptor`) for target-specific adapters
|
|
78
78
|
- **`./runtime`**: Runtime entry point (placeholder for future functionality)
|
|
79
|
+
- **`./verify`**: Verification utilities (`readMarker`, `readMarkerSql`, `parseContractMarkerRow`) for reading contract markers from databases
|
|
79
80
|
|
|
80
81
|
## Dependencies
|
|
81
82
|
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
// src/core/verify.ts
|
|
2
|
+
import { type } from "arktype";
|
|
3
|
+
var MetaSchema = type({ "[string]": "unknown" });
|
|
4
|
+
function parseMeta(meta) {
|
|
5
|
+
if (meta === null || meta === void 0) {
|
|
6
|
+
return {};
|
|
7
|
+
}
|
|
8
|
+
let parsed;
|
|
9
|
+
if (typeof meta === "string") {
|
|
10
|
+
try {
|
|
11
|
+
parsed = JSON.parse(meta);
|
|
12
|
+
} catch {
|
|
13
|
+
return {};
|
|
14
|
+
}
|
|
15
|
+
} else {
|
|
16
|
+
parsed = meta;
|
|
17
|
+
}
|
|
18
|
+
const result = MetaSchema(parsed);
|
|
19
|
+
if (result instanceof type.errors) {
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
var ContractMarkerRowSchema = type({
|
|
25
|
+
core_hash: "string",
|
|
26
|
+
profile_hash: "string",
|
|
27
|
+
"contract_json?": "unknown | null",
|
|
28
|
+
"canonical_version?": "number | null",
|
|
29
|
+
"updated_at?": "Date | string",
|
|
30
|
+
"app_tag?": "string | null",
|
|
31
|
+
"meta?": "unknown | null"
|
|
32
|
+
});
|
|
33
|
+
function parseContractMarkerRow(row) {
|
|
34
|
+
const result = ContractMarkerRowSchema(row);
|
|
35
|
+
if (result instanceof type.errors) {
|
|
36
|
+
const messages = result.map((p) => p.message).join("; ");
|
|
37
|
+
throw new Error(`Invalid contract marker row: ${messages}`);
|
|
38
|
+
}
|
|
39
|
+
const validatedRow = result;
|
|
40
|
+
const updatedAt = validatedRow.updated_at ? validatedRow.updated_at instanceof Date ? validatedRow.updated_at : new Date(validatedRow.updated_at) : /* @__PURE__ */ new Date();
|
|
41
|
+
return {
|
|
42
|
+
coreHash: validatedRow.core_hash,
|
|
43
|
+
profileHash: validatedRow.profile_hash,
|
|
44
|
+
contractJson: validatedRow.contract_json ?? null,
|
|
45
|
+
canonicalVersion: validatedRow.canonical_version ?? null,
|
|
46
|
+
updatedAt,
|
|
47
|
+
appTag: validatedRow.app_tag ?? null,
|
|
48
|
+
meta: parseMeta(validatedRow.meta)
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function readMarkerSql() {
|
|
52
|
+
return {
|
|
53
|
+
sql: `select
|
|
54
|
+
core_hash,
|
|
55
|
+
profile_hash,
|
|
56
|
+
contract_json,
|
|
57
|
+
canonical_version,
|
|
58
|
+
updated_at,
|
|
59
|
+
app_tag,
|
|
60
|
+
meta
|
|
61
|
+
from prisma_contract.marker
|
|
62
|
+
where id = $1`,
|
|
63
|
+
params: [1]
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
async function readMarker(driver) {
|
|
67
|
+
const markerStatement = readMarkerSql();
|
|
68
|
+
const queryResult = await driver.query(markerStatement.sql, markerStatement.params);
|
|
69
|
+
if (queryResult.rows.length === 0) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
const markerRow = queryResult.rows[0];
|
|
73
|
+
if (!markerRow) {
|
|
74
|
+
throw new Error("Database query returned unexpected result structure");
|
|
75
|
+
}
|
|
76
|
+
return parseContractMarkerRow(markerRow);
|
|
77
|
+
}
|
|
78
|
+
function collectSupportedCodecTypeIds(descriptors) {
|
|
79
|
+
void descriptors;
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export {
|
|
84
|
+
parseContractMarkerRow,
|
|
85
|
+
readMarkerSql,
|
|
86
|
+
readMarker,
|
|
87
|
+
collectSupportedCodecTypeIds
|
|
88
|
+
};
|
|
89
|
+
//# sourceMappingURL=chunk-3HYKCN35.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/core/verify.ts"],"sourcesContent":["import type { ContractMarkerRecord } from '@prisma-next/contract/types';\nimport type {\n ControlAdapterDescriptor,\n ControlDriverInstance,\n ControlExtensionDescriptor,\n ControlTargetDescriptor,\n} from '@prisma-next/core-control-plane/types';\nimport { type } from 'arktype';\n\nconst MetaSchema = type({ '[string]': 'unknown' });\n\nfunction parseMeta(meta: unknown): Record<string, unknown> {\n if (meta === null || meta === undefined) {\n return {};\n }\n\n let parsed: unknown;\n if (typeof meta === 'string') {\n try {\n parsed = JSON.parse(meta);\n } catch {\n return {};\n }\n } else {\n parsed = meta;\n }\n\n const result = MetaSchema(parsed);\n if (result instanceof type.errors) {\n return {};\n }\n\n return result as Record<string, unknown>;\n}\n\nconst ContractMarkerRowSchema = type({\n core_hash: 'string',\n profile_hash: 'string',\n 'contract_json?': 'unknown | null',\n 'canonical_version?': 'number | null',\n 'updated_at?': 'Date | string',\n 'app_tag?': 'string | null',\n 'meta?': 'unknown | null',\n});\n\n/**\n * Parses a contract marker row from database query result.\n * This is SQL-specific parsing logic (handles SQL row structure with snake_case columns).\n */\nexport function parseContractMarkerRow(row: unknown): ContractMarkerRecord {\n const result = ContractMarkerRowSchema(row);\n if (result instanceof type.errors) {\n const messages = result.map((p: { message: string }) => p.message).join('; ');\n throw new Error(`Invalid contract marker row: ${messages}`);\n }\n\n const validatedRow = result as {\n core_hash: string;\n profile_hash: string;\n contract_json?: unknown | null;\n canonical_version?: number | null;\n updated_at?: Date | string;\n app_tag?: string | null;\n meta?: unknown | null;\n };\n\n const updatedAt = validatedRow.updated_at\n ? validatedRow.updated_at instanceof Date\n ? validatedRow.updated_at\n : new Date(validatedRow.updated_at)\n : new Date();\n\n return {\n coreHash: validatedRow.core_hash,\n profileHash: validatedRow.profile_hash,\n contractJson: validatedRow.contract_json ?? null,\n canonicalVersion: validatedRow.canonical_version ?? null,\n updatedAt,\n appTag: validatedRow.app_tag ?? null,\n meta: parseMeta(validatedRow.meta),\n };\n}\n\n/**\n * Returns the SQL statement to read the contract marker.\n * This is a migration-plane helper (no runtime imports).\n * @internal - Used internally by readMarker(). Prefer readMarker() for Control Plane usage.\n */\nexport function readMarkerSql(): { readonly sql: string; readonly params: readonly unknown[] } {\n return {\n sql: `select\n core_hash,\n profile_hash,\n contract_json,\n canonical_version,\n updated_at,\n app_tag,\n meta\n from prisma_contract.marker\n where id = $1`,\n params: [1],\n };\n}\n\n/**\n * Reads the contract marker from the database using the provided driver.\n * Returns the parsed marker record or null if no marker is found.\n * This abstracts SQL-specific details from the Control Plane.\n *\n * @param driver - ControlDriverInstance instance for executing queries\n * @returns Promise resolving to ContractMarkerRecord or null if marker not found\n */\nexport async function readMarker(\n driver: ControlDriverInstance,\n): Promise<ContractMarkerRecord | null> {\n const markerStatement = readMarkerSql();\n const queryResult = await driver.query<{\n core_hash: string;\n profile_hash: string;\n contract_json: unknown | null;\n canonical_version: number | null;\n updated_at: Date | string;\n app_tag: string | null;\n meta: unknown | null;\n }>(markerStatement.sql, markerStatement.params);\n\n if (queryResult.rows.length === 0) {\n return null;\n }\n\n const markerRow = queryResult.rows[0];\n if (!markerRow) {\n // If rows array has length > 0 but first element is undefined, this is an unexpected result structure\n throw new Error('Database query returned unexpected result structure');\n }\n\n return parseContractMarkerRow(markerRow);\n}\n\n/**\n * Collects supported codec type IDs from adapter and extension manifests.\n * Returns a sorted, unique array of type IDs that are declared in the manifests.\n * This enables coverage checks by comparing contract column types against supported types.\n *\n * Note: This extracts type IDs from manifest type imports, not from runtime codec registries.\n * The manifests declare which codec types are available, but the actual type IDs\n * are defined in the codec-types TypeScript modules that are imported.\n *\n * For MVP, we return an empty array since extracting type IDs from TypeScript modules\n * would require runtime evaluation or static analysis. This can be enhanced later.\n */\nexport function collectSupportedCodecTypeIds<TFamilyId extends string, TTargetId extends string>(\n descriptors: ReadonlyArray<\n | ControlTargetDescriptor<TFamilyId, TTargetId>\n | ControlAdapterDescriptor<TFamilyId, TTargetId>\n | ControlExtensionDescriptor<TFamilyId, TTargetId>\n >,\n): readonly string[] {\n // For MVP, return empty array\n // Future enhancement: Extract type IDs from codec-types modules via static analysis\n // or require manifests to explicitly list supported type IDs\n void descriptors;\n return [];\n}\n"],"mappings":";AAOA,SAAS,YAAY;AAErB,IAAM,aAAa,KAAK,EAAE,YAAY,UAAU,CAAC;AAEjD,SAAS,UAAU,MAAwC;AACzD,MAAI,SAAS,QAAQ,SAAS,QAAW;AACvC,WAAO,CAAC;AAAA,EACV;AAEA,MAAI;AACJ,MAAI,OAAO,SAAS,UAAU;AAC5B,QAAI;AACF,eAAS,KAAK,MAAM,IAAI;AAAA,IAC1B,QAAQ;AACN,aAAO,CAAC;AAAA,IACV;AAAA,EACF,OAAO;AACL,aAAS;AAAA,EACX;AAEA,QAAM,SAAS,WAAW,MAAM;AAChC,MAAI,kBAAkB,KAAK,QAAQ;AACjC,WAAO,CAAC;AAAA,EACV;AAEA,SAAO;AACT;AAEA,IAAM,0BAA0B,KAAK;AAAA,EACnC,WAAW;AAAA,EACX,cAAc;AAAA,EACd,kBAAkB;AAAA,EAClB,sBAAsB;AAAA,EACtB,eAAe;AAAA,EACf,YAAY;AAAA,EACZ,SAAS;AACX,CAAC;AAMM,SAAS,uBAAuB,KAAoC;AACzE,QAAM,SAAS,wBAAwB,GAAG;AAC1C,MAAI,kBAAkB,KAAK,QAAQ;AACjC,UAAM,WAAW,OAAO,IAAI,CAAC,MAA2B,EAAE,OAAO,EAAE,KAAK,IAAI;AAC5E,UAAM,IAAI,MAAM,gCAAgC,QAAQ,EAAE;AAAA,EAC5D;AAEA,QAAM,eAAe;AAUrB,QAAM,YAAY,aAAa,aAC3B,aAAa,sBAAsB,OACjC,aAAa,aACb,IAAI,KAAK,aAAa,UAAU,IAClC,oBAAI,KAAK;AAEb,SAAO;AAAA,IACL,UAAU,aAAa;AAAA,IACvB,aAAa,aAAa;AAAA,IAC1B,cAAc,aAAa,iBAAiB;AAAA,IAC5C,kBAAkB,aAAa,qBAAqB;AAAA,IACpD;AAAA,IACA,QAAQ,aAAa,WAAW;AAAA,IAChC,MAAM,UAAU,aAAa,IAAI;AAAA,EACnC;AACF;AAOO,SAAS,gBAA+E;AAC7F,SAAO;AAAA,IACL,KAAK;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAUL,QAAQ,CAAC,CAAC;AAAA,EACZ;AACF;AAUA,eAAsB,WACpB,QACsC;AACtC,QAAM,kBAAkB,cAAc;AACtC,QAAM,cAAc,MAAM,OAAO,MAQ9B,gBAAgB,KAAK,gBAAgB,MAAM;AAE9C,MAAI,YAAY,KAAK,WAAW,GAAG;AACjC,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,YAAY,KAAK,CAAC;AACpC,MAAI,CAAC,WAAW;AAEd,UAAM,IAAI,MAAM,qDAAqD;AAAA,EACvE;AAEA,SAAO,uBAAuB,SAAS;AACzC;AAcO,SAAS,6BACd,aAKmB;AAInB,OAAK;AACL,SAAO,CAAC;AACV;","names":[]}
|
|
@@ -1,14 +1,101 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as _prisma_next_contract from '@prisma-next/contract';
|
|
3
|
-
import * as _prisma_next_contract_ir from '@prisma-next/contract/ir';
|
|
4
|
-
import { ContractIR } from '@prisma-next/contract/ir';
|
|
1
|
+
import { OperationRegistry } from '@prisma-next/operations';
|
|
5
2
|
import { ExtensionPackManifest } from '@prisma-next/contract/pack-manifest-types';
|
|
6
3
|
import { ControlFamilyInstance, ControlDriverInstance, VerifyDatabaseResult, VerifyDatabaseSchemaResult, SignDatabaseResult, EmitContractResult, ControlFamilyDescriptor, ControlTargetDescriptor, ControlAdapterDescriptor, ControlDriverDescriptor, ControlExtensionDescriptor } from '@prisma-next/core-control-plane/types';
|
|
7
|
-
import {
|
|
4
|
+
import { ContractIR as ContractIR$1 } from '@prisma-next/contract/ir';
|
|
5
|
+
import { TypesImportSpec as TypesImportSpec$1 } from '@prisma-next/contract/types';
|
|
8
6
|
import { CoreSchemaView } from '@prisma-next/core-control-plane/schema-view';
|
|
9
|
-
import { O as OperationRegistry } from './index-Bi3Sr19r.js';
|
|
10
7
|
import { SqlSchemaIR } from '@prisma-next/sql-schema-ir/types';
|
|
11
8
|
|
|
9
|
+
type StorageColumn = {
|
|
10
|
+
readonly nativeType: string;
|
|
11
|
+
readonly codecId: string;
|
|
12
|
+
readonly nullable: boolean;
|
|
13
|
+
};
|
|
14
|
+
type PrimaryKey = {
|
|
15
|
+
readonly columns: readonly string[];
|
|
16
|
+
readonly name?: string;
|
|
17
|
+
};
|
|
18
|
+
type UniqueConstraint = {
|
|
19
|
+
readonly columns: readonly string[];
|
|
20
|
+
readonly name?: string;
|
|
21
|
+
};
|
|
22
|
+
type Index = {
|
|
23
|
+
readonly columns: readonly string[];
|
|
24
|
+
readonly name?: string;
|
|
25
|
+
};
|
|
26
|
+
type ForeignKeyReferences = {
|
|
27
|
+
readonly table: string;
|
|
28
|
+
readonly columns: readonly string[];
|
|
29
|
+
};
|
|
30
|
+
type ForeignKey = {
|
|
31
|
+
readonly columns: readonly string[];
|
|
32
|
+
readonly references: ForeignKeyReferences;
|
|
33
|
+
readonly name?: string;
|
|
34
|
+
};
|
|
35
|
+
type StorageTable = {
|
|
36
|
+
readonly columns: Record<string, StorageColumn>;
|
|
37
|
+
readonly primaryKey?: PrimaryKey;
|
|
38
|
+
readonly uniques: ReadonlyArray<UniqueConstraint>;
|
|
39
|
+
readonly indexes: ReadonlyArray<Index>;
|
|
40
|
+
readonly foreignKeys: ReadonlyArray<ForeignKey>;
|
|
41
|
+
};
|
|
42
|
+
type SqlStorage = {
|
|
43
|
+
readonly tables: Record<string, StorageTable>;
|
|
44
|
+
};
|
|
45
|
+
type ModelField = {
|
|
46
|
+
readonly column: string;
|
|
47
|
+
};
|
|
48
|
+
type ModelStorage = {
|
|
49
|
+
readonly table: string;
|
|
50
|
+
};
|
|
51
|
+
type ModelDefinition = {
|
|
52
|
+
readonly storage: ModelStorage;
|
|
53
|
+
readonly fields: Record<string, ModelField>;
|
|
54
|
+
readonly relations: Record<string, unknown>;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* ContractIR types and factories for building contract intermediate representation.
|
|
59
|
+
* ContractIR is family-agnostic and used by authoring, emitter, and no-emit runtime.
|
|
60
|
+
*/
|
|
61
|
+
/**
|
|
62
|
+
* ContractIR represents the intermediate representation of a contract.
|
|
63
|
+
* It is family-agnostic and contains generic storage, models, and relations.
|
|
64
|
+
* Note: coreHash and profileHash are computed by the emitter, not part of the IR.
|
|
65
|
+
*/
|
|
66
|
+
interface ContractIR<TStorage extends Record<string, unknown> = Record<string, unknown>, TModels extends Record<string, unknown> = Record<string, unknown>, TRelations extends Record<string, unknown> = Record<string, unknown>> {
|
|
67
|
+
readonly schemaVersion: string;
|
|
68
|
+
readonly targetFamily: string;
|
|
69
|
+
readonly target: string;
|
|
70
|
+
readonly models: TModels;
|
|
71
|
+
readonly relations: TRelations;
|
|
72
|
+
readonly storage: TStorage;
|
|
73
|
+
readonly extensions: Record<string, unknown>;
|
|
74
|
+
readonly capabilities: Record<string, Record<string, boolean>>;
|
|
75
|
+
readonly meta: Record<string, unknown>;
|
|
76
|
+
readonly sources: Record<string, unknown>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Specifies how to import TypeScript types from a package.
|
|
81
|
+
* Used in extension pack manifests to declare codec and operation type imports.
|
|
82
|
+
*/
|
|
83
|
+
interface TypesImportSpec {
|
|
84
|
+
readonly package: string;
|
|
85
|
+
readonly named: string;
|
|
86
|
+
readonly alias: string;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Validation context passed to TargetFamilyHook.validateTypes().
|
|
90
|
+
* Contains pre-assembled operation registry, type imports, and extension IDs.
|
|
91
|
+
*/
|
|
92
|
+
interface ValidationContext {
|
|
93
|
+
readonly operationRegistry?: OperationRegistry;
|
|
94
|
+
readonly codecTypeImports?: ReadonlyArray<TypesImportSpec>;
|
|
95
|
+
readonly operationTypeImports?: ReadonlyArray<TypesImportSpec>;
|
|
96
|
+
readonly extensionIds?: ReadonlyArray<string>;
|
|
97
|
+
}
|
|
98
|
+
|
|
12
99
|
/**
|
|
13
100
|
* Type metadata for SQL storage types.
|
|
14
101
|
* Maps contract storage type IDs to native database types.
|
|
@@ -29,8 +116,8 @@ type SqlTypeMetadataRegistry = Map<string, SqlTypeMetadata>;
|
|
|
29
116
|
*/
|
|
30
117
|
interface SqlFamilyInstanceState {
|
|
31
118
|
readonly operationRegistry: OperationRegistry;
|
|
32
|
-
readonly codecTypeImports: ReadonlyArray<TypesImportSpec>;
|
|
33
|
-
readonly operationTypeImports: ReadonlyArray<TypesImportSpec>;
|
|
119
|
+
readonly codecTypeImports: ReadonlyArray<TypesImportSpec$1>;
|
|
120
|
+
readonly operationTypeImports: ReadonlyArray<TypesImportSpec$1>;
|
|
34
121
|
readonly extensionIds: ReadonlyArray<string>;
|
|
35
122
|
readonly typeMetadataRegistry: SqlTypeMetadataRegistry;
|
|
36
123
|
}
|
|
@@ -108,7 +195,7 @@ interface SqlControlFamilyInstance extends ControlFamilyInstance<'sql'>, SqlFami
|
|
|
108
195
|
* Handles stripping mappings and validation internally.
|
|
109
196
|
*/
|
|
110
197
|
emitContract(options: {
|
|
111
|
-
readonly contractIR: ContractIR | unknown;
|
|
198
|
+
readonly contractIR: ContractIR$1 | unknown;
|
|
112
199
|
}): Promise<EmitContractResult>;
|
|
113
200
|
}
|
|
114
201
|
|
|
@@ -123,13 +210,13 @@ declare class SqlFamilyDescriptor implements ControlFamilyDescriptor<'sql', SqlC
|
|
|
123
210
|
readonly manifest: ExtensionPackManifest;
|
|
124
211
|
readonly hook: {
|
|
125
212
|
readonly id: "sql";
|
|
126
|
-
readonly validateTypes: (ir:
|
|
127
|
-
readonly validateStructure: (ir:
|
|
128
|
-
readonly generateContractTypes: (ir:
|
|
129
|
-
readonly generateStorageType: (storage:
|
|
130
|
-
readonly generateModelsType: (models: Record<string,
|
|
213
|
+
readonly validateTypes: (ir: ContractIR, ctx: ValidationContext) => void;
|
|
214
|
+
readonly validateStructure: (ir: ContractIR) => void;
|
|
215
|
+
readonly generateContractTypes: (ir: ContractIR, codecTypeImports: ReadonlyArray<TypesImportSpec>, operationTypeImports: ReadonlyArray<TypesImportSpec>) => string;
|
|
216
|
+
readonly generateStorageType: (storage: SqlStorage) => string;
|
|
217
|
+
readonly generateModelsType: (models: Record<string, ModelDefinition> | undefined, storage: SqlStorage) => string;
|
|
131
218
|
readonly generateRelationsType: (relations: Record<string, unknown> | undefined) => string;
|
|
132
|
-
readonly generateMappingsType: (models: Record<string,
|
|
219
|
+
readonly generateMappingsType: (models: Record<string, ModelDefinition> | undefined, storage: SqlStorage, codecTypes: string, operationTypes: string) => string;
|
|
133
220
|
};
|
|
134
221
|
create<TTargetId extends string>(options: {
|
|
135
222
|
readonly target: ControlTargetDescriptor<'sql', TTargetId>;
|
package/dist/exports/control.js
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
collectSupportedCodecTypeIds,
|
|
3
|
+
readMarker
|
|
4
|
+
} from "./chunk-3HYKCN35.js";
|
|
5
|
+
|
|
1
6
|
// src/core/descriptor.ts
|
|
2
7
|
import { sqlTargetFamilyHook as sqlTargetFamilyHook2 } from "@prisma-next/sql-contract-emitter";
|
|
3
8
|
|
|
@@ -11,29 +16,8 @@ import {
|
|
|
11
16
|
writeContractMarker
|
|
12
17
|
} from "@prisma-next/sql-runtime";
|
|
13
18
|
|
|
14
|
-
// ../../framework/core-operations/src/index.ts
|
|
15
|
-
var OperationRegistryImpl = class {
|
|
16
|
-
operations = /* @__PURE__ */ new Map();
|
|
17
|
-
register(op) {
|
|
18
|
-
const existing = this.operations.get(op.forTypeId) ?? [];
|
|
19
|
-
const duplicate = existing.find((existingOp) => existingOp.method === op.method);
|
|
20
|
-
if (duplicate) {
|
|
21
|
-
throw new Error(
|
|
22
|
-
`Operation method "${op.method}" already registered for typeId "${op.forTypeId}"`
|
|
23
|
-
);
|
|
24
|
-
}
|
|
25
|
-
existing.push(op);
|
|
26
|
-
this.operations.set(op.forTypeId, existing);
|
|
27
|
-
}
|
|
28
|
-
byType(typeId) {
|
|
29
|
-
return this.operations.get(typeId) ?? [];
|
|
30
|
-
}
|
|
31
|
-
};
|
|
32
|
-
function createOperationRegistry() {
|
|
33
|
-
return new OperationRegistryImpl();
|
|
34
|
-
}
|
|
35
|
-
|
|
36
19
|
// src/core/assembly.ts
|
|
20
|
+
import { createOperationRegistry } from "@prisma-next/operations";
|
|
37
21
|
function assembleOperationRegistry(descriptors, convertOperationManifest2) {
|
|
38
22
|
const registry = createOperationRegistry();
|
|
39
23
|
for (const descriptor of descriptors) {
|
|
@@ -85,88 +69,6 @@ function extractExtensionIds(adapter, target, extensions) {
|
|
|
85
69
|
return ids;
|
|
86
70
|
}
|
|
87
71
|
|
|
88
|
-
// src/core/verify.ts
|
|
89
|
-
import { type } from "arktype";
|
|
90
|
-
var MetaSchema = type({ "[string]": "unknown" });
|
|
91
|
-
function parseMeta(meta) {
|
|
92
|
-
if (meta === null || meta === void 0) {
|
|
93
|
-
return {};
|
|
94
|
-
}
|
|
95
|
-
let parsed;
|
|
96
|
-
if (typeof meta === "string") {
|
|
97
|
-
try {
|
|
98
|
-
parsed = JSON.parse(meta);
|
|
99
|
-
} catch {
|
|
100
|
-
return {};
|
|
101
|
-
}
|
|
102
|
-
} else {
|
|
103
|
-
parsed = meta;
|
|
104
|
-
}
|
|
105
|
-
const result = MetaSchema(parsed);
|
|
106
|
-
if (result instanceof type.errors) {
|
|
107
|
-
return {};
|
|
108
|
-
}
|
|
109
|
-
return result;
|
|
110
|
-
}
|
|
111
|
-
var ContractMarkerRowSchema = type({
|
|
112
|
-
core_hash: "string",
|
|
113
|
-
profile_hash: "string",
|
|
114
|
-
"contract_json?": "unknown | null",
|
|
115
|
-
"canonical_version?": "number | null",
|
|
116
|
-
"updated_at?": "Date | string",
|
|
117
|
-
"app_tag?": "string | null",
|
|
118
|
-
"meta?": "unknown | null"
|
|
119
|
-
});
|
|
120
|
-
function parseContractMarkerRow(row) {
|
|
121
|
-
const result = ContractMarkerRowSchema(row);
|
|
122
|
-
if (result instanceof type.errors) {
|
|
123
|
-
const messages = result.map((p) => p.message).join("; ");
|
|
124
|
-
throw new Error(`Invalid contract marker row: ${messages}`);
|
|
125
|
-
}
|
|
126
|
-
const validatedRow = result;
|
|
127
|
-
const updatedAt = validatedRow.updated_at ? validatedRow.updated_at instanceof Date ? validatedRow.updated_at : new Date(validatedRow.updated_at) : /* @__PURE__ */ new Date();
|
|
128
|
-
return {
|
|
129
|
-
coreHash: validatedRow.core_hash,
|
|
130
|
-
profileHash: validatedRow.profile_hash,
|
|
131
|
-
contractJson: validatedRow.contract_json ?? null,
|
|
132
|
-
canonicalVersion: validatedRow.canonical_version ?? null,
|
|
133
|
-
updatedAt,
|
|
134
|
-
appTag: validatedRow.app_tag ?? null,
|
|
135
|
-
meta: parseMeta(validatedRow.meta)
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
function readMarkerSql() {
|
|
139
|
-
return {
|
|
140
|
-
sql: `select
|
|
141
|
-
core_hash,
|
|
142
|
-
profile_hash,
|
|
143
|
-
contract_json,
|
|
144
|
-
canonical_version,
|
|
145
|
-
updated_at,
|
|
146
|
-
app_tag,
|
|
147
|
-
meta
|
|
148
|
-
from prisma_contract.marker
|
|
149
|
-
where id = $1`,
|
|
150
|
-
params: [1]
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
async function readMarker(driver) {
|
|
154
|
-
const markerStatement = readMarkerSql();
|
|
155
|
-
const queryResult = await driver.query(markerStatement.sql, markerStatement.params);
|
|
156
|
-
if (queryResult.rows.length === 0) {
|
|
157
|
-
return null;
|
|
158
|
-
}
|
|
159
|
-
const markerRow = queryResult.rows[0];
|
|
160
|
-
if (!markerRow) {
|
|
161
|
-
throw new Error("Database query returned unexpected result structure");
|
|
162
|
-
}
|
|
163
|
-
return parseContractMarkerRow(markerRow);
|
|
164
|
-
}
|
|
165
|
-
function collectSupportedCodecTypeIds(descriptors) {
|
|
166
|
-
void descriptors;
|
|
167
|
-
return [];
|
|
168
|
-
}
|
|
169
|
-
|
|
170
72
|
// src/core/instance.ts
|
|
171
73
|
function convertOperationManifest(manifest) {
|
|
172
74
|
return {
|
|
@@ -216,8 +118,8 @@ function extractCodecTypeIdsFromContract(contract) {
|
|
|
216
118
|
if (typeof table === "object" && table !== null && "columns" in table && typeof table.columns === "object" && table.columns !== null) {
|
|
217
119
|
const columns = table.columns;
|
|
218
120
|
for (const column of Object.values(columns)) {
|
|
219
|
-
if (column && typeof column === "object" && "
|
|
220
|
-
typeIds.add(column.
|
|
121
|
+
if (column && typeof column === "object" && "codecId" in column && typeof column.codecId === "string") {
|
|
122
|
+
typeIds.add(column.codecId);
|
|
221
123
|
}
|
|
222
124
|
}
|
|
223
125
|
}
|
|
@@ -895,39 +797,47 @@ function createSqlFamilyInstance(options) {
|
|
|
895
797
|
}
|
|
896
798
|
const columnChildren = [];
|
|
897
799
|
let columnStatus = "pass";
|
|
898
|
-
const
|
|
899
|
-
const contractNativeType = typeMetadata?.nativeType;
|
|
800
|
+
const contractNativeType = contractColumn.nativeType;
|
|
900
801
|
const schemaNativeType = schemaColumn.nativeType;
|
|
901
802
|
if (!contractNativeType) {
|
|
803
|
+
issues.push({
|
|
804
|
+
kind: "type_mismatch",
|
|
805
|
+
table: tableName,
|
|
806
|
+
column: columnName,
|
|
807
|
+
expected: "nativeType required",
|
|
808
|
+
actual: schemaNativeType || "unknown",
|
|
809
|
+
message: `Column "${tableName}"."${columnName}" is missing nativeType in contract`
|
|
810
|
+
});
|
|
902
811
|
columnChildren.push({
|
|
903
|
-
status: "
|
|
812
|
+
status: "fail",
|
|
904
813
|
kind: "type",
|
|
905
814
|
name: "type",
|
|
906
|
-
contractPath: `${columnPath}.
|
|
907
|
-
code: "
|
|
908
|
-
message:
|
|
909
|
-
expected:
|
|
910
|
-
actual:
|
|
815
|
+
contractPath: `${columnPath}.nativeType`,
|
|
816
|
+
code: "type_mismatch",
|
|
817
|
+
message: "Contract column is missing nativeType",
|
|
818
|
+
expected: "nativeType required",
|
|
819
|
+
actual: schemaNativeType || "unknown",
|
|
911
820
|
children: []
|
|
912
821
|
});
|
|
822
|
+
columnStatus = "fail";
|
|
913
823
|
} else if (!schemaNativeType) {
|
|
914
824
|
issues.push({
|
|
915
825
|
kind: "type_mismatch",
|
|
916
826
|
table: tableName,
|
|
917
827
|
column: columnName,
|
|
918
828
|
expected: contractNativeType,
|
|
919
|
-
actual:
|
|
829
|
+
actual: "unknown",
|
|
920
830
|
message: `Column "${tableName}"."${columnName}" has type mismatch: schema column has no nativeType`
|
|
921
831
|
});
|
|
922
832
|
columnChildren.push({
|
|
923
833
|
status: "fail",
|
|
924
834
|
kind: "type",
|
|
925
835
|
name: "type",
|
|
926
|
-
contractPath: `${columnPath}.
|
|
836
|
+
contractPath: `${columnPath}.nativeType`,
|
|
927
837
|
code: "type_mismatch",
|
|
928
838
|
message: "Schema column has no nativeType",
|
|
929
839
|
expected: contractNativeType,
|
|
930
|
-
actual:
|
|
840
|
+
actual: "unknown",
|
|
931
841
|
children: []
|
|
932
842
|
});
|
|
933
843
|
columnStatus = "fail";
|
|
@@ -944,7 +854,7 @@ function createSqlFamilyInstance(options) {
|
|
|
944
854
|
status: "fail",
|
|
945
855
|
kind: "type",
|
|
946
856
|
name: "type",
|
|
947
|
-
contractPath: `${columnPath}.
|
|
857
|
+
contractPath: `${columnPath}.nativeType`,
|
|
948
858
|
code: "type_mismatch",
|
|
949
859
|
message: `Type mismatch: expected ${contractNativeType}, got ${schemaNativeType}`,
|
|
950
860
|
expected: contractNativeType,
|
|
@@ -953,6 +863,34 @@ function createSqlFamilyInstance(options) {
|
|
|
953
863
|
});
|
|
954
864
|
columnStatus = "fail";
|
|
955
865
|
}
|
|
866
|
+
if (contractColumn.codecId) {
|
|
867
|
+
const typeMetadata = typeMetadataRegistry.get(contractColumn.codecId);
|
|
868
|
+
if (!typeMetadata) {
|
|
869
|
+
columnChildren.push({
|
|
870
|
+
status: "warn",
|
|
871
|
+
kind: "type",
|
|
872
|
+
name: "type_metadata_missing",
|
|
873
|
+
contractPath: `${columnPath}.codecId`,
|
|
874
|
+
code: "type_metadata_missing",
|
|
875
|
+
message: `codecId "${contractColumn.codecId}" not found in type metadata registry`,
|
|
876
|
+
expected: contractColumn.codecId,
|
|
877
|
+
actual: void 0,
|
|
878
|
+
children: []
|
|
879
|
+
});
|
|
880
|
+
} else if (typeMetadata.nativeType && typeMetadata.nativeType !== contractNativeType) {
|
|
881
|
+
columnChildren.push({
|
|
882
|
+
status: "warn",
|
|
883
|
+
kind: "type",
|
|
884
|
+
name: "type_consistency",
|
|
885
|
+
contractPath: `${columnPath}.codecId`,
|
|
886
|
+
code: "type_consistency_warning",
|
|
887
|
+
message: `codecId "${contractColumn.codecId}" maps to nativeType "${typeMetadata.nativeType}" in registry, but contract has "${contractNativeType}"`,
|
|
888
|
+
expected: typeMetadata.nativeType,
|
|
889
|
+
actual: contractNativeType,
|
|
890
|
+
children: []
|
|
891
|
+
});
|
|
892
|
+
}
|
|
893
|
+
}
|
|
956
894
|
if (contractColumn.nullable !== schemaColumn.nullable) {
|
|
957
895
|
issues.push({
|
|
958
896
|
kind: "nullability_mismatch",
|
|
@@ -978,7 +916,7 @@ function createSqlFamilyInstance(options) {
|
|
|
978
916
|
const computedColumnStatus = columnChildren.some((c) => c.status === "fail") ? "fail" : columnChildren.some((c) => c.status === "warn") ? "warn" : "pass";
|
|
979
917
|
const finalColumnStatus = columnChildren.length > 0 ? computedColumnStatus : columnStatus;
|
|
980
918
|
const nullableText = contractColumn.nullable ? "nullable" : "not nullable";
|
|
981
|
-
const columnTypeDisplay =
|
|
919
|
+
const columnTypeDisplay = contractColumn.codecId ? `${contractNativeType} (${contractColumn.codecId})` : contractNativeType;
|
|
982
920
|
const failureMessages = columnChildren.filter((child) => child.status === "fail" && child.message).map((child) => child.message).filter((msg) => typeof msg === "string" && msg.length > 0);
|
|
983
921
|
const columnMessage = finalColumnStatus === "fail" && failureMessages.length > 0 ? failureMessages.join("; ") : "";
|
|
984
922
|
const columnCode = finalColumnStatus === "fail" && columnChildren.length > 0 && columnChildren[0] ? columnChildren[0].code : finalColumnStatus === "warn" && columnChildren.length > 0 && columnChildren[0] ? columnChildren[0].code : "";
|
|
@@ -1025,7 +963,7 @@ function createSqlFamilyInstance(options) {
|
|
|
1025
963
|
code: "extra_column",
|
|
1026
964
|
message: `Extra column "${columnName}" found`,
|
|
1027
965
|
expected: void 0,
|
|
1028
|
-
actual: schemaColumn.
|
|
966
|
+
actual: schemaColumn.nativeType,
|
|
1029
967
|
children: []
|
|
1030
968
|
});
|
|
1031
969
|
}
|
|
@@ -1287,16 +1225,15 @@ function createSqlFamilyInstance(options) {
|
|
|
1287
1225
|
const columnNodes = [];
|
|
1288
1226
|
for (const [columnName, column] of Object.entries(table.columns)) {
|
|
1289
1227
|
const nullableText = column.nullable ? "(nullable)" : "(not nullable)";
|
|
1290
|
-
const typeDisplay = column.nativeType
|
|
1228
|
+
const typeDisplay = column.nativeType;
|
|
1291
1229
|
const label = `${columnName}: ${typeDisplay} ${nullableText}`;
|
|
1292
1230
|
columnNodes.push({
|
|
1293
1231
|
kind: "field",
|
|
1294
1232
|
id: `column-${tableName}-${columnName}`,
|
|
1295
1233
|
label,
|
|
1296
1234
|
meta: {
|
|
1297
|
-
|
|
1298
|
-
nullable: column.nullable
|
|
1299
|
-
...column.nativeType ? { nativeType: column.nativeType } : {}
|
|
1235
|
+
nativeType: column.nativeType,
|
|
1236
|
+
nullable: column.nullable
|
|
1300
1237
|
}
|
|
1301
1238
|
});
|
|
1302
1239
|
}
|