@coffer-org/sdk 3.3.0 → 3.5.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.
- package/dist/derive.js +2 -0
- package/dist/extend.js +2 -1
- package/dist/fields/normalize.d.ts +1 -0
- package/dist/fields.d.ts +5 -17
- package/dist/fields.js +18 -26
- package/dist/parts.d.ts +6 -1
- package/dist/parts.js +17 -3
- package/dist/shelf.d.ts +2 -0
- package/dist/shelf.js +43 -8
- package/package.json +1 -1
package/dist/derive.js
CHANGED
|
@@ -12,6 +12,8 @@ function topLevelFields(fields) {
|
|
|
12
12
|
export function derivedEntries(fields) {
|
|
13
13
|
const out = [];
|
|
14
14
|
for (const [key, meta] of topLevelFields(fields)) {
|
|
15
|
+
if (meta.virtual)
|
|
16
|
+
continue;
|
|
15
17
|
const fn = meta.compute ?? meta.derive?.fn;
|
|
16
18
|
if (fn)
|
|
17
19
|
out.push([key, typeof fn === 'function' ? fn : () => fn]);
|
package/dist/extend.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { layoutToClient, fieldEntries, buildShape } from "./shelf.js";
|
|
1
|
+
import { layoutToClient, fieldEntries, buildShape, checkFieldDeclaration } from "./shelf.js";
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
export function defineExtend(e) {
|
|
4
4
|
const keys = fieldEntries(e.fields).map(([k]) => k);
|
|
5
5
|
const dup = keys.find((k, i) => keys.indexOf(k) !== i);
|
|
6
6
|
if (dup)
|
|
7
7
|
throw new Error(`[extend] ${e.id}: duplicate key '${dup}'`);
|
|
8
|
+
checkFieldDeclaration(e.fields, `[extend] ${e.id}`);
|
|
8
9
|
return e;
|
|
9
10
|
}
|
|
10
11
|
export function extendMatches(e, library, shelf) {
|
package/dist/fields.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export type { Rules, View, FieldConfig } from './fields/normalize.ts';
|
|
|
8
8
|
export { normalizeOpts } from './fields/normalize.ts';
|
|
9
9
|
import { type Rules, type View } from './fields/normalize.ts';
|
|
10
10
|
import { presets } from './field-presets.ts';
|
|
11
|
-
export type CorePrim = 'text' | 'number' | 'date' | 'time' | 'datetime' | 'checkbox' | 'select' | 'json' | 'check' | 'measured' | 'relation' | 'file' | 'per-weekday'
|
|
11
|
+
export type CorePrim = 'text' | 'number' | 'date' | 'time' | 'datetime' | 'checkbox' | 'select' | 'json' | 'check' | 'measured' | 'relation' | 'file' | 'per-weekday';
|
|
12
12
|
export type Prim = CorePrim | (string & {});
|
|
13
13
|
export type ColumnType = 'text' | 'integer' | 'real' | 'boolean' | 'date' | 'time' | 'datetime';
|
|
14
14
|
export type OptionIcon = {
|
|
@@ -48,6 +48,8 @@ export interface ComputeCtx {
|
|
|
48
48
|
shelf: string;
|
|
49
49
|
};
|
|
50
50
|
self?: Record<string, unknown>;
|
|
51
|
+
parent?: Record<string, unknown>;
|
|
52
|
+
ref?: (key: string) => Promise<Record<string, unknown> | null>;
|
|
51
53
|
}
|
|
52
54
|
export type ComputeFn = (ctx: ComputeCtx) => unknown;
|
|
53
55
|
export interface DividerEl {
|
|
@@ -184,6 +186,7 @@ export type FieldOptions = string | unknown[];
|
|
|
184
186
|
export interface FieldCoreOpts {
|
|
185
187
|
key?: string;
|
|
186
188
|
value?: unknown;
|
|
189
|
+
store?: boolean;
|
|
187
190
|
label?: string;
|
|
188
191
|
rules?: Rules;
|
|
189
192
|
view?: View;
|
|
@@ -204,6 +207,7 @@ export declare function wrapKey(opts: {
|
|
|
204
207
|
emphasis?: 'hero' | 'muted';
|
|
205
208
|
noLabel?: boolean;
|
|
206
209
|
role?: 'cover' | 'avatar';
|
|
210
|
+
store?: boolean;
|
|
207
211
|
derive?: import('./derive.ts').DeriveSpec;
|
|
208
212
|
}, meta: FieldMeta): FieldItem | StaticEl | FieldMeta;
|
|
209
213
|
export interface FieldMeta extends FieldClient {
|
|
@@ -522,21 +526,6 @@ export declare function perWeekday(o: {
|
|
|
522
526
|
export type RowSubField = FieldClient & {
|
|
523
527
|
key: string;
|
|
524
528
|
};
|
|
525
|
-
export interface LookupOpts extends FieldCoreOpts {
|
|
526
|
-
from: string;
|
|
527
|
-
pick: string[];
|
|
528
|
-
}
|
|
529
|
-
export declare function lookup(o: LookupOpts & {
|
|
530
|
-
key: string;
|
|
531
|
-
}): FieldItem;
|
|
532
|
-
export declare function lookup(o: LookupOpts & {
|
|
533
|
-
value: unknown;
|
|
534
|
-
key?: undefined;
|
|
535
|
-
}): StaticEl;
|
|
536
|
-
export declare function lookup(o: LookupOpts & {
|
|
537
|
-
key?: undefined;
|
|
538
|
-
value?: undefined;
|
|
539
|
-
}): FieldMeta;
|
|
540
529
|
export declare function clientToMeta(fc: FieldClient): FieldMeta;
|
|
541
530
|
interface PeriodOpts extends FieldCoreOpts {
|
|
542
531
|
parts?: PartsSpec;
|
|
@@ -723,7 +712,6 @@ declare const PRIMITIVES: {
|
|
|
723
712
|
money: typeof money;
|
|
724
713
|
code: typeof code;
|
|
725
714
|
geo: typeof geo;
|
|
726
|
-
lookup: typeof lookup;
|
|
727
715
|
perWeekday: typeof perWeekday;
|
|
728
716
|
period: typeof period;
|
|
729
717
|
file: typeof file;
|
package/dist/fields.js
CHANGED
|
@@ -134,8 +134,18 @@ export function wrapKey(opts, meta) {
|
|
|
134
134
|
`"the server computes this field"; use value, the canonical spelling`);
|
|
135
135
|
if (opts.derive)
|
|
136
136
|
m = { ...m, derive: opts.derive, derived: true, hints: { ...m.hints, noEditControl: true } };
|
|
137
|
-
|
|
137
|
+
const computed = opts.key && 'value' in opts && opts.value !== undefined;
|
|
138
|
+
if (opts.derive && opts.store === false)
|
|
139
|
+
throw new Error(`[wrapKey] '${opts.key}': derive writes a column on every write, so store: false contradicts it; ` +
|
|
140
|
+
`spell the computation as \`value\` instead.`);
|
|
141
|
+
if (opts.store === false && !computed)
|
|
142
|
+
throw new Error(`[wrapKey] '${opts.key ?? '(keyless)'}': store: false needs key + value — it drops the COLUMN of a ` +
|
|
143
|
+
`computed field, so that the server recomputes it on every read. A field without \`value\` has ` +
|
|
144
|
+
`nothing to recompute; a field without \`key\` already stores nothing.`);
|
|
145
|
+
if (computed) {
|
|
138
146
|
m = { ...m, compute: opts.value, derived: true, hints: { ...m.hints, noEditControl: true } };
|
|
147
|
+
if (opts.store === false)
|
|
148
|
+
m = { ...m, virtual: true };
|
|
139
149
|
return { key: opts.key, type: m };
|
|
140
150
|
}
|
|
141
151
|
if (opts.key)
|
|
@@ -625,7 +635,7 @@ export function measured(raw) {
|
|
|
625
635
|
const rowSchema = z.object(partsRowShape({
|
|
626
636
|
value: numSchema,
|
|
627
637
|
unit: z.string().refine((v) => units.some((u) => u.value === v), { message: vmsg('measured_unit') }),
|
|
628
|
-
}, parts));
|
|
638
|
+
}, parts, required !== true));
|
|
629
639
|
const s = z.unknown().transform((raw, ctx) => {
|
|
630
640
|
const parsed = jsonValue(raw);
|
|
631
641
|
if (typeof parsed === 'string') {
|
|
@@ -672,9 +682,7 @@ export function unit(raw) {
|
|
|
672
682
|
const o = normalizeOpts(raw);
|
|
673
683
|
const required = o.required ?? false;
|
|
674
684
|
const units = resolveUnits(o.options);
|
|
675
|
-
const s = z
|
|
676
|
-
.string(reqErr())
|
|
677
|
-
.refine((v) => units.some((u) => u.value === v), { message: vmsg('measured_unit') });
|
|
685
|
+
const s = z.string(reqErr()).refine((v) => units.some((u) => u.value === v), { message: vmsg('measured_unit') });
|
|
678
686
|
const meta = {
|
|
679
687
|
kind: 'unit',
|
|
680
688
|
label: o.label ?? '',
|
|
@@ -724,7 +732,7 @@ export function money(raw) {
|
|
|
724
732
|
const rowSchema = z.object(partsRowShape({
|
|
725
733
|
value: numSchema,
|
|
726
734
|
currency: z.string().refine(isCurrencyCode, { message: vmsg('money_currency') }),
|
|
727
|
-
}, parts));
|
|
735
|
+
}, parts, required !== true));
|
|
728
736
|
const s = z.unknown().transform((raw, ctx) => {
|
|
729
737
|
const parsed = jsonValue(raw);
|
|
730
738
|
if (typeof parsed === 'string') {
|
|
@@ -764,7 +772,7 @@ export function code(raw) {
|
|
|
764
772
|
const required = o.required ?? false;
|
|
765
773
|
const cfg = o.config ?? {};
|
|
766
774
|
const parts = resolveParts(CODE_ROLES, o.parts);
|
|
767
|
-
const rowSchema = z.object(partsRowShape({ code: z.string(), lang: z.string() }, parts));
|
|
775
|
+
const rowSchema = z.object(partsRowShape({ code: z.string(), lang: z.string() }, parts, required !== true));
|
|
768
776
|
const s = z.unknown().transform((raw, ctx) => {
|
|
769
777
|
const parsed = jsonValue(raw);
|
|
770
778
|
if (typeof parsed === 'string') {
|
|
@@ -804,15 +812,14 @@ export function geo(raw) {
|
|
|
804
812
|
lat: z.number().min(-90).max(90),
|
|
805
813
|
lng: z.number().min(-180).max(180),
|
|
806
814
|
label: z.string().nullish(),
|
|
807
|
-
}, parts));
|
|
815
|
+
}, parts, required !== true));
|
|
808
816
|
const clientCoordKeys = parts
|
|
809
817
|
.filter((p) => (p.role === 'lat' || p.role === 'lng') && p.mode === 'stored')
|
|
810
818
|
.map(partValueKey);
|
|
811
819
|
const s = z.unknown().transform((raw, ctx) => {
|
|
812
820
|
const parsed = jsonValue(raw);
|
|
813
821
|
const p = parsed;
|
|
814
|
-
const noCoords = p == null ||
|
|
815
|
-
(typeof p === 'object' && clientCoordKeys.length > 0 && clientCoordKeys.every((k) => p[k] == null));
|
|
822
|
+
const noCoords = p == null || (typeof p === 'object' && clientCoordKeys.length > 0 && clientCoordKeys.every((k) => p[k] == null));
|
|
816
823
|
if (noCoords) {
|
|
817
824
|
if (required)
|
|
818
825
|
ctx.addIssue({ code: z.ZodIssueCode.custom, message: vmsg('required') });
|
|
@@ -854,20 +861,6 @@ export function perWeekday(o) {
|
|
|
854
861
|
kind: 'per-weekday',
|
|
855
862
|
};
|
|
856
863
|
}
|
|
857
|
-
export function lookup(raw) {
|
|
858
|
-
const o = normalizeOpts(raw);
|
|
859
|
-
const meta = {
|
|
860
|
-
kind: 'lookup',
|
|
861
|
-
label: o.label ?? '',
|
|
862
|
-
required: false,
|
|
863
|
-
prim: 'lookup',
|
|
864
|
-
column: 'text',
|
|
865
|
-
virtual: true,
|
|
866
|
-
hints: { from: o.from, pick: o.pick, compareWith: o.compareWith ?? null },
|
|
867
|
-
zod: z.any().optional(),
|
|
868
|
-
};
|
|
869
|
-
return wrapKey(o, meta);
|
|
870
|
-
}
|
|
871
864
|
export function clientToMeta(fc) {
|
|
872
865
|
const { parts: _parts, ...rest } = fc;
|
|
873
866
|
return { ...rest, column: 'text', zod: z.any() };
|
|
@@ -1066,7 +1059,7 @@ function makeRange(kind, isInt, raw) {
|
|
|
1066
1059
|
n = n.max(cfg.max);
|
|
1067
1060
|
const parts = resolveParts(RANGE_ROLES(isInt), o.parts);
|
|
1068
1061
|
const rangeKeys = partValueKeys(parts);
|
|
1069
|
-
const rowSchema = z.object(partsRowShape({ from: n, to: n }, parts));
|
|
1062
|
+
const rowSchema = z.object(partsRowShape({ from: n, to: n }, parts, required !== true));
|
|
1070
1063
|
const endpoint = endpointReader(parts, rangeKeys);
|
|
1071
1064
|
const s = z.unknown().transform((raw, ctx) => {
|
|
1072
1065
|
if (typeof raw === 'string') {
|
|
@@ -1165,7 +1158,6 @@ const PRIMITIVES = {
|
|
|
1165
1158
|
money,
|
|
1166
1159
|
code,
|
|
1167
1160
|
geo,
|
|
1168
|
-
lookup,
|
|
1169
1161
|
perWeekday,
|
|
1170
1162
|
period,
|
|
1171
1163
|
file,
|
package/dist/parts.d.ts
CHANGED
|
@@ -24,5 +24,10 @@ export interface PartSlot {
|
|
|
24
24
|
export declare function partValueKeys(parts: PartSlot[]): Record<string, string>;
|
|
25
25
|
export declare function resolveParts(roles: RoleDef[], spec: PartsSpec | undefined): ResolvedPart[];
|
|
26
26
|
export declare function partColumns(parts: ResolvedPart[]): Record<string, ColumnType>;
|
|
27
|
-
export declare function partsRowShape(shape: Record<string, z.ZodTypeAny>, parts: ResolvedPart[]): Record<string, z.ZodTypeAny>;
|
|
27
|
+
export declare function partsRowShape(shape: Record<string, z.ZodTypeAny>, parts: ResolvedPart[], tolerateNull?: boolean): Record<string, z.ZodTypeAny>;
|
|
28
|
+
export declare function mixedOwnedStoredParts(parts: ResolvedPart[]): {
|
|
29
|
+
server: string[];
|
|
30
|
+
client: string[];
|
|
31
|
+
} | null;
|
|
32
|
+
export declare function mandatedClientParts(required: unknown, parts: ResolvedPart[]): string[];
|
|
28
33
|
export declare function stripServerOwnedParts(value: Record<string, unknown>, parts: ResolvedPart[]): Record<string, unknown>;
|
package/dist/parts.js
CHANGED
|
@@ -72,7 +72,7 @@ export function partColumns(parts) {
|
|
|
72
72
|
cols[p.key] = p.meta.column;
|
|
73
73
|
return cols;
|
|
74
74
|
}
|
|
75
|
-
export function partsRowShape(shape, parts) {
|
|
75
|
+
export function partsRowShape(shape, parts, tolerateNull = false) {
|
|
76
76
|
const out = { ...shape };
|
|
77
77
|
for (const p of parts)
|
|
78
78
|
if (partValueKey(p) !== p.role)
|
|
@@ -82,11 +82,25 @@ export function partsRowShape(shape, parts) {
|
|
|
82
82
|
out[p.role] = z.unknown().optional();
|
|
83
83
|
else if (p.mode === 'computedStored')
|
|
84
84
|
out[p.key] = z.unknown().optional();
|
|
85
|
-
else
|
|
86
|
-
|
|
85
|
+
else {
|
|
86
|
+
const own = p.overridden ? p.meta.zod : (shape[p.role] ?? z.unknown().optional());
|
|
87
|
+
out[p.key] = tolerateNull && !(p.overridden && p.meta.required === true) ? own.nullable() : own;
|
|
88
|
+
}
|
|
87
89
|
}
|
|
88
90
|
return out;
|
|
89
91
|
}
|
|
92
|
+
export function mixedOwnedStoredParts(parts) {
|
|
93
|
+
const server = parts.filter((p) => p.mode === 'computedStored').map((p) => p.role);
|
|
94
|
+
const client = parts.filter((p) => p.mode === 'stored').map((p) => p.role);
|
|
95
|
+
return server.length > 0 && client.length > 0 ? { server, client } : null;
|
|
96
|
+
}
|
|
97
|
+
export function mandatedClientParts(required, parts) {
|
|
98
|
+
const speaks = (flag) => flag !== undefined && flag !== false;
|
|
99
|
+
const field = speaks(required);
|
|
100
|
+
return parts
|
|
101
|
+
.filter((p) => p.mode === 'stored' && (field || (p.overridden && speaks(p.meta.required))))
|
|
102
|
+
.map((p) => p.role);
|
|
103
|
+
}
|
|
90
104
|
function serverOwnedSlot(p) {
|
|
91
105
|
if (!p.key)
|
|
92
106
|
return p.role;
|
package/dist/shelf.d.ts
CHANGED
|
@@ -28,12 +28,14 @@ export interface ShelfDef {
|
|
|
28
28
|
export declare function fieldEntries(items: LayoutEl[]): [string, FieldMeta][];
|
|
29
29
|
export declare function storageColumns(key: string, field: FieldMeta): [string, ColumnType][];
|
|
30
30
|
export declare function cellValue(field: FieldMeta, key: string, row: Record<string, unknown>): unknown;
|
|
31
|
+
export declare function magnitudeSub(field: FieldMeta): string | undefined;
|
|
31
32
|
export interface CollectionEntry {
|
|
32
33
|
key: string;
|
|
33
34
|
group: GroupEl;
|
|
34
35
|
}
|
|
35
36
|
export declare function collectionGroups(items: LayoutEl[]): CollectionEntry[];
|
|
36
37
|
export declare function fieldMap(items: LayoutEl[]): Record<string, FieldMeta>;
|
|
38
|
+
export declare function checkFieldDeclaration(fields: LayoutEl[], owner: string): void;
|
|
37
39
|
export declare function defineShelf(m: ShelfDef): ShelfDef;
|
|
38
40
|
export declare const SYSTEM_FIELDS: readonly ["id", "createdAt", "updatedAt"];
|
|
39
41
|
export declare function titleKey(m: ShelfDef): string;
|
package/dist/shelf.js
CHANGED
|
@@ -4,6 +4,7 @@ import { resolveFlag } from "./condition.js";
|
|
|
4
4
|
import { parseUrl } from "./fields/url.js";
|
|
5
5
|
import { vmsg } from "./fields/validation.js";
|
|
6
6
|
import { derivableKeys, legacyDeriveDeps } from "./derive.js";
|
|
7
|
+
import { mixedOwnedStoredParts, mandatedClientParts } from "./parts.js";
|
|
7
8
|
export function fieldEntries(items) {
|
|
8
9
|
const out = [];
|
|
9
10
|
for (const it of items) {
|
|
@@ -34,14 +35,21 @@ export function cellValue(field, key, row) {
|
|
|
34
35
|
const out = {};
|
|
35
36
|
let filled = false;
|
|
36
37
|
for (const sub of Object.keys(field.columns)) {
|
|
37
|
-
const
|
|
38
|
-
if (
|
|
38
|
+
const col = `${key}__${sub}`;
|
|
39
|
+
if (!(col in row))
|
|
39
40
|
continue;
|
|
41
|
+
const v = row[col];
|
|
40
42
|
out[sub] = v;
|
|
41
|
-
|
|
43
|
+
if (v != null && v !== '')
|
|
44
|
+
filled = true;
|
|
42
45
|
}
|
|
43
46
|
return filled ? out : undefined;
|
|
44
47
|
}
|
|
48
|
+
export function magnitudeSub(field) {
|
|
49
|
+
if (!field.columns)
|
|
50
|
+
return undefined;
|
|
51
|
+
return Object.keys(field.columns)[0];
|
|
52
|
+
}
|
|
45
53
|
export function collectionGroups(items) {
|
|
46
54
|
const out = [];
|
|
47
55
|
for (const it of items) {
|
|
@@ -65,14 +73,41 @@ export function fieldMap(items) {
|
|
|
65
73
|
}
|
|
66
74
|
return m;
|
|
67
75
|
}
|
|
68
|
-
function assertNoKeylessCompute(items,
|
|
76
|
+
function assertNoKeylessCompute(items, owner) {
|
|
69
77
|
for (const it of items) {
|
|
70
78
|
if (isStatic(it) && typeof it.value === 'function')
|
|
71
|
-
throw new Error(`${
|
|
79
|
+
throw new Error(`${owner}: computed element needs a key — the server computes it and needs a name to return it under`);
|
|
72
80
|
if (isGroup(it))
|
|
73
|
-
assertNoKeylessCompute(it.fields,
|
|
81
|
+
assertNoKeylessCompute(it.fields, owner);
|
|
74
82
|
}
|
|
75
83
|
}
|
|
84
|
+
function warnMixedOwnedParts(items, owner, path = '') {
|
|
85
|
+
for (const it of items) {
|
|
86
|
+
if (isGroup(it)) {
|
|
87
|
+
warnMixedOwnedParts(it.fields, owner, it.key ? `${path}${it.key}.` : path);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
if (!isField(it) || !it.type.parts)
|
|
91
|
+
continue;
|
|
92
|
+
const mixed = mixedOwnedStoredParts(it.type.parts);
|
|
93
|
+
if (!mixed)
|
|
94
|
+
continue;
|
|
95
|
+
const mandated = mandatedClientParts(it.type.required, it.type.parts);
|
|
96
|
+
if (!mandated.length)
|
|
97
|
+
continue;
|
|
98
|
+
console.warn(`${owner}: composite '${path}${it.key}' mandates a client-owned stored part (${mandated.join(', ')}) ` +
|
|
99
|
+
`and mixes it with a server-owned stored part (${mixed.server.join(', ')}) — ` +
|
|
100
|
+
`a mandated part is the client's to declare, so the write path does NOT materialize this composite ` +
|
|
101
|
+
`and the computed part stays absent until a client sends the field. Either drop \`required\` ` +
|
|
102
|
+
`(from the field or from the ${mandated.join('/')} filling), or give every stored part a \`value\` ` +
|
|
103
|
+
`(the composite becomes fully server-owned), or drop the \`key\` of ` +
|
|
104
|
+
`${mixed.client.join('/')} so it is unstored (pinned/computed and injected on read).`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
export function checkFieldDeclaration(fields, owner) {
|
|
108
|
+
assertNoKeylessCompute(fields, owner);
|
|
109
|
+
warnMixedOwnedParts(fields, owner);
|
|
110
|
+
}
|
|
76
111
|
export function defineShelf(m) {
|
|
77
112
|
const keys = fieldEntries(m.fields).map(([k]) => k);
|
|
78
113
|
const dup = keys.find((k, i) => keys.indexOf(k) !== i);
|
|
@@ -94,7 +129,7 @@ export function defineShelf(m) {
|
|
|
94
129
|
console.warn(`[shelf] ${m.library}/${m.shelf}: ${titles.length} title fields (${titles.map(([k]) => k).join(', ')}) — '${titles[0][0]}' wins`);
|
|
95
130
|
if (m.standalone !== false && !m.single && titleKey(m) === 'id')
|
|
96
131
|
console.warn(`[shelf] ${m.library}/${m.shelf}: no title declared (field.title() or views.title) — the record renders no heading`);
|
|
97
|
-
|
|
132
|
+
checkFieldDeclaration(m.fields, `[shelf] ${m.library}/${m.shelf}`);
|
|
98
133
|
const known = derivableKeys(m.fields);
|
|
99
134
|
for (const [key, deps] of legacyDeriveDeps(m.fields)) {
|
|
100
135
|
for (const dep of deps) {
|
|
@@ -228,7 +263,7 @@ export function buildShape(fields, partial) {
|
|
|
228
263
|
if (isField(it)) {
|
|
229
264
|
if (it.type.virtual)
|
|
230
265
|
continue;
|
|
231
|
-
if (it.type.derive) {
|
|
266
|
+
if (it.type.derive || it.type.compute) {
|
|
232
267
|
shape[it.key] = z.never({ error: () => vmsg('derived') }).optional();
|
|
233
268
|
continue;
|
|
234
269
|
}
|