@stonecrop/schema 0.16.3 → 0.16.5
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 +23 -9
- package/dist/cli.js +74 -53
- package/dist/cli.js.map +1 -1
- package/dist/index.js +38 -32
- package/dist/index.js.map +1 -1
- package/dist/schema.d.ts +143 -9
- package/dist/src/cli.js +49 -15
- package/dist/src/component-meta.d.ts +1 -1
- package/dist/src/component-meta.d.ts.map +1 -1
- package/dist/src/component-meta.js +1 -0
- package/dist/src/converter/index.d.ts +2 -0
- package/dist/src/converter/index.d.ts.map +1 -1
- package/dist/src/converter/index.js +30 -16
- package/dist/src/converter/merge.d.ts +87 -0
- package/dist/src/converter/merge.d.ts.map +1 -0
- package/dist/src/converter/merge.js +145 -0
- package/dist/src/converter/types.d.ts +17 -8
- package/dist/src/converter/types.d.ts.map +1 -1
- package/dist/src/field.d.ts +47 -0
- package/dist/src/field.d.ts.map +1 -1
- package/dist/src/field.js +67 -0
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -2
- package/dist/validation-CToUOy0a.js +538 -0
- package/dist/validation-CToUOy0a.js.map +1 -0
- package/package.json +1 -1
- package/dist/validation-Dyxjvaio.js +0 -458
- package/dist/validation-Dyxjvaio.js.map +0 -1
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Merge introspected schema facts into an already-authored doctype.
|
|
3
|
+
*
|
|
4
|
+
* The authored doctype is the source of truth. Generation **verifies** it and stamps provenance;
|
|
5
|
+
* it does not overwrite. That polarity is deliberate and load-bearing — a doctype legitimately
|
|
6
|
+
* declares a `primaryKey` the schema cannot express. A natural business key is very often a
|
|
7
|
+
* `UNIQUE` constraint rather than the table's `PRIMARY KEY`, and where a table carries several
|
|
8
|
+
* uniques no rule can pick between them. Overwriting identity from the schema would silently
|
|
9
|
+
* re-key such a doctype on every regeneration and break the handlers that key on the old value.
|
|
10
|
+
*
|
|
11
|
+
* So divergence is **reported, never applied** — a human decides. The only mutation this performs
|
|
12
|
+
* is adding `source: 'introspected'` to fields confirmed to exist in the GraphQL schema.
|
|
13
|
+
*
|
|
14
|
+
* @packageDocumentation
|
|
15
|
+
*/
|
|
16
|
+
import { INTROSPECTED_IDENTITY_PROPS } from '../field';
|
|
17
|
+
/** Recursively flatten authored fields, descending into fieldsets. */
|
|
18
|
+
function flattenAuthored(fields) {
|
|
19
|
+
const out = [];
|
|
20
|
+
for (const f of fields) {
|
|
21
|
+
if (Array.isArray(f.schema)) {
|
|
22
|
+
out.push(...flattenAuthored(f.schema.filter(isRecord)));
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
out.push(f);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
function isRecord(value) {
|
|
31
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
32
|
+
}
|
|
33
|
+
function describe(value) {
|
|
34
|
+
return value === undefined ? '—' : JSON.stringify(value);
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Verify an authored doctype against freshly generated output and stamp provenance.
|
|
38
|
+
*
|
|
39
|
+
* @param authored - the doctype as it exists on disk; every key not named below is preserved verbatim
|
|
40
|
+
* @param generated - `convertGraphQLSchema` output for the corresponding GraphQL type
|
|
41
|
+
* @returns the doctype to write, plus a drift report
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* const [generated] = convertGraphQLSchema(introspection, { include: ['Uom'] })
|
|
46
|
+
* const { doctype, drift } = mergeIntrospectedDoctype(JSON.parse(onDisk), generated)
|
|
47
|
+
* if (drift.identityDrift.length) console.warn(drift.identityDrift.join('\n'))
|
|
48
|
+
* ```
|
|
49
|
+
*
|
|
50
|
+
* @public
|
|
51
|
+
*/
|
|
52
|
+
export function mergeIntrospectedDoctype(authored, generated) {
|
|
53
|
+
const authoredFields = Array.isArray(authored.fields) ? authored.fields.filter(isRecord) : [];
|
|
54
|
+
const generatedByName = new Map(generated.fields.map(f => [f.fieldname, f]));
|
|
55
|
+
// Expanding links live in `links`, not `fields`, so a field naming one is modelled, not orphaned.
|
|
56
|
+
const generatedLinkNames = new Set(Object.keys(generated.links ?? {}));
|
|
57
|
+
const drift = {
|
|
58
|
+
doctype: typeof authored.name === 'string' ? authored.name : '(unnamed)',
|
|
59
|
+
mode: 'clean',
|
|
60
|
+
tagged: [],
|
|
61
|
+
orphan: [],
|
|
62
|
+
omitted: [],
|
|
63
|
+
componentDrift: [],
|
|
64
|
+
requiredDrift: [],
|
|
65
|
+
identityDrift: [],
|
|
66
|
+
};
|
|
67
|
+
const tag = (field) => {
|
|
68
|
+
// Containers have no column of their own; recurse and leave the container itself alone.
|
|
69
|
+
if (Array.isArray(field.schema)) {
|
|
70
|
+
return { ...field, schema: field.schema.filter(isRecord).map(tag) };
|
|
71
|
+
}
|
|
72
|
+
const name = typeof field.fieldname === 'string' ? field.fieldname : '';
|
|
73
|
+
const match = generatedByName.get(name);
|
|
74
|
+
if (!match) {
|
|
75
|
+
// A computed field declares up front that it has no backing column, so it is not a
|
|
76
|
+
// discrepancy. Everything else is worth surfacing — it may be an app component, or a
|
|
77
|
+
// column that has since been dropped.
|
|
78
|
+
if (field.computed !== true && !generatedLinkNames.has(name))
|
|
79
|
+
drift.orphan.push(name);
|
|
80
|
+
return field;
|
|
81
|
+
}
|
|
82
|
+
drift.tagged.push(name);
|
|
83
|
+
if (match.component !== field.component) {
|
|
84
|
+
drift.componentDrift.push(`${name}: authored=${describe(field.component)} schema=${describe(match.component)}`);
|
|
85
|
+
}
|
|
86
|
+
if (Boolean(match.required) !== Boolean(field.required)) {
|
|
87
|
+
drift.requiredDrift.push(`${name}: authored=${Boolean(field.required)} schema=${Boolean(match.required)}`);
|
|
88
|
+
}
|
|
89
|
+
for (const prop of INTROSPECTED_IDENTITY_PROPS) {
|
|
90
|
+
if (prop === 'fieldname' || prop === 'required')
|
|
91
|
+
continue;
|
|
92
|
+
const authoredValue = field[prop];
|
|
93
|
+
const schemaValue = match[prop];
|
|
94
|
+
// Absent on both sides is agreement, not drift — most fields set none of these.
|
|
95
|
+
if (authoredValue === undefined && schemaValue === undefined)
|
|
96
|
+
continue;
|
|
97
|
+
if (JSON.stringify(authoredValue) !== JSON.stringify(schemaValue)) {
|
|
98
|
+
drift.identityDrift.push(`${name}.${prop}: authored=${describe(authoredValue)} schema=${describe(schemaValue)}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return { ...field, source: 'introspected' };
|
|
102
|
+
};
|
|
103
|
+
const merged = { ...authored, fields: authoredFields.map(tag) };
|
|
104
|
+
const authoredNames = new Set(flattenAuthored(authoredFields).map(f => f.fieldname));
|
|
105
|
+
drift.omitted = generated.fields.map(f => f.fieldname).filter(n => !authoredNames.has(n));
|
|
106
|
+
// Classify identity last, once every field has been compared.
|
|
107
|
+
const authoredPk = flattenAuthored(authoredFields).find(f => f.primaryKey === true);
|
|
108
|
+
const generatedPk = generated.fields.find(f => f.primaryKey === true);
|
|
109
|
+
if (authoredPk && generatedPk && authoredPk.fieldname !== generatedPk.fieldname) {
|
|
110
|
+
drift.mode = 'partial';
|
|
111
|
+
drift.reason = `authored primary key '${String(authoredPk.fieldname)}' is not the derivable '${generatedPk.fieldname}' — left as authored`;
|
|
112
|
+
}
|
|
113
|
+
else if (authoredPk && !generatedPk) {
|
|
114
|
+
drift.mode = 'partial';
|
|
115
|
+
drift.reason = `authored primary key '${String(authoredPk.fieldname)}' is not derivable from the schema — left as authored`;
|
|
116
|
+
}
|
|
117
|
+
else if (!authoredPk && generatedPk) {
|
|
118
|
+
drift.mode = 'partial';
|
|
119
|
+
drift.reason = `schema suggests '${generatedPk.fieldname}' as primary key but the doctype declares none — not applied`;
|
|
120
|
+
}
|
|
121
|
+
return { doctype: merged, drift };
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Render a drift report as human-readable lines. Empty when generation agrees with the doctype.
|
|
125
|
+
*
|
|
126
|
+
* @param drift - a report from {@link mergeIntrospectedDoctype}
|
|
127
|
+
* @returns one line per finding, ready to print
|
|
128
|
+
*
|
|
129
|
+
* @public
|
|
130
|
+
*/
|
|
131
|
+
export function formatDoctypeDrift(drift) {
|
|
132
|
+
const lines = [];
|
|
133
|
+
if (drift.reason)
|
|
134
|
+
lines.push(` ${drift.doctype}: ${drift.reason}`);
|
|
135
|
+
const bucket = (label, entries) => {
|
|
136
|
+
if (entries.length)
|
|
137
|
+
lines.push(` ${drift.doctype}: ${label} ${entries.join('; ')}`);
|
|
138
|
+
};
|
|
139
|
+
bucket('identity drift', drift.identityDrift);
|
|
140
|
+
bucket('component drift', drift.componentDrift);
|
|
141
|
+
bucket('required drift', drift.requiredDrift);
|
|
142
|
+
bucket('authored fields with no schema field:', drift.orphan);
|
|
143
|
+
bucket('schema fields not modelled:', drift.omitted);
|
|
144
|
+
return lines;
|
|
145
|
+
}
|
|
@@ -50,19 +50,28 @@ export interface GraphQLConversionOptions {
|
|
|
50
50
|
*/
|
|
51
51
|
include?: string[];
|
|
52
52
|
/**
|
|
53
|
-
*
|
|
54
|
-
*
|
|
53
|
+
* Emit a doctype under a different name than its GraphQL type. Key is the GraphQL type name,
|
|
54
|
+
* value is the doctype `name`; `slug` is derived from the value.
|
|
55
|
+
*
|
|
56
|
+
* This exists for the case where a doctype is not one-to-one with a table — a second view over
|
|
57
|
+
* an existing type, say, distinguished only by presentation. Without it the converter can only
|
|
58
|
+
* ever name a doctype after its type.
|
|
59
|
+
*
|
|
60
|
+
* Keep it consistent with the middleware's `tables` option, which maps the resulting doctype
|
|
61
|
+
* name to its SQL target.
|
|
55
62
|
*
|
|
56
63
|
* @example
|
|
57
64
|
* ```typescript
|
|
58
|
-
* {
|
|
59
|
-
* SalesOrder: {
|
|
60
|
-
* totalAmount: { component: 'ANumericInput', align: 'right' }
|
|
61
|
-
* }
|
|
62
|
-
* }
|
|
65
|
+
* { Plan: 'Planner' } // emits a doctype named Planner, slug 'planner', from type Plan
|
|
63
66
|
* ```
|
|
64
67
|
*/
|
|
65
|
-
|
|
68
|
+
doctypeNames?: Record<string, string>;
|
|
69
|
+
/**
|
|
70
|
+
* Called with any advisory message raised during conversion — currently only the
|
|
71
|
+
* un-normalized-PostGraphile warning. Left to the caller so the library never writes to the
|
|
72
|
+
* console itself.
|
|
73
|
+
*/
|
|
74
|
+
onWarning?: (message: string) => void;
|
|
66
75
|
/**
|
|
67
76
|
* Map custom or non-standard GraphQL scalar types to the component that renders them.
|
|
68
77
|
* Merged with the built-in scalar maps (GQL_SCALAR_MAP + WELL_KNOWN_SCALARS).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/converter/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AACjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAE9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAE1C;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC7B,yFAAyF;IACzF,SAAS,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,mBAAmB,GAAG,kBAAkB,GAAG,MAAM,CAAA;AAE7D;;;;;GAKG;AACH,MAAM,WAAW,wBAAwB;IACxC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAElB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAElB
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/converter/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,SAAS,CAAA;AACjD,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAE9D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAE1C;;;;;;;GAOG;AACH,MAAM,WAAW,aAAa;IAC7B,yFAAyF;IACzF,SAAS,EAAE,MAAM,CAAA;CACjB;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,mBAAmB,GAAG,kBAAkB,GAAG,MAAM,CAAA;AAE7D;;;;;GAKG;AACH,MAAM,WAAW,wBAAwB;IACxC;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAElB;;;OAGG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAElB;;;;;;;;;;;;;;;OAeG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAErC;;;;OAIG;IACH,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;IAErC;;;;;;;;;;;;OAYG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC,CAAA;IAEtD;;;;;;;;;;;OAWG;IACH,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,KAAK,OAAO,CAAA;IAErE;;;;;;;;;;OAUG;IACH,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,UAAU,EAAE,iBAAiB,KAAK,OAAO,CAAA;IAEpH;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,CACf,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,YAAY,CAAC,OAAO,EAAE,OAAO,CAAC,EACrC,UAAU,EAAE,iBAAiB,KACzB,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC,GAAG,IAAI,CAAA;IAE7C;;;OAGG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;CAC7B;AAED;;;;;GAKG;AACH,MAAM,WAAW,0BAA2B,SAAQ,UAAU;IAC7D,2DAA2D;IAC3D,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,yDAAyD;IACzD,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,qEAAqE;IACrE,OAAO,CAAC,EAAE,OAAO,CAAA;CACjB;AAED;;;;GAIG;AACH,MAAM,WAAW,uBAAwB,SAAQ,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC;IAC3E,iGAAiG;IACjG,MAAM,EAAE,UAAU,EAAE,CAAA;IACpB,2DAA2D;IAC3D,gBAAgB,CAAC,EAAE,MAAM,CAAA;CACzB;AAGD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA"}
|
package/dist/src/field.d.ts
CHANGED
|
@@ -171,6 +171,53 @@ export type DoctypeField = ValueField | FieldsetField | TableField;
|
|
|
171
171
|
* @public
|
|
172
172
|
*/
|
|
173
173
|
export declare function normalizeFieldKind(field: unknown): unknown;
|
|
174
|
+
/**
|
|
175
|
+
* The field properties a `source: 'introspected'` marker freezes — the ones the database owns.
|
|
176
|
+
*
|
|
177
|
+
* This is the single definition of the identity set. The docbuilder greys these inputs on an
|
|
178
|
+
* introspected field, and the converter's merge refuses to rewrite them. Stating it twice is how
|
|
179
|
+
* the two drift, so both read this constant.
|
|
180
|
+
*
|
|
181
|
+
* Everything absent from this list is author-owned, `component` most importantly: it chooses the
|
|
182
|
+
* widget, which is an authoring decision the database has no opinion about.
|
|
183
|
+
*
|
|
184
|
+
* @public
|
|
185
|
+
*/
|
|
186
|
+
export declare const INTROSPECTED_IDENTITY_PROPS: readonly ["fieldname", "primaryKey", "required", "options", "cardinality", "doctype"];
|
|
187
|
+
/**
|
|
188
|
+
* Find the field a doctype marks as its primary key, or `undefined` when none is marked.
|
|
189
|
+
*
|
|
190
|
+
* This is the single definition of "which field identifies a record". Both sides depend on it:
|
|
191
|
+
* the middleware builds the SQL identity predicate from it, and the client resolves a record's
|
|
192
|
+
* route/store key from it. Call this; never re-derive the rule at the call site, or the two will
|
|
193
|
+
* drift and the client will key records by a column the server never queried.
|
|
194
|
+
*
|
|
195
|
+
* Two deliberate limits, both matching the shape `primaryKey` actually has:
|
|
196
|
+
* - Only **top-level** fields are scanned. `primaryKey` is a `ValueField` flag and a fieldset's
|
|
197
|
+
* children are not identity columns, so a nested match would be an authoring error, not a PK.
|
|
198
|
+
* - The **first** match wins. Nothing in the schema enforces exactly one `primaryKey: true`, and
|
|
199
|
+
* there is no composite-key representation — a doctype with several is already malformed, and
|
|
200
|
+
* picking the first is what the middleware has always done.
|
|
201
|
+
*
|
|
202
|
+
* @param fields - the doctype's top-level fields
|
|
203
|
+
* @returns the primary-key field, or `undefined` for a PK-less doctype
|
|
204
|
+
* @public
|
|
205
|
+
*/
|
|
206
|
+
export declare function getPrimaryKeyField(fields: readonly DoctypeField[]): ValueField | undefined;
|
|
207
|
+
/**
|
|
208
|
+
* Resolve a record's identity value using the doctype's declared primary key.
|
|
209
|
+
*
|
|
210
|
+
* Falls back to `record.id` when the doctype declares no `primaryKey`. That fallback is
|
|
211
|
+
* load-bearing, not defensive: surrogate-key doctypes carry an `id` column and never mark a
|
|
212
|
+
* primary key, and PostGraphile renames a single-column `id` PK to `rowId` — so the declared
|
|
213
|
+
* field and `id` are both real sources, in that order.
|
|
214
|
+
*
|
|
215
|
+
* @param fields - the doctype's top-level fields
|
|
216
|
+
* @param record - the record to read the identity from
|
|
217
|
+
* @returns the identity as a string, or `undefined` when neither source yields a usable value
|
|
218
|
+
* @public
|
|
219
|
+
*/
|
|
220
|
+
export declare function getRecordIdentity(fields: readonly DoctypeField[], record: Record<string, unknown>): string | undefined;
|
|
174
221
|
/**
|
|
175
222
|
* Zod runtime validation schema for ValueField.
|
|
176
223
|
* @public
|
package/dist/src/field.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../../src/field.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"field.d.ts","sourceRoot":"","sources":["../../src/field.ts"],"names":[],"mappings":"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"}
|
package/dist/src/field.js
CHANGED
|
@@ -96,6 +96,73 @@ export function normalizeFieldKind(field) {
|
|
|
96
96
|
}
|
|
97
97
|
return injected;
|
|
98
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* The field properties a `source: 'introspected'` marker freezes — the ones the database owns.
|
|
101
|
+
*
|
|
102
|
+
* This is the single definition of the identity set. The docbuilder greys these inputs on an
|
|
103
|
+
* introspected field, and the converter's merge refuses to rewrite them. Stating it twice is how
|
|
104
|
+
* the two drift, so both read this constant.
|
|
105
|
+
*
|
|
106
|
+
* Everything absent from this list is author-owned, `component` most importantly: it chooses the
|
|
107
|
+
* widget, which is an authoring decision the database has no opinion about.
|
|
108
|
+
*
|
|
109
|
+
* @public
|
|
110
|
+
*/
|
|
111
|
+
export const INTROSPECTED_IDENTITY_PROPS = [
|
|
112
|
+
'fieldname',
|
|
113
|
+
'primaryKey',
|
|
114
|
+
'required',
|
|
115
|
+
'options',
|
|
116
|
+
'cardinality',
|
|
117
|
+
'doctype',
|
|
118
|
+
];
|
|
119
|
+
/**
|
|
120
|
+
* Find the field a doctype marks as its primary key, or `undefined` when none is marked.
|
|
121
|
+
*
|
|
122
|
+
* This is the single definition of "which field identifies a record". Both sides depend on it:
|
|
123
|
+
* the middleware builds the SQL identity predicate from it, and the client resolves a record's
|
|
124
|
+
* route/store key from it. Call this; never re-derive the rule at the call site, or the two will
|
|
125
|
+
* drift and the client will key records by a column the server never queried.
|
|
126
|
+
*
|
|
127
|
+
* Two deliberate limits, both matching the shape `primaryKey` actually has:
|
|
128
|
+
* - Only **top-level** fields are scanned. `primaryKey` is a `ValueField` flag and a fieldset's
|
|
129
|
+
* children are not identity columns, so a nested match would be an authoring error, not a PK.
|
|
130
|
+
* - The **first** match wins. Nothing in the schema enforces exactly one `primaryKey: true`, and
|
|
131
|
+
* there is no composite-key representation — a doctype with several is already malformed, and
|
|
132
|
+
* picking the first is what the middleware has always done.
|
|
133
|
+
*
|
|
134
|
+
* @param fields - the doctype's top-level fields
|
|
135
|
+
* @returns the primary-key field, or `undefined` for a PK-less doctype
|
|
136
|
+
* @public
|
|
137
|
+
*/
|
|
138
|
+
export function getPrimaryKeyField(fields) {
|
|
139
|
+
return fields.find((f) => f.kind === 'field' && Boolean(f.primaryKey));
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Resolve a record's identity value using the doctype's declared primary key.
|
|
143
|
+
*
|
|
144
|
+
* Falls back to `record.id` when the doctype declares no `primaryKey`. That fallback is
|
|
145
|
+
* load-bearing, not defensive: surrogate-key doctypes carry an `id` column and never mark a
|
|
146
|
+
* primary key, and PostGraphile renames a single-column `id` PK to `rowId` — so the declared
|
|
147
|
+
* field and `id` are both real sources, in that order.
|
|
148
|
+
*
|
|
149
|
+
* @param fields - the doctype's top-level fields
|
|
150
|
+
* @param record - the record to read the identity from
|
|
151
|
+
* @returns the identity as a string, or `undefined` when neither source yields a usable value
|
|
152
|
+
* @public
|
|
153
|
+
*/
|
|
154
|
+
export function getRecordIdentity(fields, record) {
|
|
155
|
+
const pkField = getPrimaryKeyField(fields);
|
|
156
|
+
const candidates = pkField ? [record[pkField.fieldname], record.id] : [record.id];
|
|
157
|
+
for (const value of candidates) {
|
|
158
|
+
// Numbers are valid keys (a serial PK); 0 is a legitimate id, so test the type, not truthiness.
|
|
159
|
+
if (typeof value === 'number')
|
|
160
|
+
return String(value);
|
|
161
|
+
if (typeof value === 'string' && value !== '')
|
|
162
|
+
return value;
|
|
163
|
+
}
|
|
164
|
+
return undefined;
|
|
165
|
+
}
|
|
99
166
|
function createDoctypeFieldSchemas() {
|
|
100
167
|
const ValueFieldSchema = z
|
|
101
168
|
.object({
|
package/dist/src/index.d.ts
CHANGED
|
@@ -2,11 +2,11 @@ export type { InteractionMode } from './mode';
|
|
|
2
2
|
export { TableViewConfig } from './table';
|
|
3
3
|
export { CANONICAL_COMPONENTS, COMPONENT_CATEGORY, COMPONENT_LINK_EXPANSION, componentCategory, componentLinkExpansion, resolveLinkRenderMode, type ComponentCategory, type LinkExpansion, type LinkRenderMode, } from './component-meta';
|
|
4
4
|
export type { DoctypeField, FieldOptions, FieldValidation, FieldsetField, TableField, ValueField } from './field';
|
|
5
|
-
export { DoctypeFieldSchema, FieldsetFieldSchema, normalizeFieldKind, TableFieldSchema, ValueFieldSchema, } from './field';
|
|
5
|
+
export { DoctypeFieldSchema, FieldsetFieldSchema, getPrimaryKeyField, getRecordIdentity, INTROSPECTED_IDENTITY_PROPS, normalizeFieldKind, TableFieldSchema, ValueFieldSchema, } from './field';
|
|
6
6
|
export { ActionDefinition, TriggerDefinition, WorkflowLayout, WorkflowMeta, isActionAllowedInState } from './doctype';
|
|
7
7
|
export type { Cardinality, CustomFetch, DataClient, DoctypeContext, DoctypeMeta, DoctypeRef, FetchStrategy, GetRecordOptions, GetRecordResult, GetRecordsOptions, LazyFetch, LinkDeclaration, SerializedFunction, SyncFetch, } from './doctype';
|
|
8
8
|
export { parseDoctype, parseField, validateDoctype, validateField, type ValidationError, type ValidationResult, } from './validation';
|
|
9
|
-
export { buildScalarMap, classifyFieldType, convertGraphQLSchema, defaultIsEntityField, defaultIsEntityType, GQL_SCALAR_MAP, INTERNAL_SCALARS, WELL_KNOWN_SCALARS, type ConvertedGraphQLDoctype, type GraphQLConversionFieldMeta, type GraphQLConversionOptions, type IntrospectionSource, } from './converter';
|
|
9
|
+
export { buildScalarMap, classifyFieldType, convertGraphQLSchema, defaultIsEntityField, defaultIsEntityType, formatDoctypeDrift, GQL_SCALAR_MAP, INTERNAL_SCALARS, mergeIntrospectedDoctype, WELL_KNOWN_SCALARS, type AuthoredDoctype, type ConvertedGraphQLDoctype, type DoctypeDrift, type GraphQLConversionFieldMeta, type GraphQLConversionOptions, type IntrospectionSource, type MergeResult, } from './converter';
|
|
10
10
|
export { toSlug, toPascalCase, pascalToSnake, snakeToCamel, camelToSnake, snakeToLabel, camelToLabel } from './naming';
|
|
11
11
|
export type { ColumnSchema } from './column-schema';
|
|
12
12
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAA;AAG7C,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAGzC,OAAO,EACN,oBAAoB,EACpB,kBAAkB,EAClB,wBAAwB,EACxB,iBAAiB,EACjB,sBAAsB,EACtB,qBAAqB,EACrB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,cAAc,GACnB,MAAM,kBAAkB,CAAA;AAGzB,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACjH,OAAO,EACN,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,GAChB,MAAM,SAAS,CAAA;AAKhB,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,cAAc,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAA;AACrH,YAAY,EACX,WAAW,EACX,WAAW,EACX,UAAU,EACV,cAAc,EACd,WAAW,EACX,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,SAAS,EACT,eAAe,EACf,kBAAkB,EAClB,SAAS,GACT,MAAM,WAAW,CAAA;AAGlB,OAAO,EACN,YAAY,EACZ,UAAU,EACV,eAAe,EACf,aAAa,EACb,KAAK,eAAe,EACpB,KAAK,gBAAgB,GACrB,MAAM,cAAc,CAAA;AAGrB,OAAO,EACN,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACd,gBAAgB,EAChB,kBAAkB,EAClB,KAAK,uBAAuB,EAC5B,KAAK,0BAA0B,EAC/B,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,eAAe,EAAE,MAAM,QAAQ,CAAA;AAG7C,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AAGzC,OAAO,EACN,oBAAoB,EACpB,kBAAkB,EAClB,wBAAwB,EACxB,iBAAiB,EACjB,sBAAsB,EACtB,qBAAqB,EACrB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,cAAc,GACnB,MAAM,kBAAkB,CAAA;AAGzB,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,aAAa,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACjH,OAAO,EACN,kBAAkB,EAClB,mBAAmB,EACnB,kBAAkB,EAClB,iBAAiB,EACjB,2BAA2B,EAC3B,kBAAkB,EAClB,gBAAgB,EAChB,gBAAgB,GAChB,MAAM,SAAS,CAAA;AAKhB,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,cAAc,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,WAAW,CAAA;AACrH,YAAY,EACX,WAAW,EACX,WAAW,EACX,UAAU,EACV,cAAc,EACd,WAAW,EACX,UAAU,EACV,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,iBAAiB,EACjB,SAAS,EACT,eAAe,EACf,kBAAkB,EAClB,SAAS,GACT,MAAM,WAAW,CAAA;AAGlB,OAAO,EACN,YAAY,EACZ,UAAU,EACV,eAAe,EACf,aAAa,EACb,KAAK,eAAe,EACpB,KAAK,gBAAgB,GACrB,MAAM,cAAc,CAAA;AAGrB,OAAO,EACN,cAAc,EACd,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EAClB,cAAc,EACd,gBAAgB,EAChB,wBAAwB,EACxB,kBAAkB,EAClB,KAAK,eAAe,EACpB,KAAK,uBAAuB,EAC5B,KAAK,YAAY,EACjB,KAAK,0BAA0B,EAC/B,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,EACxB,KAAK,WAAW,GAChB,MAAM,aAAa,CAAA;AAGpB,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAGtH,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA"}
|
package/dist/src/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
export { TableViewConfig } from './table';
|
|
3
3
|
// Component → semantic category — the single source of "what kind of value does this component render"
|
|
4
4
|
export { CANONICAL_COMPONENTS, COMPONENT_CATEGORY, COMPONENT_LINK_EXPANSION, componentCategory, componentLinkExpansion, resolveLinkRenderMode, } from './component-meta';
|
|
5
|
-
export { DoctypeFieldSchema, FieldsetFieldSchema, normalizeFieldKind, TableFieldSchema, ValueFieldSchema, } from './field';
|
|
5
|
+
export { DoctypeFieldSchema, FieldsetFieldSchema, getPrimaryKeyField, getRecordIdentity, INTROSPECTED_IDENTITY_PROPS, normalizeFieldKind, TableFieldSchema, ValueFieldSchema, } from './field';
|
|
6
6
|
// Doctype schema
|
|
7
7
|
// ActionDefinition and WorkflowMeta are exported as values (Zod schemas) so consumers can use
|
|
8
8
|
// .safeParse(), .shape, etc. at runtime. TypeScript types are inferred from the same exports.
|
|
@@ -10,6 +10,6 @@ export { ActionDefinition, TriggerDefinition, WorkflowLayout, WorkflowMeta, isAc
|
|
|
10
10
|
// Validation helpers
|
|
11
11
|
export { parseDoctype, parseField, validateDoctype, validateField, } from './validation';
|
|
12
12
|
// GraphQL to Doctype conversion
|
|
13
|
-
export { buildScalarMap, classifyFieldType, convertGraphQLSchema, defaultIsEntityField, defaultIsEntityType, GQL_SCALAR_MAP, INTERNAL_SCALARS, WELL_KNOWN_SCALARS, } from './converter';
|
|
13
|
+
export { buildScalarMap, classifyFieldType, convertGraphQLSchema, defaultIsEntityField, defaultIsEntityType, formatDoctypeDrift, GQL_SCALAR_MAP, INTERNAL_SCALARS, mergeIntrospectedDoctype, WELL_KNOWN_SCALARS, } from './converter';
|
|
14
14
|
// Naming utilities
|
|
15
15
|
export { toSlug, toPascalCase, pascalToSnake, snakeToCamel, camelToSnake, snakeToLabel, camelToLabel } from './naming';
|