@danielhritcu/zenstack-custom 1.1.0 → 1.2.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.cjs CHANGED
@@ -940,6 +940,32 @@ __name(sortTypedJsonFields, "sortTypedJsonFields");
940
940
 
941
941
  // src/codegen/emit.ts
942
942
  var DB_ATTR_RE = /@db\.(\w+)(?:\((\d+)\))?/;
943
+ function isUuidField(t, fieldName) {
944
+ const col = t.table[fieldName];
945
+ if (!col) return false;
946
+ return col.columnType === "PgUUID";
947
+ }
948
+ __name(isUuidField, "isUuidField");
949
+ function isPkField(t, fieldName) {
950
+ const col = t.table[fieldName];
951
+ if (!col) return false;
952
+ if (col.primary) return true;
953
+ const sqlName = col.name;
954
+ return t.cfg.primaryKeys.some((pk) => pk.columns.some((c) => c.name === sqlName));
955
+ }
956
+ __name(isPkField, "isPkField");
957
+ function findFkTarget(t, fieldName, fkRelations) {
958
+ const modelFks = fkRelations[t.modelName];
959
+ if (!modelFks) return null;
960
+ for (const fkInfo of Object.values(modelFks)) {
961
+ const idx = fkInfo.fields.indexOf(fieldName);
962
+ if (idx >= 0) {
963
+ return fkInfo.targetExportName;
964
+ }
965
+ }
966
+ return null;
967
+ }
968
+ __name(findFkTarget, "findFkTarget");
943
969
  function buildDerivedEnumName(modelName, fieldName) {
944
970
  return `${toPascalCase(modelName)}${toPascalCase(fieldName)}`;
945
971
  }
@@ -1435,8 +1461,9 @@ function emitSchemaTs({ tables, enumMap, modelRelations, inverseRelations, searc
1435
1461
  return out.join("\n");
1436
1462
  }
1437
1463
  __name(emitSchemaTs, "emitSchemaTs");
1438
- function emitModelsTs(tables, typedJsonFields = []) {
1464
+ function emitModelsTs(tables, typedJsonFields = [], branding) {
1439
1465
  const jsonFieldAliases = createJsonFieldAliasMap(typedJsonFields);
1466
+ const hasBranding = branding && (Object.keys(branding.brandedFields).length > 0 || tables.some((t) => getEntityFieldNames(t).some((f) => isUuidField(t, f) && isPkField(t, f))));
1440
1467
  const out = [
1441
1468
  ...emitGeneratedFileBanner()
1442
1469
  ];
@@ -1444,6 +1471,10 @@ function emitModelsTs(tables, typedJsonFields = []) {
1444
1471
  out.push('import type * as $ from "@zenstackhq/orm";');
1445
1472
  out.push('import type * as Json from "./json";');
1446
1473
  out.push('import { type SchemaType as Schema } from "./schema";');
1474
+ if (hasBranding) {
1475
+ out.push("");
1476
+ out.push("declare const __brand: unique symbol;");
1477
+ }
1447
1478
  out.push("");
1448
1479
  for (const t of tables) {
1449
1480
  out.push(`export interface ${t.typeName} extends $.ModelResult<Schema, ${esc(t.modelName)}> {}`);
@@ -1454,7 +1485,12 @@ function emitModelsTs(tables, typedJsonFields = []) {
1454
1485
  if (jsonAlias) {
1455
1486
  out.push(` export interface ${exportName} extends Json.${jsonAlias} {}`);
1456
1487
  } else {
1457
- out.push(` export type ${exportName} = ${t.typeName}[${esc(fieldName)}];`);
1488
+ const brandedLine = branding ? emitBrandedFieldLine(t, fieldName, exportName, branding, " ") : null;
1489
+ if (brandedLine) {
1490
+ out.push(...brandedLine);
1491
+ } else {
1492
+ out.push(` export type ${exportName} = ${t.typeName}[${esc(fieldName)}];`);
1493
+ }
1458
1494
  }
1459
1495
  }
1460
1496
  out.push("}");
@@ -1463,6 +1499,33 @@ function emitModelsTs(tables, typedJsonFields = []) {
1463
1499
  return out.join("\n");
1464
1500
  }
1465
1501
  __name(emitModelsTs, "emitModelsTs");
1502
+ function emitBrandedFieldLine(t, fieldName, exportName, branding, indent) {
1503
+ const customBranded = branding.brandedFields[t.modelName]?.[fieldName];
1504
+ if (isUuidField(t, fieldName) && isPkField(t, fieldName)) {
1505
+ const brand = `${t.modelName}.${fieldName}`;
1506
+ return [
1507
+ `${indent}export type ${exportName} = string & { [__brand]: '${brand}' };`,
1508
+ `${indent}export const ${exportName} = (id: string): ${exportName} => id as ${exportName};`
1509
+ ];
1510
+ }
1511
+ if (isUuidField(t, fieldName)) {
1512
+ const targetExportName = findFkTarget(t, fieldName, branding.fkRelations);
1513
+ if (targetExportName) {
1514
+ return [
1515
+ `${indent}export type ${exportName} = ${targetExportName}.Id;`
1516
+ ];
1517
+ }
1518
+ }
1519
+ if (customBranded) {
1520
+ const brand = `${t.modelName}.${fieldName}`;
1521
+ return [
1522
+ `${indent}export type ${exportName} = string & { [__brand]: '${brand}' };`,
1523
+ `${indent}export const ${exportName} = (id: string): ${exportName} => id as ${exportName};`
1524
+ ];
1525
+ }
1526
+ return null;
1527
+ }
1528
+ __name(emitBrandedFieldLine, "emitBrandedFieldLine");
1466
1529
  function emitViewsTs(views, typedJsonFields = []) {
1467
1530
  const jsonFieldAliases = createJsonFieldAliasMap(typedJsonFields);
1468
1531
  const out = [
@@ -1528,12 +1591,13 @@ function emitInputTs(tables) {
1528
1591
  return out.join("\n");
1529
1592
  }
1530
1593
  __name(emitInputTs, "emitInputTs");
1531
- function emitIndexTs({ tables, views, enumMap, typeDefs, typedJsonFields }) {
1594
+ function emitIndexTs({ tables, views, enumMap, typeDefs, typedJsonFields, branding }) {
1532
1595
  const allEnums = new Map([
1533
1596
  ...enumMap,
1534
1597
  ...collectDerivedEnums(tables, enumMap)
1535
1598
  ]);
1536
1599
  const jsonFieldAliases = createJsonFieldAliasMap(typedJsonFields);
1600
+ const hasBranding = branding && (Object.keys(branding.brandedFields).length > 0 || tables.some((t) => getEntityFieldNames(t).some((f) => isUuidField(t, f) && isPkField(t, f))));
1537
1601
  const out = [
1538
1602
  ...emitGeneratedFileBanner()
1539
1603
  ];
@@ -1541,6 +1605,10 @@ function emitIndexTs({ tables, views, enumMap, typeDefs, typedJsonFields }) {
1541
1605
  out.push('import type * as $ from "@zenstackhq/orm";');
1542
1606
  out.push('import type * as RawJson from "../../supabase/schema/types";');
1543
1607
  out.push('import { type SchemaType as Schema } from "./schema";');
1608
+ if (hasBranding) {
1609
+ out.push("");
1610
+ out.push("declare const __brand: unique symbol;");
1611
+ }
1544
1612
  out.push("");
1545
1613
  out.push("export namespace Json {");
1546
1614
  out.push(...emitJsonNamespaceBlock(typeDefs, typedJsonFields, " "));
@@ -1555,7 +1623,7 @@ function emitIndexTs({ tables, views, enumMap, typeDefs, typedJsonFields }) {
1555
1623
  out.push("");
1556
1624
  out.push("export namespace Table {");
1557
1625
  for (const table of tables) {
1558
- out.push(...emitEntityNamespaceBlock(table, table.modelName, jsonFieldAliases, " "));
1626
+ out.push(...emitEntityNamespaceBlock(table, table.modelName, jsonFieldAliases, " ", branding));
1559
1627
  out.push("");
1560
1628
  }
1561
1629
  out.push("}");
@@ -1714,7 +1782,7 @@ function createJsonFieldAliasMap(typedJsonFields) {
1714
1782
  ]));
1715
1783
  }
1716
1784
  __name(createJsonFieldAliasMap, "createJsonFieldAliasMap");
1717
- function emitEntityNamespaceBlock(entity, modelName, jsonFieldAliases, indent = "") {
1785
+ function emitEntityNamespaceBlock(entity, modelName, jsonFieldAliases, indent = "", branding) {
1718
1786
  const out = [];
1719
1787
  out.push(`${indent}export interface ${entity.typeName} extends $.ModelResult<Schema, ${esc(modelName)}> {}`);
1720
1788
  out.push(`${indent}export namespace ${entity.typeName} {`);
@@ -1724,7 +1792,13 @@ function emitEntityNamespaceBlock(entity, modelName, jsonFieldAliases, indent =
1724
1792
  if (jsonAlias) {
1725
1793
  out.push(`${indent} export interface ${exportName} extends Json.${jsonAlias} {}`);
1726
1794
  } else {
1727
- out.push(`${indent} export type ${exportName} = ${entity.typeName}[${esc(fieldName)}];`);
1795
+ const isTable = "table" in entity;
1796
+ const brandedLine = isTable && branding ? emitBrandedFieldLine(entity, fieldName, exportName, branding, `${indent} `) : null;
1797
+ if (brandedLine) {
1798
+ out.push(...brandedLine);
1799
+ } else {
1800
+ out.push(`${indent} export type ${exportName} = ${entity.typeName}[${esc(fieldName)}];`);
1801
+ }
1728
1802
  }
1729
1803
  }
1730
1804
  out.push(`${indent}}`);
@@ -2273,6 +2347,7 @@ function resolveConfig(config) {
2273
2347
  const derivedFieldsMap = {};
2274
2348
  const validationSchemas = {};
2275
2349
  const fkRelations = {};
2350
+ const brandedFields = {};
2276
2351
  for (const [derivedName, desc] of Object.entries(derived)) {
2277
2352
  const baseExportName = sqlNameToExportName(tables, desc.baseName);
2278
2353
  if (!derivedModels[baseExportName]) {
@@ -2405,6 +2480,9 @@ function resolveConfig(config) {
2405
2480
  if (modelConfig.validate) {
2406
2481
  validationSchemas[modelName] = modelConfig.validate;
2407
2482
  }
2483
+ if (modelConfig.branded) {
2484
+ brandedFields[modelName] = modelConfig.branded;
2485
+ }
2408
2486
  if (modelConfig.search) {
2409
2487
  const modelSearchDefaults = {};
2410
2488
  for (const [profileName, profile] of Object.entries(modelConfig.search)) {
@@ -2424,7 +2502,8 @@ function resolveConfig(config) {
2424
2502
  modelScopes,
2425
2503
  derivedFields: derivedFieldsMap,
2426
2504
  validationSchemas,
2427
- fkRelations
2505
+ fkRelations,
2506
+ brandedFields
2428
2507
  };
2429
2508
  }
2430
2509
  __name(resolveConfig, "resolveConfig");
@@ -2458,12 +2537,17 @@ async function runCodegen(config) {
2458
2537
  fieldToTypeDef,
2459
2538
  views
2460
2539
  });
2540
+ const branding = {
2541
+ fkRelations: resolved.fkRelations,
2542
+ brandedFields: resolved.brandedFields
2543
+ };
2461
2544
  const indexContent = emitIndexTs({
2462
2545
  tables,
2463
2546
  views,
2464
2547
  enumMap,
2465
2548
  typeDefs,
2466
- typedJsonFields
2549
+ typedJsonFields,
2550
+ branding
2467
2551
  });
2468
2552
  const compatContent = emitCompatTs({
2469
2553
  tables,
@@ -2472,7 +2556,7 @@ async function runCodegen(config) {
2472
2556
  typedJsonFields
2473
2557
  });
2474
2558
  const ormTypesContent = emitOrmTypesTs(tables);
2475
- const modelsContent = emitModelsTs(tables, typedJsonFields);
2559
+ const modelsContent = emitModelsTs(tables, typedJsonFields, branding);
2476
2560
  const inputContent = emitInputTs(tables);
2477
2561
  const enumsContent = emitEnumsTs(enumMap);
2478
2562
  const jsonContent = emitJsonTs(typeDefs, typedJsonFields);