@postxl/schema 1.2.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,6 +5,9 @@ export declare const standardFieldName_createdAt: string & import("zod").$brand<
5
5
  export declare const standardFieldName_updatedAt: string & import("zod").$brand<"PXL.FieldName">;
6
6
  export declare const standardFieldName_name: string & import("zod").$brand<"PXL.FieldName">;
7
7
  export declare const standardFieldName_label: string & import("zod").$brand<"PXL.FieldName">;
8
+ export declare const standardFieldName_key: string & import("zod").$brand<"PXL.FieldName">;
9
+ export declare const standardFieldName_excelKey: string & import("zod").$brand<"PXL.FieldName">;
10
+ export declare const standardFieldName_slug: string & import("zod").$brand<"PXL.FieldName">;
8
11
  export declare const standardFieldNames: (string & import("zod").$brand<"PXL.FieldName">)[];
9
12
  export declare const standardField_id: FieldJSONInput;
10
13
  export declare const standardField_createdAt: FieldJSONInput;
@@ -33,13 +33,17 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.standardFields = exports.standardField_name = exports.standardField_updatedAt = exports.standardField_createdAt = exports.standardField_id = exports.standardFieldNames = exports.standardFieldName_label = exports.standardFieldName_name = exports.standardFieldName_updatedAt = exports.standardFieldName_createdAt = exports.standardFieldName_id = void 0;
36
+ exports.standardFields = exports.standardField_name = exports.standardField_updatedAt = exports.standardField_createdAt = exports.standardField_id = exports.standardFieldNames = exports.standardFieldName_slug = exports.standardFieldName_excelKey = exports.standardFieldName_key = exports.standardFieldName_label = exports.standardFieldName_name = exports.standardFieldName_updatedAt = exports.standardFieldName_createdAt = exports.standardFieldName_id = void 0;
37
37
  const Branded = __importStar(require("./shared/brands"));
38
38
  exports.standardFieldName_id = Branded.toFieldIdName('id');
39
39
  exports.standardFieldName_createdAt = Branded.toFieldName('createdAt');
40
40
  exports.standardFieldName_updatedAt = Branded.toFieldName('updatedAt');
41
41
  exports.standardFieldName_name = Branded.toFieldName('name');
42
42
  exports.standardFieldName_label = Branded.toFieldName('label');
43
+ // Field names used as fallback for keyField resolution (in order: key, excelKey, slug, id)
44
+ exports.standardFieldName_key = Branded.toFieldName('key');
45
+ exports.standardFieldName_excelKey = Branded.toFieldName('excelKey');
46
+ exports.standardFieldName_slug = Branded.toFieldName('slug');
43
47
  exports.standardFieldNames = [
44
48
  exports.standardFieldName_id,
45
49
  exports.standardFieldName_createdAt,
@@ -13,6 +13,7 @@ export declare const zModelJSON: z.ZodPipe<z.ZodTransform<unknown, unknown>, z.Z
13
13
  standardFields: z.ZodOptional<z.ZodArray<z.ZodString>>;
14
14
  defaultField: z.ZodOptional<z.ZodString>;
15
15
  labelField: z.ZodOptional<z.ZodString>;
16
+ keyField: z.ZodOptional<z.ZodString>;
16
17
  indexes: z.ZodOptional<z.ZodArray<z.ZodArray<z.core.$ZodBranded<z.ZodString, "PXL.FieldName", "out">>>>;
17
18
  seed: z.ZodOptional<z.ZodArray<z.ZodAny>>;
18
19
  source: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
@@ -80,6 +80,12 @@ Example use case: default language, root region in region tree.`),
80
80
  .string()
81
81
  .optional().describe(`The name of the field that is used in the UI as the label of the entity.
82
82
  The label field must be a string field. If not provided, the \`name\` field is used as the label.`),
83
+ keyField: zod_1.z
84
+ //
85
+ .string()
86
+ .optional().describe(`The name of the field that is used as the key (unique identifier) for the entity.
87
+ The key field must be a scalar string field. If not provided, the fallback order is: \`key\`, \`excelKey\`, \`slug\`, then \`id\`.
88
+ The key field is used in navigation, Excel import, and other places where a human-readable identifier is needed.`),
83
89
  indexes: zod_1.z
84
90
  //
85
91
  .array(zod_1.z.array(brands_1.zFieldName).min(2, { error: 'Index must have at least 2 fields' }))
@@ -11,6 +11,7 @@ export declare function modelJSONTransformer(input: ModelJSON, ctx: z.Refinement
11
11
  databaseName: string & z.core.$brand<"PXL.DatabaseModelName">;
12
12
  defaultFieldName: (string & z.core.$brand<"PXL.FieldName">) | undefined;
13
13
  labelFieldName: (string & z.core.$brand<"PXL.FieldName">) | undefined;
14
+ keyFieldName: (string & z.core.$brand<"PXL.FieldName">) | undefined;
14
15
  isReadonly: boolean;
15
16
  excelName: string;
16
17
  description: string;
@@ -43,16 +44,18 @@ declare function addDefaults({ description, fields, standardFields, seed, faker,
43
44
  schema?: string | undefined;
44
45
  defaultField?: string | undefined;
45
46
  labelField?: string | undefined;
47
+ keyField?: string | undefined;
46
48
  source?: Record<string, unknown> | undefined;
47
49
  };
48
50
  type WithDefaults = ReturnType<typeof addDefaults>;
49
- declare function brandModel({ name, schema, databaseName, standardFields, defaultField, labelField, ...input }: WithDefaults): {
51
+ declare function brandModel({ name, schema, databaseName, standardFields, defaultField, labelField, keyField, ...input }: WithDefaults): {
50
52
  name: string & z.core.$brand<"PXL.ModelName">;
51
53
  schema: (string & z.core.$brand<"PXL.DatabaseSchemaName">) | undefined;
52
54
  databaseName: string & z.core.$brand<"PXL.DatabaseModelName">;
53
55
  standardFields: (string & z.core.$brand<"PXL.FieldName">)[];
54
56
  defaultFieldName: (string & z.core.$brand<"PXL.FieldName">) | undefined;
55
57
  labelFieldName: (string & z.core.$brand<"PXL.FieldName">) | undefined;
58
+ keyFieldName: (string & z.core.$brand<"PXL.FieldName">) | undefined;
56
59
  isReadonly: boolean;
57
60
  excelName: string;
58
61
  description: string;
@@ -80,6 +83,7 @@ declare function combineFields({ fields, standardFields, ...model }: BrandedMode
80
83
  databaseName: string & z.core.$brand<"PXL.DatabaseModelName">;
81
84
  defaultFieldName: (string & z.core.$brand<"PXL.FieldName">) | undefined;
82
85
  labelFieldName: (string & z.core.$brand<"PXL.FieldName">) | undefined;
86
+ keyFieldName: (string & z.core.$brand<"PXL.FieldName">) | undefined;
83
87
  isReadonly: boolean;
84
88
  excelName: string;
85
89
  description: string;
@@ -65,7 +65,7 @@ function addDefaults({ description, fields, standardFields, seed, faker, ...inpu
65
65
  },
66
66
  };
67
67
  }
68
- function brandModel({ name, schema, databaseName, standardFields, defaultField, labelField, ...input }) {
68
+ function brandModel({ name, schema, databaseName, standardFields, defaultField, labelField, keyField, ...input }) {
69
69
  return {
70
70
  ...input,
71
71
  name: (0, model_brands_1.toModelName)(name),
@@ -74,6 +74,7 @@ function brandModel({ name, schema, databaseName, standardFields, defaultField,
74
74
  standardFields: standardFields.map((f) => Field.toFieldName(f)),
75
75
  defaultFieldName: defaultField ? Field.toFieldName(defaultField) : undefined,
76
76
  labelFieldName: labelField ? Field.toFieldName(labelField) : undefined,
77
+ keyFieldName: keyField ? Field.toFieldName(keyField) : undefined,
77
78
  };
78
79
  }
79
80
  function combineFields({ fields, standardFields, ...model }, ctx) {
@@ -13,6 +13,13 @@ export type Model = {
13
13
  idField: Field.FieldId;
14
14
  defaultField: Field.FieldDatabaseNative | undefined;
15
15
  labelField: Field.FieldDatabaseNative;
16
+ /**
17
+ * The field used as the key/unique identifier for the entity.
18
+ * Used in navigation, Excel import, and other places where a human-readable identifier is needed.
19
+ * Must be a scalar string field. Resolved from: explicit keyField, or fallback to key, excelKey, slug, id (in that order).
20
+ * Always defined since id field is always present.
21
+ */
22
+ keyField: Field.FieldDatabaseNative;
16
23
  indexes: ModelIndex[];
17
24
  seed: Seed[];
18
25
  /**
@@ -23,6 +23,7 @@ export declare const zProjectSchemaJSON: z.ZodPipe<z.ZodTransform<unknown, unkno
23
23
  standardFields: z.ZodOptional<z.ZodArray<z.ZodString>>;
24
24
  defaultField: z.ZodOptional<z.ZodString>;
25
25
  labelField: z.ZodOptional<z.ZodString>;
26
+ keyField: z.ZodOptional<z.ZodString>;
26
27
  indexes: z.ZodOptional<z.ZodArray<z.ZodArray<z.core.$ZodBranded<z.ZodString, "PXL.FieldName", "out">>>>;
27
28
  seed: z.ZodOptional<z.ZodArray<z.ZodAny>>;
28
29
  source: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
@@ -28,6 +28,7 @@ export declare const zProjectSchema: z.ZodPipe<z.ZodPipe<z.ZodTransform<unknown,
28
28
  standardFields: z.ZodOptional<z.ZodArray<z.ZodString>>;
29
29
  defaultField: z.ZodOptional<z.ZodString>;
30
30
  labelField: z.ZodOptional<z.ZodString>;
31
+ keyField: z.ZodOptional<z.ZodString>;
31
32
  indexes: z.ZodOptional<z.ZodArray<z.ZodArray<z.core.$ZodBranded<z.ZodString, "PXL.FieldName", "out">>>>;
32
33
  seed: z.ZodOptional<z.ZodArray<z.ZodAny>>;
33
34
  source: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
@@ -73,6 +74,7 @@ export declare const zProjectSchema: z.ZodPipe<z.ZodPipe<z.ZodTransform<unknown,
73
74
  standardFields?: string[] | undefined;
74
75
  defaultField?: string | undefined;
75
76
  labelField?: string | undefined;
77
+ keyField?: string | undefined;
76
78
  indexes?: (string & z.core.$brand<"PXL.FieldName">)[][] | undefined;
77
79
  seed?: any[] | undefined;
78
80
  source?: Record<string, unknown> | undefined;
@@ -270,13 +270,15 @@ function transformModel(model, projectSchema, ctx) {
270
270
  const idField = extractIdField(fields, model, ctx);
271
271
  const defaultField = extractDefaultField(model, fields, ctx);
272
272
  const labelField = extractLabelField(model, fields, ctx);
273
- const { defaultFieldName, labelFieldName, ...remainingModel } = model;
273
+ const keyField = extractKeyField(model, fields, idField, ctx);
274
+ const { defaultFieldName, labelFieldName, keyFieldName, ...remainingModel } = model;
274
275
  return {
275
276
  ...remainingModel,
276
277
  databaseSchema: model.schema ?? projectSchema.defaultDatabaseSchema,
277
278
  fields,
278
279
  defaultField,
279
280
  labelField,
281
+ keyField,
280
282
  idField,
281
283
  };
282
284
  }
@@ -444,3 +446,53 @@ function extractLabelFieldFromNameOrIdField({ name }, fields, ctx) {
444
446
  }
445
447
  return fallbackField;
446
448
  }
449
+ function extractKeyField({ keyFieldName, ...model }, fields, idField, ctx) {
450
+ if (keyFieldName === undefined) {
451
+ return extractKeyFieldFromFallbacks(fields, idField);
452
+ }
453
+ const keyField = fields.get(keyFieldName);
454
+ if (keyField === undefined) {
455
+ ctx.addIssue({
456
+ code: zod_1.z.ZodIssueCode.custom,
457
+ message: `Key field ${keyFieldName} is not defined in model ${model.name}!`,
458
+ });
459
+ return zod_1.z.NEVER;
460
+ }
461
+ if (keyField.kind !== 'scalar' && keyField.kind !== 'id') {
462
+ ctx.addIssue({
463
+ code: zod_1.z.ZodIssueCode.custom,
464
+ message: `Key field ${keyFieldName} in model ${model.name} must be a scalar string field, but instead is of type ${Field.getFieldType(keyField)}!`,
465
+ });
466
+ return zod_1.z.NEVER;
467
+ }
468
+ const type = keyField.kind === 'scalar' ? keyField.type : keyField.unbrandedTypeName;
469
+ if (type !== 'string') {
470
+ ctx.addIssue({
471
+ code: zod_1.z.ZodIssueCode.custom,
472
+ message: `Key field ${keyFieldName} in model ${model.name} must be of type \`string\`, but instead is of type ${type}!`,
473
+ });
474
+ return zod_1.z.NEVER;
475
+ }
476
+ return keyField;
477
+ }
478
+ function extractKeyFieldFromFallbacks(fields, idField) {
479
+ // Fallback order: key, excelKey, slug, id
480
+ // Only consider string scalar fields for the fallbacks (except id which is always string)
481
+ const keyField = fields.get(Field.standardFieldName_key);
482
+ if (keyField !== undefined && isStringScalarField(keyField)) {
483
+ return keyField;
484
+ }
485
+ const excelKeyField = fields.get(Field.standardFieldName_excelKey);
486
+ if (excelKeyField !== undefined && isStringScalarField(excelKeyField)) {
487
+ return excelKeyField;
488
+ }
489
+ const slugField = fields.get(Field.standardFieldName_slug);
490
+ if (slugField !== undefined && isStringScalarField(slugField)) {
491
+ return slugField;
492
+ }
493
+ // id field is always present and is always a string
494
+ return idField;
495
+ }
496
+ function isStringScalarField(field) {
497
+ return field.kind === 'scalar' && field.type === 'string';
498
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@postxl/schema",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "description": "Decoders for PXL Schema definitions and validation for PXL code generation framework",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -36,7 +36,7 @@
36
36
  "directory": "packages/schema"
37
37
  },
38
38
  "dependencies": {
39
- "@postxl/utils": "^1.3.0"
39
+ "@postxl/utils": "^1.3.1"
40
40
  },
41
41
  "devDependencies": {},
42
42
  "wallaby": {