arkormx 1.3.0 → 1.3.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/dist/cli.mjs CHANGED
@@ -1870,10 +1870,225 @@ var CliApp = class {
1870
1870
  if (value === "String") return "string";
1871
1871
  if (value === "Boolean") return "boolean";
1872
1872
  if (value === "DateTime") return "Date";
1873
- if (value === "Json") return "Record<string, unknown>";
1873
+ if (value === "Json") return "Record<string, unknown> | unknown[]";
1874
1874
  if (value === "Bytes") return "Buffer";
1875
1875
  return "unknown";
1876
1876
  }
1877
+ splitTopLevel(value, delimiter) {
1878
+ const parts = [];
1879
+ let start = 0;
1880
+ let angleDepth = 0;
1881
+ let parenthesisDepth = 0;
1882
+ let quote = null;
1883
+ for (let index = 0; index < value.length; index += 1) {
1884
+ const character = value[index];
1885
+ const previous = index > 0 ? value[index - 1] : "";
1886
+ if (quote) {
1887
+ if (character === quote && previous !== "\\") quote = null;
1888
+ continue;
1889
+ }
1890
+ if (character === "'" || character === "\"") {
1891
+ quote = character;
1892
+ continue;
1893
+ }
1894
+ if (character === "<") {
1895
+ angleDepth += 1;
1896
+ continue;
1897
+ }
1898
+ if (character === ">") {
1899
+ angleDepth = Math.max(0, angleDepth - 1);
1900
+ continue;
1901
+ }
1902
+ if (character === "(") {
1903
+ parenthesisDepth += 1;
1904
+ continue;
1905
+ }
1906
+ if (character === ")") {
1907
+ parenthesisDepth = Math.max(0, parenthesisDepth - 1);
1908
+ continue;
1909
+ }
1910
+ if (character === delimiter && angleDepth === 0 && parenthesisDepth === 0) {
1911
+ parts.push(value.slice(start, index).trim());
1912
+ start = index + 1;
1913
+ }
1914
+ }
1915
+ parts.push(value.slice(start).trim());
1916
+ return parts.filter(Boolean);
1917
+ }
1918
+ hasWrappedParentheses(value) {
1919
+ if (!value.startsWith("(") || !value.endsWith(")")) return false;
1920
+ let depth = 0;
1921
+ let quote = null;
1922
+ for (let index = 0; index < value.length; index += 1) {
1923
+ const character = value[index];
1924
+ const previous = index > 0 ? value[index - 1] : "";
1925
+ if (quote) {
1926
+ if (character === quote && previous !== "\\") quote = null;
1927
+ continue;
1928
+ }
1929
+ if (character === "'" || character === "\"") {
1930
+ quote = character;
1931
+ continue;
1932
+ }
1933
+ if (character === "(") depth += 1;
1934
+ if (character === ")") {
1935
+ depth -= 1;
1936
+ if (depth === 0 && index < value.length - 1) return false;
1937
+ }
1938
+ }
1939
+ return depth === 0;
1940
+ }
1941
+ stripWrappedParentheses(value) {
1942
+ let nextValue = value.trim();
1943
+ while (this.hasWrappedParentheses(nextValue)) nextValue = nextValue.slice(1, -1).trim();
1944
+ return nextValue;
1945
+ }
1946
+ parseDeclarationType(value) {
1947
+ const trimmed = this.stripWrappedParentheses(value.trim());
1948
+ if (!trimmed) return null;
1949
+ const unionParts = this.splitTopLevel(trimmed, "|");
1950
+ if (unionParts.length > 1) {
1951
+ const types = unionParts.map((part) => this.parseDeclarationType(part)).filter((part) => part !== null);
1952
+ if (types.length !== unionParts.length) return null;
1953
+ return {
1954
+ kind: "union",
1955
+ types: types.flatMap((type) => type.kind === "union" ? type.types : [type])
1956
+ };
1957
+ }
1958
+ if (trimmed.endsWith("[]")) {
1959
+ const element = this.parseDeclarationType(trimmed.slice(0, -2));
1960
+ if (!element) return null;
1961
+ return {
1962
+ kind: "array",
1963
+ element
1964
+ };
1965
+ }
1966
+ const arrayMatch = trimmed.match(/^(Array|ReadonlyArray)<([\s\S]+)>$/);
1967
+ if (arrayMatch) {
1968
+ const element = this.parseDeclarationType(arrayMatch[2]);
1969
+ if (!element) return null;
1970
+ return {
1971
+ kind: "array",
1972
+ element
1973
+ };
1974
+ }
1975
+ if (trimmed === "null") return { kind: "null" };
1976
+ if (trimmed.startsWith("'") && trimmed.endsWith("'") || trimmed.startsWith("\"") && trimmed.endsWith("\"")) return {
1977
+ kind: "string-literal",
1978
+ value: trimmed.slice(1, -1)
1979
+ };
1980
+ if (trimmed === "Record<string, unknown>") return {
1981
+ kind: "named",
1982
+ name: trimmed
1983
+ };
1984
+ if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(trimmed)) return {
1985
+ kind: "named",
1986
+ name: trimmed
1987
+ };
1988
+ return null;
1989
+ }
1990
+ expandUnion(node) {
1991
+ return node.kind === "union" ? node.types : [node];
1992
+ }
1993
+ isEnumTypeName(value, enums) {
1994
+ return enums.has(value);
1995
+ }
1996
+ isDeclarationNodeAssignable(actual, expected, enums) {
1997
+ if (expected.kind === "named") {
1998
+ if (expected.name === "unknown") return true;
1999
+ if (actual.kind === "named") {
2000
+ if (actual.name === expected.name) return true;
2001
+ if (expected.name === "string" && this.isEnumTypeName(actual.name, enums)) return true;
2002
+ }
2003
+ if (actual.kind === "string-literal") {
2004
+ if (expected.name === "string") return true;
2005
+ if (enums.get(expected.name)?.includes(actual.value)) return true;
2006
+ }
2007
+ return false;
2008
+ }
2009
+ if (expected.kind === "array") return actual.kind === "array" && this.isDeclarationAssignable(actual.element, expected.element, enums);
2010
+ if (expected.kind === "null") return actual.kind === "null";
2011
+ if (expected.kind === "string-literal") return actual.kind === "string-literal" && actual.value === expected.value;
2012
+ return false;
2013
+ }
2014
+ isDeclarationAssignable(actual, expected, enums) {
2015
+ const actualTypes = this.expandUnion(actual);
2016
+ const expectedTypes = this.expandUnion(expected);
2017
+ return actualTypes.every((actualType) => {
2018
+ return expectedTypes.some((expectedType) => {
2019
+ return this.isDeclarationNodeAssignable(actualType, expectedType, enums);
2020
+ });
2021
+ });
2022
+ }
2023
+ isCompatibleDeclarationType(actualType, expectedType, enums) {
2024
+ const actual = this.parseDeclarationType(actualType);
2025
+ const expected = this.parseDeclarationType(expectedType);
2026
+ if (!actual || !expected) return actualType.replace(/\s+/g, " ").trim() === expectedType.replace(/\s+/g, " ").trim();
2027
+ return this.isDeclarationAssignable(actual, expected, enums);
2028
+ }
2029
+ collectEnumReferencesFromNode(node, enums, collected) {
2030
+ if (node.kind === "named" && enums.has(node.name)) {
2031
+ collected.add(node.name);
2032
+ return;
2033
+ }
2034
+ if (node.kind === "array") {
2035
+ this.collectEnumReferencesFromNode(node.element, enums, collected);
2036
+ return;
2037
+ }
2038
+ if (node.kind === "union") node.types.forEach((type) => this.collectEnumReferencesFromNode(type, enums, collected));
2039
+ }
2040
+ collectEnumReferences(type, enums) {
2041
+ const parsed = this.parseDeclarationType(type);
2042
+ if (!parsed) return [];
2043
+ const collected = /* @__PURE__ */ new Set();
2044
+ this.collectEnumReferencesFromNode(parsed, enums, collected);
2045
+ return [...collected].sort((left, right) => left.localeCompare(right));
2046
+ }
2047
+ syncPrismaEnumImports(modelSource, enumTypes) {
2048
+ if (enumTypes.length === 0) return modelSource;
2049
+ const importRegex = /^import\s+type\s+\{([^}]+)\}\s+from\s+['"]@prisma\/client['"]\s*;?$/m;
2050
+ const existingImport = modelSource.match(importRegex);
2051
+ if (existingImport) {
2052
+ const existingTypes = existingImport[1].split(",").map((value) => value.trim()).filter(Boolean);
2053
+ const mergedTypes = [...new Set([...existingTypes, ...enumTypes])].sort((left, right) => left.localeCompare(right));
2054
+ return modelSource.replace(importRegex, `import type { ${mergedTypes.join(", ")} } from '@prisma/client'`);
2055
+ }
2056
+ const lines = modelSource.split("\n");
2057
+ let insertionIndex = 0;
2058
+ while (insertionIndex < lines.length && lines[insertionIndex].trim().startsWith("import ")) insertionIndex += 1;
2059
+ lines.splice(insertionIndex, 0, `import type { ${enumTypes.join(", ")} } from '@prisma/client'`);
2060
+ return lines.join("\n");
2061
+ }
2062
+ /**
2063
+ * Parse Prisma enum definitions from a schema and return their member names.
2064
+ *
2065
+ * @param schema The Prisma schema source.
2066
+ * @returns A map of enum names to their declared member names.
2067
+ */
2068
+ parsePrismaEnums(schema) {
2069
+ const enums = /* @__PURE__ */ new Map();
2070
+ for (const match of schema.matchAll(PRISMA_ENUM_REGEX)) {
2071
+ const enumName = match[1];
2072
+ const values = match[0].split("\n").slice(1, -1).map((line) => line.trim()).filter((line) => Boolean(line) && !line.startsWith("//")).map((line) => {
2073
+ return line.match(/^([A-Za-z][A-Za-z0-9_]*)\b/)?.[1];
2074
+ }).filter((value) => Boolean(value));
2075
+ enums.set(enumName, values);
2076
+ }
2077
+ return enums;
2078
+ }
2079
+ /**
2080
+ * Resolve the generated TypeScript declaration type for a Prisma field.
2081
+ *
2082
+ * @param fieldType The Prisma field type token.
2083
+ * @param isList Whether the field is declared as a Prisma list.
2084
+ * @param enums Known Prisma enum definitions.
2085
+ * @returns The declaration type to emit, or null when unsupported.
2086
+ */
2087
+ prismaFieldTypeToTs(fieldType, isList, enums) {
2088
+ const baseType = enums.has(fieldType) ? fieldType : this.prismaTypeToTs(fieldType);
2089
+ if (baseType === "unknown" && !enums.has(fieldType)) return null;
2090
+ return isList ? `Array<${baseType}>` : baseType;
2091
+ }
1877
2092
  /**
1878
2093
  * Parse the Prisma schema to extract model definitions and their fields, focusing
1879
2094
  * on scalar types.
@@ -1883,6 +2098,7 @@ var CliApp = class {
1883
2098
  */
1884
2099
  parsePrismaModels(schema) {
1885
2100
  const models = [];
2101
+ const enumDefinitions = this.parsePrismaEnums(schema);
1886
2102
  const modelRegex = /model\s+(\w+)\s*\{([\s\S]*?)\n\}/g;
1887
2103
  const scalarTypes = new Set([
1888
2104
  "Int",
@@ -1903,14 +2119,16 @@ var CliApp = class {
1903
2119
  body.split("\n").forEach((rawLine) => {
1904
2120
  const line = rawLine.trim();
1905
2121
  if (!line || line.startsWith("@@") || line.startsWith("//")) return;
1906
- const fieldMatch = line.match(/^(\w+)\s+([A-Za-z]+)(\?)?(?:\s|$)/);
2122
+ const fieldMatch = line.match(/^(\w+)\s+([A-Za-z][A-Za-z0-9_]*)(\[\])?(\?)?(?:\s|$)/);
1907
2123
  if (!fieldMatch) return;
1908
2124
  const fieldType = fieldMatch[2];
1909
- if (!scalarTypes.has(fieldType)) return;
2125
+ if (!scalarTypes.has(fieldType) && !enumDefinitions.has(fieldType)) return;
2126
+ const declarationType = this.prismaFieldTypeToTs(fieldType, Boolean(fieldMatch[3]), enumDefinitions);
2127
+ if (!declarationType) return;
1910
2128
  fields.push({
1911
2129
  name: fieldMatch[1],
1912
- type: this.prismaTypeToTs(fieldType),
1913
- nullable: Boolean(fieldMatch[3])
2130
+ type: declarationType,
2131
+ nullable: Boolean(fieldMatch[4])
1914
2132
  });
1915
2133
  });
1916
2134
  models.push({
@@ -1931,7 +2149,7 @@ var CliApp = class {
1931
2149
  * @param declarations A list of attribute declarations to sync.
1932
2150
  * @returns An object containing the updated content and a flag indicating if it was updated.
1933
2151
  */
1934
- syncModelDeclarations(modelSource, declarations) {
2152
+ syncModelDeclarations(modelSource, declarations, enums) {
1935
2153
  const lines = modelSource.split("\n");
1936
2154
  const classIndex = lines.findIndex((line) => /export\s+class\s+\w+\s+extends\s+Model<.+>\s*\{/.test(line));
1937
2155
  if (classIndex < 0) return {
@@ -1953,16 +2171,38 @@ var CliApp = class {
1953
2171
  content: modelSource,
1954
2172
  updated: false
1955
2173
  };
1956
- const withoutDeclares = lines.slice(classIndex + 1, classEndIndex).filter((line) => !/^\s*declare\s+\w+\??:\s*[^\n]+$/.test(line));
1957
- const rebuiltClass = [...declarations.map((declaration) => ` ${declaration}`), ...withoutDeclares];
2174
+ const withinClass = lines.slice(classIndex + 1, classEndIndex);
2175
+ const existingDeclarations = /* @__PURE__ */ new Map();
2176
+ withinClass.forEach((line) => {
2177
+ const declarationMatch = line.match(/^\s*declare\s+(\w+)\??:\s*([^;\n]+);?\s*$/);
2178
+ if (!declarationMatch) return;
2179
+ existingDeclarations.set(declarationMatch[1], {
2180
+ name: declarationMatch[1],
2181
+ raw: line.trim(),
2182
+ type: declarationMatch[2].trim()
2183
+ });
2184
+ });
2185
+ const withoutDeclares = withinClass.filter((line) => !/^\s*declare\s+\w+\??:\s*[^\n]+$/.test(line));
2186
+ const chosenDeclarations = declarations.map((declaration) => {
2187
+ const expectedType = `${declaration.type}${declaration.nullable ? " | null" : ""}`;
2188
+ const existingDeclaration = existingDeclarations.get(declaration.name);
2189
+ if (existingDeclaration && this.isCompatibleDeclarationType(existingDeclaration.type, expectedType, enums)) return existingDeclaration.raw;
2190
+ return `declare ${declaration.name}: ${expectedType}`;
2191
+ });
2192
+ const rebuiltClass = [...chosenDeclarations.map((declaration) => ` ${declaration}`), ...withoutDeclares];
1958
2193
  const content = [
1959
2194
  ...lines.slice(0, classIndex + 1),
1960
2195
  ...rebuiltClass,
1961
2196
  ...lines.slice(classEndIndex)
1962
2197
  ].join("\n");
2198
+ const enumImports = [...new Set(chosenDeclarations.flatMap((declaration) => {
2199
+ const type = declaration.replace(/^declare\s+\w+\??:\s*/, "").replace(/;$/, "").trim();
2200
+ return this.collectEnumReferences(type, enums);
2201
+ }))].sort((left, right) => left.localeCompare(right));
2202
+ const contentWithImports = this.syncPrismaEnumImports(content, enumImports);
1963
2203
  return {
1964
- content,
1965
- updated: content !== modelSource
2204
+ content: contentWithImports,
2205
+ updated: contentWithImports !== modelSource
1966
2206
  };
1967
2207
  }
1968
2208
  /**
@@ -1982,6 +2222,7 @@ var CliApp = class {
1982
2222
  if (!existsSync$1(schemaPath)) throw new Error(`Prisma schema file not found: ${schemaPath}`);
1983
2223
  if (!existsSync$1(modelsDir)) throw new Error(`Models directory not found: ${modelsDir}`);
1984
2224
  const schema = readFileSync$1(schemaPath, "utf-8");
2225
+ const prismaEnums = this.parsePrismaEnums(schema);
1985
2226
  const prismaModels = this.parsePrismaModels(schema);
1986
2227
  const modelFiles = readdirSync$1(modelsDir).filter((file) => file.endsWith(".ts"));
1987
2228
  const updated = [];
@@ -2001,8 +2242,7 @@ var CliApp = class {
2001
2242
  skipped.push(filePath);
2002
2243
  return;
2003
2244
  }
2004
- const declarations = prismaModel.fields.map((field) => `declare ${field.name}: ${field.type}${field.nullable ? " | null" : ""}`);
2005
- const synced = this.syncModelDeclarations(source, declarations);
2245
+ const synced = this.syncModelDeclarations(source, prismaModel.fields, prismaEnums);
2006
2246
  if (!synced.updated) {
2007
2247
  skipped.push(filePath);
2008
2248
  return;
package/dist/index.cjs CHANGED
@@ -2146,10 +2146,225 @@ var CliApp = class {
2146
2146
  if (value === "String") return "string";
2147
2147
  if (value === "Boolean") return "boolean";
2148
2148
  if (value === "DateTime") return "Date";
2149
- if (value === "Json") return "Record<string, unknown>";
2149
+ if (value === "Json") return "Record<string, unknown> | unknown[]";
2150
2150
  if (value === "Bytes") return "Buffer";
2151
2151
  return "unknown";
2152
2152
  }
2153
+ splitTopLevel(value, delimiter) {
2154
+ const parts = [];
2155
+ let start = 0;
2156
+ let angleDepth = 0;
2157
+ let parenthesisDepth = 0;
2158
+ let quote = null;
2159
+ for (let index = 0; index < value.length; index += 1) {
2160
+ const character = value[index];
2161
+ const previous = index > 0 ? value[index - 1] : "";
2162
+ if (quote) {
2163
+ if (character === quote && previous !== "\\") quote = null;
2164
+ continue;
2165
+ }
2166
+ if (character === "'" || character === "\"") {
2167
+ quote = character;
2168
+ continue;
2169
+ }
2170
+ if (character === "<") {
2171
+ angleDepth += 1;
2172
+ continue;
2173
+ }
2174
+ if (character === ">") {
2175
+ angleDepth = Math.max(0, angleDepth - 1);
2176
+ continue;
2177
+ }
2178
+ if (character === "(") {
2179
+ parenthesisDepth += 1;
2180
+ continue;
2181
+ }
2182
+ if (character === ")") {
2183
+ parenthesisDepth = Math.max(0, parenthesisDepth - 1);
2184
+ continue;
2185
+ }
2186
+ if (character === delimiter && angleDepth === 0 && parenthesisDepth === 0) {
2187
+ parts.push(value.slice(start, index).trim());
2188
+ start = index + 1;
2189
+ }
2190
+ }
2191
+ parts.push(value.slice(start).trim());
2192
+ return parts.filter(Boolean);
2193
+ }
2194
+ hasWrappedParentheses(value) {
2195
+ if (!value.startsWith("(") || !value.endsWith(")")) return false;
2196
+ let depth = 0;
2197
+ let quote = null;
2198
+ for (let index = 0; index < value.length; index += 1) {
2199
+ const character = value[index];
2200
+ const previous = index > 0 ? value[index - 1] : "";
2201
+ if (quote) {
2202
+ if (character === quote && previous !== "\\") quote = null;
2203
+ continue;
2204
+ }
2205
+ if (character === "'" || character === "\"") {
2206
+ quote = character;
2207
+ continue;
2208
+ }
2209
+ if (character === "(") depth += 1;
2210
+ if (character === ")") {
2211
+ depth -= 1;
2212
+ if (depth === 0 && index < value.length - 1) return false;
2213
+ }
2214
+ }
2215
+ return depth === 0;
2216
+ }
2217
+ stripWrappedParentheses(value) {
2218
+ let nextValue = value.trim();
2219
+ while (this.hasWrappedParentheses(nextValue)) nextValue = nextValue.slice(1, -1).trim();
2220
+ return nextValue;
2221
+ }
2222
+ parseDeclarationType(value) {
2223
+ const trimmed = this.stripWrappedParentheses(value.trim());
2224
+ if (!trimmed) return null;
2225
+ const unionParts = this.splitTopLevel(trimmed, "|");
2226
+ if (unionParts.length > 1) {
2227
+ const types = unionParts.map((part) => this.parseDeclarationType(part)).filter((part) => part !== null);
2228
+ if (types.length !== unionParts.length) return null;
2229
+ return {
2230
+ kind: "union",
2231
+ types: types.flatMap((type) => type.kind === "union" ? type.types : [type])
2232
+ };
2233
+ }
2234
+ if (trimmed.endsWith("[]")) {
2235
+ const element = this.parseDeclarationType(trimmed.slice(0, -2));
2236
+ if (!element) return null;
2237
+ return {
2238
+ kind: "array",
2239
+ element
2240
+ };
2241
+ }
2242
+ const arrayMatch = trimmed.match(/^(Array|ReadonlyArray)<([\s\S]+)>$/);
2243
+ if (arrayMatch) {
2244
+ const element = this.parseDeclarationType(arrayMatch[2]);
2245
+ if (!element) return null;
2246
+ return {
2247
+ kind: "array",
2248
+ element
2249
+ };
2250
+ }
2251
+ if (trimmed === "null") return { kind: "null" };
2252
+ if (trimmed.startsWith("'") && trimmed.endsWith("'") || trimmed.startsWith("\"") && trimmed.endsWith("\"")) return {
2253
+ kind: "string-literal",
2254
+ value: trimmed.slice(1, -1)
2255
+ };
2256
+ if (trimmed === "Record<string, unknown>") return {
2257
+ kind: "named",
2258
+ name: trimmed
2259
+ };
2260
+ if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(trimmed)) return {
2261
+ kind: "named",
2262
+ name: trimmed
2263
+ };
2264
+ return null;
2265
+ }
2266
+ expandUnion(node) {
2267
+ return node.kind === "union" ? node.types : [node];
2268
+ }
2269
+ isEnumTypeName(value, enums) {
2270
+ return enums.has(value);
2271
+ }
2272
+ isDeclarationNodeAssignable(actual, expected, enums) {
2273
+ if (expected.kind === "named") {
2274
+ if (expected.name === "unknown") return true;
2275
+ if (actual.kind === "named") {
2276
+ if (actual.name === expected.name) return true;
2277
+ if (expected.name === "string" && this.isEnumTypeName(actual.name, enums)) return true;
2278
+ }
2279
+ if (actual.kind === "string-literal") {
2280
+ if (expected.name === "string") return true;
2281
+ if (enums.get(expected.name)?.includes(actual.value)) return true;
2282
+ }
2283
+ return false;
2284
+ }
2285
+ if (expected.kind === "array") return actual.kind === "array" && this.isDeclarationAssignable(actual.element, expected.element, enums);
2286
+ if (expected.kind === "null") return actual.kind === "null";
2287
+ if (expected.kind === "string-literal") return actual.kind === "string-literal" && actual.value === expected.value;
2288
+ return false;
2289
+ }
2290
+ isDeclarationAssignable(actual, expected, enums) {
2291
+ const actualTypes = this.expandUnion(actual);
2292
+ const expectedTypes = this.expandUnion(expected);
2293
+ return actualTypes.every((actualType) => {
2294
+ return expectedTypes.some((expectedType) => {
2295
+ return this.isDeclarationNodeAssignable(actualType, expectedType, enums);
2296
+ });
2297
+ });
2298
+ }
2299
+ isCompatibleDeclarationType(actualType, expectedType, enums) {
2300
+ const actual = this.parseDeclarationType(actualType);
2301
+ const expected = this.parseDeclarationType(expectedType);
2302
+ if (!actual || !expected) return actualType.replace(/\s+/g, " ").trim() === expectedType.replace(/\s+/g, " ").trim();
2303
+ return this.isDeclarationAssignable(actual, expected, enums);
2304
+ }
2305
+ collectEnumReferencesFromNode(node, enums, collected) {
2306
+ if (node.kind === "named" && enums.has(node.name)) {
2307
+ collected.add(node.name);
2308
+ return;
2309
+ }
2310
+ if (node.kind === "array") {
2311
+ this.collectEnumReferencesFromNode(node.element, enums, collected);
2312
+ return;
2313
+ }
2314
+ if (node.kind === "union") node.types.forEach((type) => this.collectEnumReferencesFromNode(type, enums, collected));
2315
+ }
2316
+ collectEnumReferences(type, enums) {
2317
+ const parsed = this.parseDeclarationType(type);
2318
+ if (!parsed) return [];
2319
+ const collected = /* @__PURE__ */ new Set();
2320
+ this.collectEnumReferencesFromNode(parsed, enums, collected);
2321
+ return [...collected].sort((left, right) => left.localeCompare(right));
2322
+ }
2323
+ syncPrismaEnumImports(modelSource, enumTypes) {
2324
+ if (enumTypes.length === 0) return modelSource;
2325
+ const importRegex = /^import\s+type\s+\{([^}]+)\}\s+from\s+['"]@prisma\/client['"]\s*;?$/m;
2326
+ const existingImport = modelSource.match(importRegex);
2327
+ if (existingImport) {
2328
+ const existingTypes = existingImport[1].split(",").map((value) => value.trim()).filter(Boolean);
2329
+ const mergedTypes = [...new Set([...existingTypes, ...enumTypes])].sort((left, right) => left.localeCompare(right));
2330
+ return modelSource.replace(importRegex, `import type { ${mergedTypes.join(", ")} } from '@prisma/client'`);
2331
+ }
2332
+ const lines = modelSource.split("\n");
2333
+ let insertionIndex = 0;
2334
+ while (insertionIndex < lines.length && lines[insertionIndex].trim().startsWith("import ")) insertionIndex += 1;
2335
+ lines.splice(insertionIndex, 0, `import type { ${enumTypes.join(", ")} } from '@prisma/client'`);
2336
+ return lines.join("\n");
2337
+ }
2338
+ /**
2339
+ * Parse Prisma enum definitions from a schema and return their member names.
2340
+ *
2341
+ * @param schema The Prisma schema source.
2342
+ * @returns A map of enum names to their declared member names.
2343
+ */
2344
+ parsePrismaEnums(schema) {
2345
+ const enums = /* @__PURE__ */ new Map();
2346
+ for (const match of schema.matchAll(PRISMA_ENUM_REGEX)) {
2347
+ const enumName = match[1];
2348
+ const values = match[0].split("\n").slice(1, -1).map((line) => line.trim()).filter((line) => Boolean(line) && !line.startsWith("//")).map((line) => {
2349
+ return line.match(/^([A-Za-z][A-Za-z0-9_]*)\b/)?.[1];
2350
+ }).filter((value) => Boolean(value));
2351
+ enums.set(enumName, values);
2352
+ }
2353
+ return enums;
2354
+ }
2355
+ /**
2356
+ * Resolve the generated TypeScript declaration type for a Prisma field.
2357
+ *
2358
+ * @param fieldType The Prisma field type token.
2359
+ * @param isList Whether the field is declared as a Prisma list.
2360
+ * @param enums Known Prisma enum definitions.
2361
+ * @returns The declaration type to emit, or null when unsupported.
2362
+ */
2363
+ prismaFieldTypeToTs(fieldType, isList, enums) {
2364
+ const baseType = enums.has(fieldType) ? fieldType : this.prismaTypeToTs(fieldType);
2365
+ if (baseType === "unknown" && !enums.has(fieldType)) return null;
2366
+ return isList ? `Array<${baseType}>` : baseType;
2367
+ }
2153
2368
  /**
2154
2369
  * Parse the Prisma schema to extract model definitions and their fields, focusing
2155
2370
  * on scalar types.
@@ -2159,6 +2374,7 @@ var CliApp = class {
2159
2374
  */
2160
2375
  parsePrismaModels(schema) {
2161
2376
  const models = [];
2377
+ const enumDefinitions = this.parsePrismaEnums(schema);
2162
2378
  const modelRegex = /model\s+(\w+)\s*\{([\s\S]*?)\n\}/g;
2163
2379
  const scalarTypes = new Set([
2164
2380
  "Int",
@@ -2179,14 +2395,16 @@ var CliApp = class {
2179
2395
  body.split("\n").forEach((rawLine) => {
2180
2396
  const line = rawLine.trim();
2181
2397
  if (!line || line.startsWith("@@") || line.startsWith("//")) return;
2182
- const fieldMatch = line.match(/^(\w+)\s+([A-Za-z]+)(\?)?(?:\s|$)/);
2398
+ const fieldMatch = line.match(/^(\w+)\s+([A-Za-z][A-Za-z0-9_]*)(\[\])?(\?)?(?:\s|$)/);
2183
2399
  if (!fieldMatch) return;
2184
2400
  const fieldType = fieldMatch[2];
2185
- if (!scalarTypes.has(fieldType)) return;
2401
+ if (!scalarTypes.has(fieldType) && !enumDefinitions.has(fieldType)) return;
2402
+ const declarationType = this.prismaFieldTypeToTs(fieldType, Boolean(fieldMatch[3]), enumDefinitions);
2403
+ if (!declarationType) return;
2186
2404
  fields.push({
2187
2405
  name: fieldMatch[1],
2188
- type: this.prismaTypeToTs(fieldType),
2189
- nullable: Boolean(fieldMatch[3])
2406
+ type: declarationType,
2407
+ nullable: Boolean(fieldMatch[4])
2190
2408
  });
2191
2409
  });
2192
2410
  models.push({
@@ -2207,7 +2425,7 @@ var CliApp = class {
2207
2425
  * @param declarations A list of attribute declarations to sync.
2208
2426
  * @returns An object containing the updated content and a flag indicating if it was updated.
2209
2427
  */
2210
- syncModelDeclarations(modelSource, declarations) {
2428
+ syncModelDeclarations(modelSource, declarations, enums) {
2211
2429
  const lines = modelSource.split("\n");
2212
2430
  const classIndex = lines.findIndex((line) => /export\s+class\s+\w+\s+extends\s+Model<.+>\s*\{/.test(line));
2213
2431
  if (classIndex < 0) return {
@@ -2229,16 +2447,38 @@ var CliApp = class {
2229
2447
  content: modelSource,
2230
2448
  updated: false
2231
2449
  };
2232
- const withoutDeclares = lines.slice(classIndex + 1, classEndIndex).filter((line) => !/^\s*declare\s+\w+\??:\s*[^\n]+$/.test(line));
2233
- const rebuiltClass = [...declarations.map((declaration) => ` ${declaration}`), ...withoutDeclares];
2450
+ const withinClass = lines.slice(classIndex + 1, classEndIndex);
2451
+ const existingDeclarations = /* @__PURE__ */ new Map();
2452
+ withinClass.forEach((line) => {
2453
+ const declarationMatch = line.match(/^\s*declare\s+(\w+)\??:\s*([^;\n]+);?\s*$/);
2454
+ if (!declarationMatch) return;
2455
+ existingDeclarations.set(declarationMatch[1], {
2456
+ name: declarationMatch[1],
2457
+ raw: line.trim(),
2458
+ type: declarationMatch[2].trim()
2459
+ });
2460
+ });
2461
+ const withoutDeclares = withinClass.filter((line) => !/^\s*declare\s+\w+\??:\s*[^\n]+$/.test(line));
2462
+ const chosenDeclarations = declarations.map((declaration) => {
2463
+ const expectedType = `${declaration.type}${declaration.nullable ? " | null" : ""}`;
2464
+ const existingDeclaration = existingDeclarations.get(declaration.name);
2465
+ if (existingDeclaration && this.isCompatibleDeclarationType(existingDeclaration.type, expectedType, enums)) return existingDeclaration.raw;
2466
+ return `declare ${declaration.name}: ${expectedType}`;
2467
+ });
2468
+ const rebuiltClass = [...chosenDeclarations.map((declaration) => ` ${declaration}`), ...withoutDeclares];
2234
2469
  const content = [
2235
2470
  ...lines.slice(0, classIndex + 1),
2236
2471
  ...rebuiltClass,
2237
2472
  ...lines.slice(classEndIndex)
2238
2473
  ].join("\n");
2474
+ const enumImports = [...new Set(chosenDeclarations.flatMap((declaration) => {
2475
+ const type = declaration.replace(/^declare\s+\w+\??:\s*/, "").replace(/;$/, "").trim();
2476
+ return this.collectEnumReferences(type, enums);
2477
+ }))].sort((left, right) => left.localeCompare(right));
2478
+ const contentWithImports = this.syncPrismaEnumImports(content, enumImports);
2239
2479
  return {
2240
- content,
2241
- updated: content !== modelSource
2480
+ content: contentWithImports,
2481
+ updated: contentWithImports !== modelSource
2242
2482
  };
2243
2483
  }
2244
2484
  /**
@@ -2258,6 +2498,7 @@ var CliApp = class {
2258
2498
  if (!(0, fs.existsSync)(schemaPath)) throw new Error(`Prisma schema file not found: ${schemaPath}`);
2259
2499
  if (!(0, fs.existsSync)(modelsDir)) throw new Error(`Models directory not found: ${modelsDir}`);
2260
2500
  const schema = (0, fs.readFileSync)(schemaPath, "utf-8");
2501
+ const prismaEnums = this.parsePrismaEnums(schema);
2261
2502
  const prismaModels = this.parsePrismaModels(schema);
2262
2503
  const modelFiles = (0, fs.readdirSync)(modelsDir).filter((file) => file.endsWith(".ts"));
2263
2504
  const updated = [];
@@ -2277,8 +2518,7 @@ var CliApp = class {
2277
2518
  skipped.push(filePath);
2278
2519
  return;
2279
2520
  }
2280
- const declarations = prismaModel.fields.map((field) => `declare ${field.name}: ${field.type}${field.nullable ? " | null" : ""}`);
2281
- const synced = this.syncModelDeclarations(source, declarations);
2521
+ const synced = this.syncModelDeclarations(source, prismaModel.fields, prismaEnums);
2282
2522
  if (!synced.updated) {
2283
2523
  skipped.push(filePath);
2284
2524
  return;
package/dist/index.d.cts CHANGED
@@ -2470,6 +2470,34 @@ declare class CliApp {
2470
2470
  * @returns The corresponding TypeScript type.
2471
2471
  */
2472
2472
  private prismaTypeToTs;
2473
+ private splitTopLevel;
2474
+ private hasWrappedParentheses;
2475
+ private stripWrappedParentheses;
2476
+ private parseDeclarationType;
2477
+ private expandUnion;
2478
+ private isEnumTypeName;
2479
+ private isDeclarationNodeAssignable;
2480
+ private isDeclarationAssignable;
2481
+ private isCompatibleDeclarationType;
2482
+ private collectEnumReferencesFromNode;
2483
+ private collectEnumReferences;
2484
+ private syncPrismaEnumImports;
2485
+ /**
2486
+ * Parse Prisma enum definitions from a schema and return their member names.
2487
+ *
2488
+ * @param schema The Prisma schema source.
2489
+ * @returns A map of enum names to their declared member names.
2490
+ */
2491
+ private parsePrismaEnums;
2492
+ /**
2493
+ * Resolve the generated TypeScript declaration type for a Prisma field.
2494
+ *
2495
+ * @param fieldType The Prisma field type token.
2496
+ * @param isList Whether the field is declared as a Prisma list.
2497
+ * @param enums Known Prisma enum definitions.
2498
+ * @returns The declaration type to emit, or null when unsupported.
2499
+ */
2500
+ private prismaFieldTypeToTs;
2473
2501
  /**
2474
2502
  * Parse the Prisma schema to extract model definitions and their fields, focusing
2475
2503
  * on scalar types.
package/dist/index.d.mts CHANGED
@@ -2470,6 +2470,34 @@ declare class CliApp {
2470
2470
  * @returns The corresponding TypeScript type.
2471
2471
  */
2472
2472
  private prismaTypeToTs;
2473
+ private splitTopLevel;
2474
+ private hasWrappedParentheses;
2475
+ private stripWrappedParentheses;
2476
+ private parseDeclarationType;
2477
+ private expandUnion;
2478
+ private isEnumTypeName;
2479
+ private isDeclarationNodeAssignable;
2480
+ private isDeclarationAssignable;
2481
+ private isCompatibleDeclarationType;
2482
+ private collectEnumReferencesFromNode;
2483
+ private collectEnumReferences;
2484
+ private syncPrismaEnumImports;
2485
+ /**
2486
+ * Parse Prisma enum definitions from a schema and return their member names.
2487
+ *
2488
+ * @param schema The Prisma schema source.
2489
+ * @returns A map of enum names to their declared member names.
2490
+ */
2491
+ private parsePrismaEnums;
2492
+ /**
2493
+ * Resolve the generated TypeScript declaration type for a Prisma field.
2494
+ *
2495
+ * @param fieldType The Prisma field type token.
2496
+ * @param isList Whether the field is declared as a Prisma list.
2497
+ * @param enums Known Prisma enum definitions.
2498
+ * @returns The declaration type to emit, or null when unsupported.
2499
+ */
2500
+ private prismaFieldTypeToTs;
2473
2501
  /**
2474
2502
  * Parse the Prisma schema to extract model definitions and their fields, focusing
2475
2503
  * on scalar types.
package/dist/index.mjs CHANGED
@@ -2117,10 +2117,225 @@ var CliApp = class {
2117
2117
  if (value === "String") return "string";
2118
2118
  if (value === "Boolean") return "boolean";
2119
2119
  if (value === "DateTime") return "Date";
2120
- if (value === "Json") return "Record<string, unknown>";
2120
+ if (value === "Json") return "Record<string, unknown> | unknown[]";
2121
2121
  if (value === "Bytes") return "Buffer";
2122
2122
  return "unknown";
2123
2123
  }
2124
+ splitTopLevel(value, delimiter) {
2125
+ const parts = [];
2126
+ let start = 0;
2127
+ let angleDepth = 0;
2128
+ let parenthesisDepth = 0;
2129
+ let quote = null;
2130
+ for (let index = 0; index < value.length; index += 1) {
2131
+ const character = value[index];
2132
+ const previous = index > 0 ? value[index - 1] : "";
2133
+ if (quote) {
2134
+ if (character === quote && previous !== "\\") quote = null;
2135
+ continue;
2136
+ }
2137
+ if (character === "'" || character === "\"") {
2138
+ quote = character;
2139
+ continue;
2140
+ }
2141
+ if (character === "<") {
2142
+ angleDepth += 1;
2143
+ continue;
2144
+ }
2145
+ if (character === ">") {
2146
+ angleDepth = Math.max(0, angleDepth - 1);
2147
+ continue;
2148
+ }
2149
+ if (character === "(") {
2150
+ parenthesisDepth += 1;
2151
+ continue;
2152
+ }
2153
+ if (character === ")") {
2154
+ parenthesisDepth = Math.max(0, parenthesisDepth - 1);
2155
+ continue;
2156
+ }
2157
+ if (character === delimiter && angleDepth === 0 && parenthesisDepth === 0) {
2158
+ parts.push(value.slice(start, index).trim());
2159
+ start = index + 1;
2160
+ }
2161
+ }
2162
+ parts.push(value.slice(start).trim());
2163
+ return parts.filter(Boolean);
2164
+ }
2165
+ hasWrappedParentheses(value) {
2166
+ if (!value.startsWith("(") || !value.endsWith(")")) return false;
2167
+ let depth = 0;
2168
+ let quote = null;
2169
+ for (let index = 0; index < value.length; index += 1) {
2170
+ const character = value[index];
2171
+ const previous = index > 0 ? value[index - 1] : "";
2172
+ if (quote) {
2173
+ if (character === quote && previous !== "\\") quote = null;
2174
+ continue;
2175
+ }
2176
+ if (character === "'" || character === "\"") {
2177
+ quote = character;
2178
+ continue;
2179
+ }
2180
+ if (character === "(") depth += 1;
2181
+ if (character === ")") {
2182
+ depth -= 1;
2183
+ if (depth === 0 && index < value.length - 1) return false;
2184
+ }
2185
+ }
2186
+ return depth === 0;
2187
+ }
2188
+ stripWrappedParentheses(value) {
2189
+ let nextValue = value.trim();
2190
+ while (this.hasWrappedParentheses(nextValue)) nextValue = nextValue.slice(1, -1).trim();
2191
+ return nextValue;
2192
+ }
2193
+ parseDeclarationType(value) {
2194
+ const trimmed = this.stripWrappedParentheses(value.trim());
2195
+ if (!trimmed) return null;
2196
+ const unionParts = this.splitTopLevel(trimmed, "|");
2197
+ if (unionParts.length > 1) {
2198
+ const types = unionParts.map((part) => this.parseDeclarationType(part)).filter((part) => part !== null);
2199
+ if (types.length !== unionParts.length) return null;
2200
+ return {
2201
+ kind: "union",
2202
+ types: types.flatMap((type) => type.kind === "union" ? type.types : [type])
2203
+ };
2204
+ }
2205
+ if (trimmed.endsWith("[]")) {
2206
+ const element = this.parseDeclarationType(trimmed.slice(0, -2));
2207
+ if (!element) return null;
2208
+ return {
2209
+ kind: "array",
2210
+ element
2211
+ };
2212
+ }
2213
+ const arrayMatch = trimmed.match(/^(Array|ReadonlyArray)<([\s\S]+)>$/);
2214
+ if (arrayMatch) {
2215
+ const element = this.parseDeclarationType(arrayMatch[2]);
2216
+ if (!element) return null;
2217
+ return {
2218
+ kind: "array",
2219
+ element
2220
+ };
2221
+ }
2222
+ if (trimmed === "null") return { kind: "null" };
2223
+ if (trimmed.startsWith("'") && trimmed.endsWith("'") || trimmed.startsWith("\"") && trimmed.endsWith("\"")) return {
2224
+ kind: "string-literal",
2225
+ value: trimmed.slice(1, -1)
2226
+ };
2227
+ if (trimmed === "Record<string, unknown>") return {
2228
+ kind: "named",
2229
+ name: trimmed
2230
+ };
2231
+ if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(trimmed)) return {
2232
+ kind: "named",
2233
+ name: trimmed
2234
+ };
2235
+ return null;
2236
+ }
2237
+ expandUnion(node) {
2238
+ return node.kind === "union" ? node.types : [node];
2239
+ }
2240
+ isEnumTypeName(value, enums) {
2241
+ return enums.has(value);
2242
+ }
2243
+ isDeclarationNodeAssignable(actual, expected, enums) {
2244
+ if (expected.kind === "named") {
2245
+ if (expected.name === "unknown") return true;
2246
+ if (actual.kind === "named") {
2247
+ if (actual.name === expected.name) return true;
2248
+ if (expected.name === "string" && this.isEnumTypeName(actual.name, enums)) return true;
2249
+ }
2250
+ if (actual.kind === "string-literal") {
2251
+ if (expected.name === "string") return true;
2252
+ if (enums.get(expected.name)?.includes(actual.value)) return true;
2253
+ }
2254
+ return false;
2255
+ }
2256
+ if (expected.kind === "array") return actual.kind === "array" && this.isDeclarationAssignable(actual.element, expected.element, enums);
2257
+ if (expected.kind === "null") return actual.kind === "null";
2258
+ if (expected.kind === "string-literal") return actual.kind === "string-literal" && actual.value === expected.value;
2259
+ return false;
2260
+ }
2261
+ isDeclarationAssignable(actual, expected, enums) {
2262
+ const actualTypes = this.expandUnion(actual);
2263
+ const expectedTypes = this.expandUnion(expected);
2264
+ return actualTypes.every((actualType) => {
2265
+ return expectedTypes.some((expectedType) => {
2266
+ return this.isDeclarationNodeAssignable(actualType, expectedType, enums);
2267
+ });
2268
+ });
2269
+ }
2270
+ isCompatibleDeclarationType(actualType, expectedType, enums) {
2271
+ const actual = this.parseDeclarationType(actualType);
2272
+ const expected = this.parseDeclarationType(expectedType);
2273
+ if (!actual || !expected) return actualType.replace(/\s+/g, " ").trim() === expectedType.replace(/\s+/g, " ").trim();
2274
+ return this.isDeclarationAssignable(actual, expected, enums);
2275
+ }
2276
+ collectEnumReferencesFromNode(node, enums, collected) {
2277
+ if (node.kind === "named" && enums.has(node.name)) {
2278
+ collected.add(node.name);
2279
+ return;
2280
+ }
2281
+ if (node.kind === "array") {
2282
+ this.collectEnumReferencesFromNode(node.element, enums, collected);
2283
+ return;
2284
+ }
2285
+ if (node.kind === "union") node.types.forEach((type) => this.collectEnumReferencesFromNode(type, enums, collected));
2286
+ }
2287
+ collectEnumReferences(type, enums) {
2288
+ const parsed = this.parseDeclarationType(type);
2289
+ if (!parsed) return [];
2290
+ const collected = /* @__PURE__ */ new Set();
2291
+ this.collectEnumReferencesFromNode(parsed, enums, collected);
2292
+ return [...collected].sort((left, right) => left.localeCompare(right));
2293
+ }
2294
+ syncPrismaEnumImports(modelSource, enumTypes) {
2295
+ if (enumTypes.length === 0) return modelSource;
2296
+ const importRegex = /^import\s+type\s+\{([^}]+)\}\s+from\s+['"]@prisma\/client['"]\s*;?$/m;
2297
+ const existingImport = modelSource.match(importRegex);
2298
+ if (existingImport) {
2299
+ const existingTypes = existingImport[1].split(",").map((value) => value.trim()).filter(Boolean);
2300
+ const mergedTypes = [...new Set([...existingTypes, ...enumTypes])].sort((left, right) => left.localeCompare(right));
2301
+ return modelSource.replace(importRegex, `import type { ${mergedTypes.join(", ")} } from '@prisma/client'`);
2302
+ }
2303
+ const lines = modelSource.split("\n");
2304
+ let insertionIndex = 0;
2305
+ while (insertionIndex < lines.length && lines[insertionIndex].trim().startsWith("import ")) insertionIndex += 1;
2306
+ lines.splice(insertionIndex, 0, `import type { ${enumTypes.join(", ")} } from '@prisma/client'`);
2307
+ return lines.join("\n");
2308
+ }
2309
+ /**
2310
+ * Parse Prisma enum definitions from a schema and return their member names.
2311
+ *
2312
+ * @param schema The Prisma schema source.
2313
+ * @returns A map of enum names to their declared member names.
2314
+ */
2315
+ parsePrismaEnums(schema) {
2316
+ const enums = /* @__PURE__ */ new Map();
2317
+ for (const match of schema.matchAll(PRISMA_ENUM_REGEX)) {
2318
+ const enumName = match[1];
2319
+ const values = match[0].split("\n").slice(1, -1).map((line) => line.trim()).filter((line) => Boolean(line) && !line.startsWith("//")).map((line) => {
2320
+ return line.match(/^([A-Za-z][A-Za-z0-9_]*)\b/)?.[1];
2321
+ }).filter((value) => Boolean(value));
2322
+ enums.set(enumName, values);
2323
+ }
2324
+ return enums;
2325
+ }
2326
+ /**
2327
+ * Resolve the generated TypeScript declaration type for a Prisma field.
2328
+ *
2329
+ * @param fieldType The Prisma field type token.
2330
+ * @param isList Whether the field is declared as a Prisma list.
2331
+ * @param enums Known Prisma enum definitions.
2332
+ * @returns The declaration type to emit, or null when unsupported.
2333
+ */
2334
+ prismaFieldTypeToTs(fieldType, isList, enums) {
2335
+ const baseType = enums.has(fieldType) ? fieldType : this.prismaTypeToTs(fieldType);
2336
+ if (baseType === "unknown" && !enums.has(fieldType)) return null;
2337
+ return isList ? `Array<${baseType}>` : baseType;
2338
+ }
2124
2339
  /**
2125
2340
  * Parse the Prisma schema to extract model definitions and their fields, focusing
2126
2341
  * on scalar types.
@@ -2130,6 +2345,7 @@ var CliApp = class {
2130
2345
  */
2131
2346
  parsePrismaModels(schema) {
2132
2347
  const models = [];
2348
+ const enumDefinitions = this.parsePrismaEnums(schema);
2133
2349
  const modelRegex = /model\s+(\w+)\s*\{([\s\S]*?)\n\}/g;
2134
2350
  const scalarTypes = new Set([
2135
2351
  "Int",
@@ -2150,14 +2366,16 @@ var CliApp = class {
2150
2366
  body.split("\n").forEach((rawLine) => {
2151
2367
  const line = rawLine.trim();
2152
2368
  if (!line || line.startsWith("@@") || line.startsWith("//")) return;
2153
- const fieldMatch = line.match(/^(\w+)\s+([A-Za-z]+)(\?)?(?:\s|$)/);
2369
+ const fieldMatch = line.match(/^(\w+)\s+([A-Za-z][A-Za-z0-9_]*)(\[\])?(\?)?(?:\s|$)/);
2154
2370
  if (!fieldMatch) return;
2155
2371
  const fieldType = fieldMatch[2];
2156
- if (!scalarTypes.has(fieldType)) return;
2372
+ if (!scalarTypes.has(fieldType) && !enumDefinitions.has(fieldType)) return;
2373
+ const declarationType = this.prismaFieldTypeToTs(fieldType, Boolean(fieldMatch[3]), enumDefinitions);
2374
+ if (!declarationType) return;
2157
2375
  fields.push({
2158
2376
  name: fieldMatch[1],
2159
- type: this.prismaTypeToTs(fieldType),
2160
- nullable: Boolean(fieldMatch[3])
2377
+ type: declarationType,
2378
+ nullable: Boolean(fieldMatch[4])
2161
2379
  });
2162
2380
  });
2163
2381
  models.push({
@@ -2178,7 +2396,7 @@ var CliApp = class {
2178
2396
  * @param declarations A list of attribute declarations to sync.
2179
2397
  * @returns An object containing the updated content and a flag indicating if it was updated.
2180
2398
  */
2181
- syncModelDeclarations(modelSource, declarations) {
2399
+ syncModelDeclarations(modelSource, declarations, enums) {
2182
2400
  const lines = modelSource.split("\n");
2183
2401
  const classIndex = lines.findIndex((line) => /export\s+class\s+\w+\s+extends\s+Model<.+>\s*\{/.test(line));
2184
2402
  if (classIndex < 0) return {
@@ -2200,16 +2418,38 @@ var CliApp = class {
2200
2418
  content: modelSource,
2201
2419
  updated: false
2202
2420
  };
2203
- const withoutDeclares = lines.slice(classIndex + 1, classEndIndex).filter((line) => !/^\s*declare\s+\w+\??:\s*[^\n]+$/.test(line));
2204
- const rebuiltClass = [...declarations.map((declaration) => ` ${declaration}`), ...withoutDeclares];
2421
+ const withinClass = lines.slice(classIndex + 1, classEndIndex);
2422
+ const existingDeclarations = /* @__PURE__ */ new Map();
2423
+ withinClass.forEach((line) => {
2424
+ const declarationMatch = line.match(/^\s*declare\s+(\w+)\??:\s*([^;\n]+);?\s*$/);
2425
+ if (!declarationMatch) return;
2426
+ existingDeclarations.set(declarationMatch[1], {
2427
+ name: declarationMatch[1],
2428
+ raw: line.trim(),
2429
+ type: declarationMatch[2].trim()
2430
+ });
2431
+ });
2432
+ const withoutDeclares = withinClass.filter((line) => !/^\s*declare\s+\w+\??:\s*[^\n]+$/.test(line));
2433
+ const chosenDeclarations = declarations.map((declaration) => {
2434
+ const expectedType = `${declaration.type}${declaration.nullable ? " | null" : ""}`;
2435
+ const existingDeclaration = existingDeclarations.get(declaration.name);
2436
+ if (existingDeclaration && this.isCompatibleDeclarationType(existingDeclaration.type, expectedType, enums)) return existingDeclaration.raw;
2437
+ return `declare ${declaration.name}: ${expectedType}`;
2438
+ });
2439
+ const rebuiltClass = [...chosenDeclarations.map((declaration) => ` ${declaration}`), ...withoutDeclares];
2205
2440
  const content = [
2206
2441
  ...lines.slice(0, classIndex + 1),
2207
2442
  ...rebuiltClass,
2208
2443
  ...lines.slice(classEndIndex)
2209
2444
  ].join("\n");
2445
+ const enumImports = [...new Set(chosenDeclarations.flatMap((declaration) => {
2446
+ const type = declaration.replace(/^declare\s+\w+\??:\s*/, "").replace(/;$/, "").trim();
2447
+ return this.collectEnumReferences(type, enums);
2448
+ }))].sort((left, right) => left.localeCompare(right));
2449
+ const contentWithImports = this.syncPrismaEnumImports(content, enumImports);
2210
2450
  return {
2211
- content,
2212
- updated: content !== modelSource
2451
+ content: contentWithImports,
2452
+ updated: contentWithImports !== modelSource
2213
2453
  };
2214
2454
  }
2215
2455
  /**
@@ -2229,6 +2469,7 @@ var CliApp = class {
2229
2469
  if (!existsSync$1(schemaPath)) throw new Error(`Prisma schema file not found: ${schemaPath}`);
2230
2470
  if (!existsSync$1(modelsDir)) throw new Error(`Models directory not found: ${modelsDir}`);
2231
2471
  const schema = readFileSync$1(schemaPath, "utf-8");
2472
+ const prismaEnums = this.parsePrismaEnums(schema);
2232
2473
  const prismaModels = this.parsePrismaModels(schema);
2233
2474
  const modelFiles = readdirSync$1(modelsDir).filter((file) => file.endsWith(".ts"));
2234
2475
  const updated = [];
@@ -2248,8 +2489,7 @@ var CliApp = class {
2248
2489
  skipped.push(filePath);
2249
2490
  return;
2250
2491
  }
2251
- const declarations = prismaModel.fields.map((field) => `declare ${field.name}: ${field.type}${field.nullable ? " | null" : ""}`);
2252
- const synced = this.syncModelDeclarations(source, declarations);
2492
+ const synced = this.syncModelDeclarations(source, prismaModel.fields, prismaEnums);
2253
2493
  if (!synced.updated) {
2254
2494
  skipped.push(filePath);
2255
2495
  return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arkormx",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
4
  "description": "Modern TypeScript-first ORM for Node.js.",
5
5
  "keywords": [
6
6
  "orm",
@@ -48,14 +48,14 @@
48
48
  "devDependencies": {
49
49
  "@eslint/js": "^10.0.1",
50
50
  "@eslint/markdown": "^7.5.1",
51
- "@prisma/adapter-pg": "^7.4.2",
52
51
  "@types/express": "^4.17.21",
53
52
  "@types/node": "^20.10.6",
53
+ "@prisma/adapter-pg": "^7.6.0",
54
54
  "@vitest/coverage-v8": "4.0.18",
55
55
  "barrelize": "^1.7.3",
56
56
  "eslint": "^10.0.0",
57
57
  "pg": "^8.19.0",
58
- "prisma": "^7.4.2",
58
+ "prisma": "^7.6.0",
59
59
  "tsdown": "^0.20.3",
60
60
  "tsx": "^4.21.0",
61
61
  "typescript": "^5.3.3",
@@ -75,7 +75,7 @@
75
75
  "dotenv": "^17.3.1"
76
76
  },
77
77
  "peerDependencies": {
78
- "@prisma/client": "^7.4.2"
78
+ "@prisma/client": "^7.6.0"
79
79
  },
80
80
  "scripts": {
81
81
  "cmd": "tsx src/cli/index.ts",