@coffer-org/plugin-transit 7.0.1 → 7.1.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/schema.js CHANGED
@@ -29,6 +29,8 @@ function defineLibrary(v) {
29
29
  //#region ../sdk/src/plugin.ts
30
30
  function definePlugin(p) {
31
31
  if (!p.id) throw new Error("[plugin] missing id");
32
+ if (!p.label) throw new Error(`[plugin] ${p.id}: missing label`);
33
+ if (!p.description) throw new Error(`[plugin] ${p.id}: missing description`);
32
34
  if (!p.version) console.warn(`[plugin] ${p.id}: missing version`);
33
35
  return p;
34
36
  }
@@ -169,7 +171,7 @@ function slugify(input) {
169
171
  return input.toLowerCase().trim().replace(/[^\w\s-]/g, "").replace(/[\s_-]+/g, "-").replace(/^-+|-+$/g, "");
170
172
  }
171
173
  var captureStackTrace = "captureStackTrace" in Error ? Error.captureStackTrace : (..._args) => {};
172
- function isObject$1(data) {
174
+ function isObject(data) {
173
175
  return typeof data === "object" && data !== null && !Array.isArray(data);
174
176
  }
175
177
  var allowsEval = /* @__PURE__*/ cached(() => {
@@ -183,12 +185,12 @@ var allowsEval = /* @__PURE__*/ cached(() => {
183
185
  }
184
186
  });
185
187
  function isPlainObject(o) {
186
- if (isObject$1(o) === false) return false;
188
+ if (isObject(o) === false) return false;
187
189
  const ctor = o.constructor;
188
190
  if (ctor === void 0) return true;
189
191
  if (typeof ctor !== "function") return true;
190
192
  const prot = ctor.prototype;
191
- if (isObject$1(prot) === false) return false;
193
+ if (isObject(prot) === false) return false;
192
194
  if (Object.prototype.hasOwnProperty.call(prot, "isPrototypeOf") === false) return false;
193
195
  return true;
194
196
  }
@@ -1590,13 +1592,13 @@ var $ZodObject = /*@__PURE__*/ $constructor("$ZodObject", (inst, def) => {
1590
1592
  }
1591
1593
  return propValues;
1592
1594
  });
1593
- const isObject = isObject$1;
1595
+ const isObject$2 = isObject;
1594
1596
  const catchall = def.catchall;
1595
1597
  let value;
1596
1598
  inst._zod.parse = (payload, ctx) => {
1597
1599
  value ?? (value = _normalized.value);
1598
1600
  const input = payload.value;
1599
- if (!isObject(input)) {
1601
+ if (!isObject$2(input)) {
1600
1602
  payload.issues.push({
1601
1603
  expected: "object",
1602
1604
  code: "invalid_type",
@@ -1719,7 +1721,7 @@ var $ZodObjectJIT = /*@__PURE__*/ $constructor("$ZodObjectJIT", (inst, def) => {
1719
1721
  return (payload, ctx) => fn(shape, payload, ctx);
1720
1722
  };
1721
1723
  let fastpass;
1722
- const isObject = isObject$1;
1724
+ const isObject$1 = isObject;
1723
1725
  const jit = !globalConfig.jitless;
1724
1726
  const fastEnabled = jit && allowsEval.value;
1725
1727
  const catchall = def.catchall;
@@ -1727,7 +1729,7 @@ var $ZodObjectJIT = /*@__PURE__*/ $constructor("$ZodObjectJIT", (inst, def) => {
1727
1729
  inst._zod.parse = (payload, ctx) => {
1728
1730
  value ?? (value = _normalized.value);
1729
1731
  const input = payload.value;
1730
- if (!isObject(input)) {
1732
+ if (!isObject$1(input)) {
1731
1733
  payload.issues.push({
1732
1734
  expected: "object",
1733
1735
  code: "invalid_type",
@@ -2729,7 +2731,7 @@ function initializeContext(params) {
2729
2731
  external: params?.external ?? void 0
2730
2732
  };
2731
2733
  }
2732
- function process$1(schema, ctx, _params = {
2734
+ function process(schema, ctx, _params = {
2733
2735
  path: [],
2734
2736
  schemaPath: []
2735
2737
  }) {
@@ -2766,7 +2768,7 @@ function process$1(schema, ctx, _params = {
2766
2768
  const parent = schema._zod.parent;
2767
2769
  if (parent) {
2768
2770
  if (!result.ref) result.ref = parent;
2769
- process$1(parent, ctx, params);
2771
+ process(parent, ctx, params);
2770
2772
  ctx.seen.get(parent).isParent = true;
2771
2773
  }
2772
2774
  }
@@ -2986,7 +2988,7 @@ var createToJSONSchemaMethod = (schema, processors = {}) => (params) => {
2986
2988
  ...params,
2987
2989
  processors
2988
2990
  });
2989
- process$1(schema, ctx);
2991
+ process(schema, ctx);
2990
2992
  extractDefs(ctx, schema);
2991
2993
  return finalize(ctx, schema);
2992
2994
  };
@@ -2998,7 +3000,7 @@ var createStandardJSONSchemaMethod = (schema, io, processors = {}) => (params) =
2998
3000
  io,
2999
3001
  processors
3000
3002
  });
3001
- process$1(schema, ctx);
3003
+ process(schema, ctx);
3002
3004
  extractDefs(ctx, schema);
3003
3005
  return finalize(ctx, schema);
3004
3006
  };
@@ -3078,7 +3080,7 @@ var arrayProcessor = (schema, ctx, _json, params) => {
3078
3080
  if (typeof minimum === "number") json.minItems = minimum;
3079
3081
  if (typeof maximum === "number") json.maxItems = maximum;
3080
3082
  json.type = "array";
3081
- json.items = process$1(def.element, ctx, {
3083
+ json.items = process(def.element, ctx, {
3082
3084
  ...params,
3083
3085
  path: [...params.path, "items"]
3084
3086
  });
@@ -3089,7 +3091,7 @@ var objectProcessor = (schema, ctx, _json, params) => {
3089
3091
  json.type = "object";
3090
3092
  json.properties = {};
3091
3093
  const shape = def.shape;
3092
- for (const key in shape) json.properties[key] = process$1(shape[key], ctx, {
3094
+ for (const key in shape) json.properties[key] = process(shape[key], ctx, {
3093
3095
  ...params,
3094
3096
  path: [
3095
3097
  ...params.path,
@@ -3107,7 +3109,7 @@ var objectProcessor = (schema, ctx, _json, params) => {
3107
3109
  if (def.catchall?._zod.def.type === "never") json.additionalProperties = false;
3108
3110
  else if (!def.catchall) {
3109
3111
  if (ctx.io === "output") json.additionalProperties = false;
3110
- } else if (def.catchall) json.additionalProperties = process$1(def.catchall, ctx, {
3112
+ } else if (def.catchall) json.additionalProperties = process(def.catchall, ctx, {
3111
3113
  ...params,
3112
3114
  path: [...params.path, "additionalProperties"]
3113
3115
  });
@@ -3115,7 +3117,7 @@ var objectProcessor = (schema, ctx, _json, params) => {
3115
3117
  var unionProcessor = (schema, ctx, json, params) => {
3116
3118
  const def = schema._zod.def;
3117
3119
  const isExclusive = def.inclusive === false;
3118
- const options = def.options.map((x, i) => process$1(x, ctx, {
3120
+ const options = def.options.map((x, i) => process(x, ctx, {
3119
3121
  ...params,
3120
3122
  path: [
3121
3123
  ...params.path,
@@ -3128,7 +3130,7 @@ var unionProcessor = (schema, ctx, json, params) => {
3128
3130
  };
3129
3131
  var intersectionProcessor = (schema, ctx, json, params) => {
3130
3132
  const def = schema._zod.def;
3131
- const a = process$1(def.left, ctx, {
3133
+ const a = process(def.left, ctx, {
3132
3134
  ...params,
3133
3135
  path: [
3134
3136
  ...params.path,
@@ -3136,7 +3138,7 @@ var intersectionProcessor = (schema, ctx, json, params) => {
3136
3138
  0
3137
3139
  ]
3138
3140
  });
3139
- const b = process$1(def.right, ctx, {
3141
+ const b = process(def.right, ctx, {
3140
3142
  ...params,
3141
3143
  path: [
3142
3144
  ...params.path,
@@ -3149,7 +3151,7 @@ var intersectionProcessor = (schema, ctx, json, params) => {
3149
3151
  };
3150
3152
  var nullableProcessor = (schema, ctx, json, params) => {
3151
3153
  const def = schema._zod.def;
3152
- const inner = process$1(def.innerType, ctx, params);
3154
+ const inner = process(def.innerType, ctx, params);
3153
3155
  const seen = ctx.seen.get(schema);
3154
3156
  if (ctx.target === "openapi-3.0") {
3155
3157
  seen.ref = def.innerType;
@@ -3158,27 +3160,27 @@ var nullableProcessor = (schema, ctx, json, params) => {
3158
3160
  };
3159
3161
  var nonoptionalProcessor = (schema, ctx, _json, params) => {
3160
3162
  const def = schema._zod.def;
3161
- process$1(def.innerType, ctx, params);
3163
+ process(def.innerType, ctx, params);
3162
3164
  const seen = ctx.seen.get(schema);
3163
3165
  seen.ref = def.innerType;
3164
3166
  };
3165
3167
  var defaultProcessor = (schema, ctx, json, params) => {
3166
3168
  const def = schema._zod.def;
3167
- process$1(def.innerType, ctx, params);
3169
+ process(def.innerType, ctx, params);
3168
3170
  const seen = ctx.seen.get(schema);
3169
3171
  seen.ref = def.innerType;
3170
3172
  json.default = JSON.parse(JSON.stringify(def.defaultValue));
3171
3173
  };
3172
3174
  var prefaultProcessor = (schema, ctx, json, params) => {
3173
3175
  const def = schema._zod.def;
3174
- process$1(def.innerType, ctx, params);
3176
+ process(def.innerType, ctx, params);
3175
3177
  const seen = ctx.seen.get(schema);
3176
3178
  seen.ref = def.innerType;
3177
3179
  if (ctx.io === "input") json._prefault = JSON.parse(JSON.stringify(def.defaultValue));
3178
3180
  };
3179
3181
  var catchProcessor = (schema, ctx, json, params) => {
3180
3182
  const def = schema._zod.def;
3181
- process$1(def.innerType, ctx, params);
3183
+ process(def.innerType, ctx, params);
3182
3184
  const seen = ctx.seen.get(schema);
3183
3185
  seen.ref = def.innerType;
3184
3186
  let catchValue;
@@ -3193,20 +3195,20 @@ var pipeProcessor = (schema, ctx, _json, params) => {
3193
3195
  const def = schema._zod.def;
3194
3196
  const inIsTransform = def.in._zod.traits.has("$ZodTransform");
3195
3197
  const innerType = ctx.io === "input" ? inIsTransform ? def.out : def.in : def.out;
3196
- process$1(innerType, ctx, params);
3198
+ process(innerType, ctx, params);
3197
3199
  const seen = ctx.seen.get(schema);
3198
3200
  seen.ref = innerType;
3199
3201
  };
3200
3202
  var readonlyProcessor = (schema, ctx, json, params) => {
3201
3203
  const def = schema._zod.def;
3202
- process$1(def.innerType, ctx, params);
3204
+ process(def.innerType, ctx, params);
3203
3205
  const seen = ctx.seen.get(schema);
3204
3206
  seen.ref = def.innerType;
3205
3207
  json.readOnly = true;
3206
3208
  };
3207
3209
  var optionalProcessor = (schema, ctx, _json, params) => {
3208
3210
  const def = schema._zod.def;
3209
- process$1(def.innerType, ctx, params);
3211
+ process(def.innerType, ctx, params);
3210
3212
  const seen = ctx.seen.get(schema);
3211
3213
  seen.ref = def.innerType;
3212
3214
  };
@@ -4467,16 +4469,18 @@ function jsonRefined(inner, code) {
4467
4469
  });
4468
4470
  });
4469
4471
  }
4470
- function optionalize(schema, required) {
4471
- const req = required === true;
4472
- const pre = (v) => v === "" || v === null ? void 0 : v;
4473
- if (!req) return preprocess(pre, schema.optional());
4474
- return preprocess(pre, unknown().superRefine((v, ctx) => {
4475
- if (v === void 0) ctx.addIssue({
4476
- code: ZodIssueCode.custom,
4477
- message: vmsg("required")
4478
- });
4479
- }).pipe(schema));
4472
+ /**
4473
+ * An empty value is no value: `''` and `null` become `undefined`, and the field's own schema
4474
+ * never sees them. Every field is optional, so this is the ONLY shape a field cannot demand
4475
+ * a value, and the rules it carries judge what was entered rather than whether anything was.
4476
+ *
4477
+ * This used to have a second branch, for `required` fields, which turned the same emptiness
4478
+ * into a `required` issue through a `z.unknown()` guard piped into the schema. That branch is
4479
+ * why a `.min(1)` on a required string was unreachable: the empty string had already been
4480
+ * converted to the issue before the string schema ran.
4481
+ */
4482
+ function optionalize(schema) {
4483
+ return preprocess((v) => v === "" || v === null ? void 0 : v, schema.optional());
4480
4484
  }
4481
4485
  //#endregion
4482
4486
  //#region ../sdk/src/fields/meta.ts
@@ -4558,7 +4562,7 @@ function applyMultiple(base, multiple) {
4558
4562
  multiple: true
4559
4563
  },
4560
4564
  json: true,
4561
- zod: optionalize(s, base.required)
4565
+ zod: optionalize(s)
4562
4566
  };
4563
4567
  }
4564
4568
  /** Inline option entry → OptionItem (plain string = value and label at once). */
@@ -4673,13 +4677,6 @@ function wrapKey(key, opts, meta) {
4673
4677
  noLabel: true
4674
4678
  }
4675
4679
  };
4676
- if (opts.role) m = {
4677
- ...m,
4678
- hints: {
4679
- ...m.hints,
4680
- role: opts.role
4681
- }
4682
- };
4683
4680
  if (opts.editor) m = {
4684
4681
  ...m,
4685
4682
  hints: {
@@ -4701,6 +4698,13 @@ function wrapKey(key, opts, meta) {
4701
4698
  span: opts.span
4702
4699
  }
4703
4700
  };
4701
+ if (opts.faces !== void 0) m = {
4702
+ ...m,
4703
+ hints: {
4704
+ ...m.hints,
4705
+ faces: opts.faces
4706
+ }
4707
+ };
4704
4708
  if (opts.default !== void 0) m = {
4705
4709
  ...m,
4706
4710
  default: opts.default
@@ -4800,9 +4804,9 @@ function normalizeOpts(rawIn) {
4800
4804
  ...v.valueLabel !== void 0 ? { valueLabel: v.valueLabel } : {},
4801
4805
  compareWith: v.compareWith,
4802
4806
  span: v.span,
4807
+ faces: v.faces,
4803
4808
  emphasis: v.emphasis,
4804
4809
  noLabel: v.noLabel,
4805
- role: v.role,
4806
4810
  editor: v.editor,
4807
4811
  activate: v.activate
4808
4812
  };
@@ -4943,7 +4947,7 @@ function registerPreset(factory, base, presetOpts, over = {}) {
4943
4947
  * dimensions, check(single)) and differed only in the validation code and the per-role
4944
4948
  * default zod.
4945
4949
  *
4946
- * raw → jsonValue → absent by `absenceRoles`? (required 'required')
4950
+ * raw → jsonValue → carries nothing? (passthrough, when `acceptsEmptyRow`)
4947
4951
  * → carries nothing at all? (pass through, there is nothing to judge)
4948
4952
  * → rowSchema (partsRowShape) → stripServerOwnedParts → optional per-type refine
4949
4953
  *
@@ -4953,28 +4957,16 @@ function registerPreset(factory, base, presetOpts, over = {}) {
4953
4957
  * exactly once.
4954
4958
  */
4955
4959
  function buildComposite(def, opts, parts) {
4956
- const required = opts.required ?? false;
4957
- const roleZod = def.roleZod(opts, parts);
4958
- const rowSchema = object(partsRowShape(roleZod, parts, required === true ? "strict" : "null"));
4959
- const absentRowSchema = required === true ? object(partsRowShape(roleZod, parts, "null-or-missing")) : rowSchema;
4960
+ const rowSchema = object(partsRowShape(def.roleZod(opts, parts), parts));
4960
4961
  const roleKeys = (which) => parts.filter((p) => p.mode === "stored" && (which?.(p) ?? true)).map(partValueKey);
4961
- const absenceKeys = def.absenceRoles ? roleKeys((p) => def.absenceRoles.includes(p.role)) : void 0;
4962
- const clientKeys = def.absenceRoles ? roleKeys() : void 0;
4962
+ const clientKeys = def.acceptsEmptyRow ? roleKeys() : void 0;
4963
4963
  const zod = unknown().transform((raw, ctx) => {
4964
4964
  const parsed = jsonValue(raw);
4965
4965
  const p = parsed;
4966
4966
  const isRow = p == null || typeof p === "object" && !Array.isArray(p);
4967
4967
  /** Every one of `keys` empty in this value — `null`/absent alike. */
4968
4968
  const allEmpty = (keys) => isRow && (p == null || keys.length > 0 && keys.every((k) => p[k] == null));
4969
- let absent = false;
4970
- if (absenceKeys && clientKeys) {
4971
- absent = allEmpty(absenceKeys);
4972
- if (absent && required) ctx.addIssue({
4973
- code: ZodIssueCode.custom,
4974
- message: vmsg("required")
4975
- });
4976
- if (allEmpty(clientKeys)) return raw;
4977
- }
4969
+ if (clientKeys && allEmpty(clientKeys)) return raw;
4978
4970
  if (typeof parsed === "string") {
4979
4971
  ctx.addIssue({
4980
4972
  code: ZodIssueCode.custom,
@@ -4982,7 +4974,7 @@ function buildComposite(def, opts, parts) {
4982
4974
  });
4983
4975
  return NEVER;
4984
4976
  }
4985
- const r = (absent ? absentRowSchema : rowSchema).safeParse(parsed);
4977
+ const r = rowSchema.safeParse(parsed);
4986
4978
  if (!r.success) {
4987
4979
  const message = typeof def.structureCode === "function" ? def.structureCode(r.error) : vmsg(def.structureCode);
4988
4980
  ctx.addIssue({
@@ -5035,12 +5027,10 @@ function materialize(decl, name) {
5035
5027
  if (isComposite(type) && (opts.multiple ?? false) && parts.length > 0 && !type.selfManages?.has("multiple")) return collectionGroup(name, opts, parts, type.collectionView);
5036
5028
  const built = isComposite(type) ? buildComposite(type, opts, parts) : type.build(opts, parts, name);
5037
5029
  const selfOptional = type.selfManages?.has("optional") ?? false;
5038
- const required = selfOptional ? false : opts.required ?? false;
5039
5030
  const base = {
5040
5031
  kind: type.kind,
5041
5032
  label: opts.label ?? "",
5042
5033
  ...opts.agent !== void 0 && { agent: opts.agent },
5043
- required,
5044
5034
  prim: type.prim,
5045
5035
  column: built.column,
5046
5036
  hints: built.hints ?? {},
@@ -5051,7 +5041,7 @@ function materialize(decl, name) {
5051
5041
  columns: built.columns,
5052
5042
  parts
5053
5043
  },
5054
- zod: selfOptional ? built.zod : optionalize(built.zod, required)
5044
+ zod: selfOptional ? built.zod : optionalize(built.zod)
5055
5045
  };
5056
5046
  return wrapKey(name, opts, type.selfManages?.has("multiple") ? base : applyMultiple(base, opts.multiple ?? false));
5057
5047
  }
@@ -5081,7 +5071,6 @@ function collectionGroup(key, opts, parts, collectionView) {
5081
5071
  key,
5082
5072
  scope: "nest",
5083
5073
  multiple: true,
5084
- required: opts.required ?? false,
5085
5074
  label: opts.label ?? key,
5086
5075
  ...opts.agent !== void 0 && { agent: opts.agent },
5087
5076
  display: "wrap",
@@ -5252,7 +5241,7 @@ function accepts(def, d) {
5252
5241
  }
5253
5242
  /**
5254
5243
  * The correction every role gets for free: keep what the author said ABOUT the field
5255
- * (`label`, `required`, `ui`, `value` — a constant or a ComputeFn), replace the type, and
5244
+ * (`label`, `ui`, `value` — a constant or a ComputeFn), replace the type, and
5256
5245
  * drop what belonged to the OLD type (`rules` — a number's min/max mean nothing to a
5257
5246
  * picture — and `multiple`, because a composite role owns exactly one column). A role whose
5258
5247
  * own default needs more than a bare factory (`measured`'s unit, `period`'s endpoints)
@@ -5260,10 +5249,9 @@ function accepts(def, d) {
5260
5249
  */
5261
5250
  function retypeTo(factory) {
5262
5251
  return (d) => {
5263
- const { label, required, value } = d?.opts ?? {};
5252
+ const { label, value } = d?.opts ?? {};
5264
5253
  return declare(factory, {
5265
5254
  label,
5266
- required,
5267
5255
  value,
5268
5256
  noSearch: d?.opts.noSearch,
5269
5257
  ui: uiOf(d?.opts)
@@ -5449,72 +5437,73 @@ function partColumns(parts) {
5449
5437
  }
5450
5438
  return cols;
5451
5439
  }
5452
- function partsRowShape(shape, parts, tolerance = "strict") {
5440
+ /**
5441
+ * A composite's write-time row shape, DERIVED from its resolved parts. `shape` carries
5442
+ * the composite's DEFAULT schema per role — the literal the factory writes by hand
5443
+ * (`{ value: numSchema, unit: z.string().refine(…) }`) — and each part decides which
5444
+ * schema guards its (role-named) key:
5445
+ *
5446
+ * - a plain STORED part is keyed by its role name, matching the value object and the
5447
+ * column `<field>__<role>` that `flattenEmbeddedAt`/`nestEmbeddedAt` read. A role left
5448
+ * to its default filling keeps the default schema — byte-identical to the hand-written
5449
+ * literal, so every existing structure/unit/range code and message is preserved.
5450
+ * - an OVERRIDDEN stored part is validated by its OWN zod (`meta.zod`) instead of the
5451
+ * default schema for that role. This is what makes a RETYPED filling real: an
5452
+ * `f.int({})` magnitude rejects 5.5 (its own `int` check) and gets an integer column,
5453
+ * and an `f.relation({…})` currency accepts a record id instead of being measured
5454
+ * against an ISO-4217 string rule it was never meant to satisfy. The filling also
5455
+ * brings its own optionality and its own min/max — an author replacing a filling
5456
+ * takes over that role's constraints, so `f.real({ rules: { min: 0 } })` is how a
5457
+ * replaced magnitude keeps a bound.
5458
+ * - a COMPUTED-AND-STORED part ('computedStored') is keyed by its role name like any
5459
+ * other part, but its schema is `z.unknown().optional()`: the SERVER owns the value, so
5460
+ * whatever the client sends there is ignored — `stripServerOwnedParts` deletes it a
5461
+ * moment later and the write path recomputes the column. It is stripped rather than
5462
+ * `z.never()`-rejected because a read hands the client that key (it is a real column,
5463
+ * present in every response), so rejecting it would break a read → PATCH-the-whole-object
5464
+ * round trip.
5465
+ * - a CLIENT-OWNED stored part of a composite also accepts
5466
+ * an explicit `null` (`tolerateNull`), which is what closes the read → write round trip
5467
+ * for a PARTIALLY FILLED composite. A SELECT returns every sub-column, so a record whose
5468
+ * magnitude column is set and whose unit column is empty reads back as
5469
+ * `{value: 900, unit: null}` (`nestEmbeddedAt` drops the key only when EVERY column is
5470
+ * empty — `0` is a value), and feeding that exact object back used to fail with
5471
+ * `measured_structure`: the write path refused the shape its own read produced. The null
5472
+ * slot now parses through and is written back as NULL, so the round trip is closed and
5473
+ * clearing ONE role of a filled composite works instead of erroring.
5474
+ * NULL, not absence: a read emits every stored role (empty ones as `null`), so `null` is
5475
+ * how "this record has no value there" arrives, while an ABSENT key is a writer that
5476
+ * never mentioned a role it owns — which stays the structure error it has always been
5477
+ * (`{unit: 'kg'}` with no magnitude, `{lat: 50}` with no longitude). That is the
5478
+ * all-or-nothing rule for a DECLARATION, and it is untouched.
5479
+ * A composite used to be able to override this: `required: true` kept every client-owned
5480
+ * slot MANDATORY, null included, so that "tolerate null" could not turn a demanded field
5481
+ * optional. Nothing demands a value now, so there is no override and the rule above is the
5482
+ * only one.
5483
+ */
5484
+ /**
5485
+ * How much a CLIENT-OWNED slot forgives in the row shape: exactly one thing, an explicit
5486
+ * `null`. `.nullable()`, never `.nullish()` — a part that is MISSING entirely is still a
5487
+ * malformed row (`{lat: 5.5}` is half a coordinate), which is the one rule about composites
5488
+ * that judges CORRECTNESS rather than presence.
5489
+ *
5490
+ * There used to be a `RowTolerance` of three levels, and the field's `required` is what chose
5491
+ * between them: 'strict' when the field demanded a value, 'null-or-missing' when the value was
5492
+ * ABSENT under that demand — the third existing only so an absent required composite did not
5493
+ * report the same absence twice, once as `required` and once as a structure code. With nothing
5494
+ * able to demand a value there is no first issue to restate, so both levels and the type
5495
+ * itself are gone.
5496
+ */
5497
+ function partsRowShape(shape, parts) {
5453
5498
  const out = { ...shape };
5454
5499
  for (const p of parts) if (p.mode === "computedStored") out[p.key] = unknown().optional();
5455
5500
  else {
5456
5501
  const own = p.overridden ? p.meta.zod : shape[p.role] ?? unknown().optional();
5457
- const relaxed = tolerance !== "strict" && !(p.overridden && p.meta.required === true);
5458
- out[p.key] = relaxed ? tolerance === "null-or-missing" ? own.nullish() : own.nullable() : own;
5502
+ out[p.key] = own.nullable();
5459
5503
  }
5460
5504
  return out;
5461
5505
  }
5462
5506
  /**
5463
- * MIXED OWNERSHIP of the STORED parts: the composite has a computed-and-stored part (the
5464
- * server owns that column) AND a plain stored part (the client owns that one). Returns the
5465
- * two role lists, or null when the composite is uniformly owned.
5466
- *
5467
- * Such a composite IS materialized by the write path (`applyStoredParts`): the server's part
5468
- * is computed on every write, whether or not the write carries the field, and the client's
5469
- * columns keep whatever the record already holds. A computed sum exists whether or not a
5470
- * user has picked a unit, and a NULL column is invisible to SQL. See THE ABSENT-COMPOSITE
5471
- * RULE in `@coffer-org/server/part-injection` and `docs/decisions.md` (2026-08-12).
5472
- *
5473
- * The one shape this is still wrong for is a composite that MANDATES one of those client-owned
5474
- * roles — see `mandatedClientParts` below.
5475
- */
5476
- function mixedOwnedStoredParts(parts) {
5477
- const server = parts.filter((p) => p.mode === "computedStored").map((p) => p.role);
5478
- const client = parts.filter((p) => p.mode === "stored").map((p) => p.role);
5479
- return server.length > 0 && client.length > 0 ? {
5480
- server,
5481
- client
5482
- } : null;
5483
- }
5484
- /**
5485
- * The CLIENT-OWNED stored roles this composite demands a value for — the roles a
5486
- * server-materialized half-value would leave empty in breach of the field's own contract.
5487
- * Empty list → nothing is mandated and the composite is safe to materialize.
5488
- *
5489
- * THE MANDATE HAS TWO SOURCES, and reading only the first is a bug this had:
5490
- *
5491
- * - the FIELD's own `required`, which `partsRowShape` turns into `tolerateNull = false` for
5492
- * every client-owned slot;
5493
- * - the FILLING's `required`, per part: `partsRowShape` keeps an OVERRIDDEN part's own
5494
- * mandate ("a filling the author declared `required` keeps its mandate"), so
5495
- * `fields: { unit: f.string({ required: true, … }) }` makes THAT slot non-nullable even
5496
- * when the field around it is optional.
5497
- *
5498
- * Either way the materialized value — the server's column filled, the client's empty — reads
5499
- * back as `{value: 5, unit: null}` and is rejected by the composite's own zod with
5500
- * `{"code":"required"}` the moment a record form or an MCP agent feeds it straight back. So
5501
- * both sources must count, and the write path refuses to materialize when either speaks.
5502
- *
5503
- * A CONDITION counts too, though its harm is different and milder. `partsRowShape` compares
5504
- * `=== true`, so a conditional mandate does not make the slot non-nullable and the round trip
5505
- * still parses; what it does instead is let a materialized object satisfy
5506
- * `updateRecord`'s conditional-required check (which only asks whether the value is non-null),
5507
- * excusing the user from a field the schema says is mandatory. Nothing static can evaluate a
5508
- * Condition here, so any mandate that is not literally `false`/absent is treated as speaking —
5509
- * conservative in the safe direction, since refusing to materialize is exactly the older
5510
- * behaviour and loses nothing.
5511
- */
5512
- function mandatedClientParts(required, parts) {
5513
- const speaks = (flag) => flag !== void 0 && flag !== false;
5514
- const field = speaks(required);
5515
- return parts.filter((p) => p.mode === "stored" && (field || p.overridden && speaks(p.meta.required))).map((p) => p.role);
5516
- }
5517
- /**
5518
5507
  * Drops SERVER-OWNED part slots from a successfully parsed composite row: a computed-and-stored
5519
5508
  * role, keyed by its role name. Its value belongs to the schema, not to the writer — it is
5520
5509
  * recomputed and written by the server on the same write (`applyStoredParts`) — so a client
@@ -5561,13 +5550,10 @@ function email(o) {
5561
5550
  registerPreset("email", "string", {}, {
5562
5551
  kind: "email",
5563
5552
  widget: "email",
5564
- build(o) {
5565
- const required = o.required ?? false;
5566
- let s = string$1().email({ message: vmsg("email") });
5567
- if (required) s = s.min(1, { message: vmsg("min_length", { min: 1 }) });
5553
+ build() {
5568
5554
  return {
5569
5555
  column: "text",
5570
- zod: s,
5556
+ zod: string$1().email({ message: vmsg("email") }),
5571
5557
  hints: {
5572
5558
  format: "email",
5573
5559
  inputType: "email"
@@ -5590,12 +5576,10 @@ function tel(o) {
5590
5576
  registerPreset("tel", "string", {}, {
5591
5577
  kind: "tel",
5592
5578
  widget: "tel",
5593
- build(o) {
5594
- const required = o.required ?? false;
5595
- const base_z = string$1().regex(TEL_RE, { message: vmsg("pattern", { messageKey: "core.presets.tel" }) });
5579
+ build() {
5596
5580
  return {
5597
5581
  column: "text",
5598
- zod: required ? base_z.min(1, { message: vmsg("min_length", { min: 1 }) }) : base_z,
5582
+ zod: string$1().regex(TEL_RE, { message: vmsg("pattern", { messageKey: "core.presets.tel" }) }),
5599
5583
  hints: {
5600
5584
  format: "tel",
5601
5585
  inputType: "tel"
@@ -5619,10 +5603,10 @@ registerPreset("password", "string", {}, {
5619
5603
  kind: "password",
5620
5604
  widget: "password",
5621
5605
  selfManages: /* @__PURE__ */ new Set(["multiple"]),
5622
- build(o) {
5606
+ build() {
5623
5607
  return {
5624
5608
  column: "text",
5625
- zod: o.required ?? false ? string$1().min(1, { message: vmsg("min_length", { min: 1 }) }) : string$1(),
5609
+ zod: string$1(),
5626
5610
  hints: {
5627
5611
  format: "password",
5628
5612
  inputType: "password"
@@ -5684,6 +5668,31 @@ function internalOauthGrants(o) {
5684
5668
  }
5685
5669
  });
5686
5670
  }
5671
+ /**
5672
+ * The account page's linked-accounts section. Same trick as `internalApiToken`: a container
5673
+ * with a custom `ui.kind`, drawn by its own renderer, which reads `/api/auth/links` itself.
5674
+ * The declared children are what one ROW holds — the renderer reads them through the slot
5675
+ * contract, so this is a declaration, not a layout.
5676
+ *
5677
+ * @layer preset
5678
+ * @base group
5679
+ * @prim —
5680
+ * @widget internalIdentityLinks
5681
+ * @example f.internalIdentityLinks({ label: 'auth.linksTitle' })
5682
+ */
5683
+ function internalIdentityLinks(o) {
5684
+ return group({
5685
+ scope: "nest",
5686
+ label: o.label,
5687
+ multiple: true,
5688
+ ui: { kind: "internalIdentityLinks" },
5689
+ fields: {
5690
+ provider: string({}),
5691
+ externalId: string({}),
5692
+ linkedAt: string({})
5693
+ }
5694
+ });
5695
+ }
5687
5696
  var SLUG_RE = /^[a-z0-9-]+$/;
5688
5697
  /**
5689
5698
  * Slug — kind 'slug', `^[a-z0-9-]+$`.
@@ -5708,6 +5717,63 @@ registerPreset("slug", "string", {}, {
5708
5717
  };
5709
5718
  }
5710
5719
  });
5720
+ /**
5721
+ * Identifier — a code printed for a machine and read back by one: a serial number, an order
5722
+ * number, a policy number, an IMEI. What a person actually DOES with one is copy it, which is
5723
+ * why this is a type of its own and not `f.string({ ui: { voice: 'data' } })`: the machine face
5724
+ * is one word on any string now, but the click that copies belongs to the kind.
5725
+ *
5726
+ * Deliberately unvalidated beyond a length: an identifier's format belongs to whoever issued
5727
+ * it, and a pattern here would reject the next manufacturer's.
5728
+ *
5729
+ * @layer preset
5730
+ * @base string
5731
+ * @prim text
5732
+ * @widget identifier
5733
+ * @example f.identifier({ label: 'mod.fields.serialNumber' })
5734
+ */
5735
+ function identifier(o) {
5736
+ return declare("identifier", o);
5737
+ }
5738
+ registerPreset("identifier", "string", {}, {
5739
+ kind: "identifier",
5740
+ widget: "identifier",
5741
+ build() {
5742
+ return {
5743
+ column: "text",
5744
+ zod: string$1(),
5745
+ hints: {}
5746
+ };
5747
+ }
5748
+ });
5749
+ /**
5750
+ * The machine-readable zone of a travel document — the identifier taken to its limit: printed
5751
+ * for a scanner, not for a person. Fixed pitch, chevrons kept as the filler they are, and the
5752
+ * line breaks preserved, because an MRZ's line structure is part of what it encodes.
5753
+ *
5754
+ * Unvalidated on purpose, like `identifier`: the ICAO line formats differ by document type, and
5755
+ * a strip transcribed from a real document is worth storing even when it does not check out.
5756
+ *
5757
+ * @layer preset
5758
+ * @base string
5759
+ * @prim text
5760
+ * @widget mrz
5761
+ * @example f.mrz({ label: 'documents.personal_document.fields.mrz' })
5762
+ */
5763
+ function mrz(o) {
5764
+ return declare("mrz", o);
5765
+ }
5766
+ registerPreset("mrz", "string", {}, {
5767
+ kind: "mrz",
5768
+ widget: "mrz",
5769
+ build() {
5770
+ return {
5771
+ column: "text",
5772
+ zod: string$1(),
5773
+ hints: {}
5774
+ };
5775
+ }
5776
+ });
5711
5777
  var COLOR_RE = /^#[0-9a-fA-F]{6}$/;
5712
5778
  /**
5713
5779
  * Hex color — kind 'color', `#rrggbb` + swatch widget.
@@ -5747,10 +5813,10 @@ function colorname(o) {
5747
5813
  registerPreset("colorname", "string", {}, {
5748
5814
  kind: "colorname",
5749
5815
  widget: "colorname",
5750
- build(o) {
5816
+ build() {
5751
5817
  return {
5752
5818
  column: "text",
5753
- zod: (o.required ?? false ? string$1().min(1, { message: vmsg("min_length", { min: 1 }) }) : string$1()).refine((v) => CSS_COLOR_NAMES.has(v.toLowerCase()), { message: vmsg("colorname") }),
5819
+ zod: string$1().refine((v) => CSS_COLOR_NAMES.has(v.toLowerCase()), { message: vmsg("colorname") }),
5754
5820
  hints: {}
5755
5821
  };
5756
5822
  }
@@ -5770,10 +5836,10 @@ function title(o) {
5770
5836
  registerPreset("title", "string", {}, {
5771
5837
  kind: "title",
5772
5838
  widget: "title",
5773
- build(o) {
5839
+ build() {
5774
5840
  return {
5775
5841
  column: "text",
5776
- zod: o.required ?? false ? string$1().min(1, { message: vmsg("min_length", { min: 1 }) }) : string$1(),
5842
+ zod: string$1(),
5777
5843
  hints: {}
5778
5844
  };
5779
5845
  }
@@ -5794,12 +5860,10 @@ function link(o) {
5794
5860
  registerPreset("link", "string", {}, {
5795
5861
  kind: "link",
5796
5862
  widget: "link",
5797
- build(o) {
5798
- const required = o.required ?? false;
5799
- const base_z = string$1().regex(LINK_RE, { message: vmsg("pattern", { messageKey: "core.presets.link" }) });
5863
+ build() {
5800
5864
  return {
5801
5865
  column: "text",
5802
- zod: required ? base_z.min(1, { message: vmsg("min_length", { min: 1 }) }) : base_z,
5866
+ zod: string$1().regex(LINK_RE, { message: vmsg("pattern", { messageKey: "core.presets.link" }) }),
5803
5867
  hints: {
5804
5868
  format: "url",
5805
5869
  inputType: "url"
@@ -5992,6 +6056,38 @@ registerPreset("tag", "string", {}, {
5992
6056
  }
5993
6057
  });
5994
6058
  /**
6059
+ * A location written the way it is said aloud: workshop › shelf 2 › box. A `string` fixed to
6060
+ * `multiple`, exactly as `f.tags` is, because a place inside a place inside a place IS a
6061
+ * sequence — the plurality is the type, not a modifier someone remembered to add.
6062
+ *
6063
+ * Distinct from `f.tags`, which stores the same shape: tags are an unordered SET, and a path is
6064
+ * an ordered CHAIN where each step is inside the one before it. Drawn as chips, that
6065
+ * containment — the only thing the value actually carries — is lost.
6066
+ *
6067
+ * @layer preset
6068
+ * @base string
6069
+ * @prim text
6070
+ * @widget path
6071
+ * @example f.path({ label: 'things.storage_location.fields.path' })
6072
+ */
6073
+ function path(o) {
6074
+ return declare("path", {
6075
+ ...o,
6076
+ multiple: true
6077
+ });
6078
+ }
6079
+ registerPreset("path", "string", {}, {
6080
+ kind: "path",
6081
+ widget: "path",
6082
+ build() {
6083
+ return {
6084
+ column: "text",
6085
+ zod: string$1(),
6086
+ hints: {}
6087
+ };
6088
+ }
6089
+ });
6090
+ /**
5995
6091
  * Tags — `tag({ multiple: true })`.
5996
6092
  *
5997
6093
  * @layer preset
@@ -6184,6 +6280,34 @@ registerPreset("reminder", "date", { lead: 30 }, {
6184
6280
  };
6185
6281
  }
6186
6282
  });
6283
+ /** Timetable — a set of times of day, read as a schedule rather than as a bag of values.
6284
+ * kind 'timetable', prim 'time', always `multiple`.
6285
+ *
6286
+ * Same shape as `f.reminder`/`f.age` over `date`: a semantic type whose renderer knows what
6287
+ * the values MEAN. Everything it shows beyond the times themselves — when the service starts
6288
+ * and ends, how many runs there are, which one is next — is derived from the values at render
6289
+ * time, never stored: a "next departure" is wrong the moment it is written down, exactly the
6290
+ * argument `f.age` already makes for an age.
6291
+ *
6292
+ * Storage is a plain multiple `time`, so a field can be switched to this from
6293
+ * `f.time({ multiple: true })` and back with no migration.
6294
+ *
6295
+ * @layer preset
6296
+ * @base time
6297
+ * @prim time
6298
+ * @widget timetable
6299
+ * @example f.timetable({ label: 'mod.fields.weekdays' })
6300
+ */
6301
+ function timetable(o) {
6302
+ return declare("timetable", {
6303
+ ...o,
6304
+ multiple: true
6305
+ });
6306
+ }
6307
+ registerPreset("timetable", "time", {}, {
6308
+ kind: "timetable",
6309
+ widget: "timetable"
6310
+ });
6187
6311
  /** Age — date whose whole-year age (as of today) renders alongside it, e.g. a birth date.
6188
6312
  * kind 'age', prim 'date'. The age itself is computed at RENDER time, never stored: unlike a
6189
6313
  * `mutate`-driven computed field, it must be right on every day that passes, not only the day
@@ -6398,6 +6522,9 @@ var presets = {
6398
6522
  email,
6399
6523
  tel,
6400
6524
  slug,
6525
+ path,
6526
+ identifier,
6527
+ mrz,
6401
6528
  color,
6402
6529
  colorname,
6403
6530
  title,
@@ -6412,6 +6539,7 @@ var presets = {
6412
6539
  duration,
6413
6540
  reminder,
6414
6541
  age,
6542
+ timetable,
6415
6543
  percent,
6416
6544
  year,
6417
6545
  weight,
@@ -6420,6 +6548,7 @@ var presets = {
6420
6548
  currency,
6421
6549
  money,
6422
6550
  internalApiToken,
6551
+ internalIdentityLinks,
6423
6552
  internalOauthGrants
6424
6553
  };
6425
6554
  //#endregion
@@ -7069,26 +7198,38 @@ function identity(opts) {
7069
7198
  }
7070
7199
  };
7071
7200
  }
7072
- /** A scored value out of an optional max, with an optional verdict.
7201
+ /** One measure out of an optional max, from ONE OR MORE named sources, with an optional verdict.
7202
+ *
7203
+ * `source` takes several entries for the same reason `f.identity`'s `channels` does — it is a
7204
+ * role that holds a LIST, so `view.source` is an ordered list of names rather than one name.
7205
+ * Two ratings of the same film out of ten are one measure read twice, and drawing them apart is
7206
+ * what makes them incomparable: the eye has to carry the scale between two figures instead of
7207
+ * reading them against a shared one. `media/title` had exactly that, two `f.score` blocks of
7208
+ * `max: 10` held apart inside an `f.compare`, until this took the restriction off.
7209
+ *
7210
+ * NOT `multiple`. A multiple field is an anonymous array; these are NAMED sources, and the name
7211
+ * is what says which reading came from where. The two are different shapes and the block wants
7212
+ * this one.
7073
7213
  *
7074
7214
  * @layer block
7075
7215
  * @base group
7076
7216
  * @prim —
7077
7217
  * @widget score
7078
- * @example f.score({ source: { value: f.real({ label: '…' }) }, max: 100, verdict: { verdict: f.string({ label: '…' }) } })
7218
+ * @example f.score({ source: { imdb: f.real({ label: '…' }), tmdb: f.real({ label: '…' }) }, max: 10 })
7079
7219
  */
7080
7220
  function score(opts) {
7081
- const source = slot("score", "source", opts.source);
7221
+ const names = Object.keys(opts.source ?? {});
7222
+ if (names.length === 0) throw new Error(`[field.score] slot 'source' expects at least one entry, got 0`);
7082
7223
  const verdict = opts.verdict !== void 0 ? slot("score", "verdict", opts.verdict) : void 0;
7083
7224
  return {
7084
7225
  ...group({
7085
7226
  label: opts.label,
7086
- fields: merge("score", source, verdict),
7227
+ fields: merge("score", opts.source, verdict),
7087
7228
  ui: { kind: "score" }
7088
7229
  }),
7089
7230
  view: {
7090
7231
  kind: "score",
7091
- source: Object.keys(source)[0],
7232
+ source: names,
7092
7233
  max: opts.max,
7093
7234
  verdict: verdict && Object.keys(verdict)[0]
7094
7235
  }
@@ -7210,6 +7351,11 @@ function balance(opts) {
7210
7351
  * @base group
7211
7352
  * @prim —
7212
7353
  * @widget route
7354
+ * A `stub` is the part of the ticket that is TORN OFF and kept — a seat, a gate, a booking
7355
+ * reference. Declaring one makes the block a ticket rather than a line: the two halves are
7356
+ * separated by a perforation, and the fields in the stub sit below it. Without one the block
7357
+ * is exactly what it was, a route from here to there, so no existing call changes.
7358
+ *
7213
7359
  * @example f.route({ from: { from: f.string({ label: '…' }) }, to: { to: f.string({ label: '…' }) } })
7214
7360
  */
7215
7361
  function route(opts) {
@@ -7218,9 +7364,10 @@ function route(opts) {
7218
7364
  const depart = opts.depart !== void 0 ? slot("route", "depart", opts.depart) : void 0;
7219
7365
  const arrive = opts.arrive !== void 0 ? slot("route", "arrive", opts.arrive) : void 0;
7220
7366
  const duration = opts.duration !== void 0 ? slot("route", "duration", opts.duration) : void 0;
7367
+ const stub = opts.stub ?? {};
7221
7368
  return {
7222
7369
  ...group({
7223
- fields: merge("route", from, to, depart, arrive, duration),
7370
+ fields: merge("route", from, to, depart, arrive, duration, stub),
7224
7371
  ui: { kind: "route" }
7225
7372
  }),
7226
7373
  view: {
@@ -7229,7 +7376,8 @@ function route(opts) {
7229
7376
  to: Object.keys(to)[0],
7230
7377
  depart: depart && Object.keys(depart)[0],
7231
7378
  arrive: arrive && Object.keys(arrive)[0],
7232
- duration: duration && Object.keys(duration)[0]
7379
+ duration: duration && Object.keys(duration)[0],
7380
+ stub: Object.keys(stub)
7233
7381
  }
7234
7382
  };
7235
7383
  }
@@ -7407,6 +7555,81 @@ function specimen(opts) {
7407
7555
  };
7408
7556
  }
7409
7557
  /**
7558
+ * Measurements against the range they were supposed to fall in, rendered from a single
7559
+ * collection source — see `stampCollection`. `analyte`, `value`, `unit`, `low` and `high` are
7560
+ * part KEYS inside each collection row, NOT `LayoutEl` positions — the same reasoning as
7561
+ * `manifest`'s `quantity`/`item`.
7562
+ *
7563
+ * The point is the COMPARISON. A `f.table` over the same rows prints the bounds as two more
7564
+ * columns and leaves the reader to do it; here each value sits on its own band with the
7565
+ * reference span marked on it, so "outside the range" is seen rather than worked out. That is
7566
+ * also the only thing on the row worth a colour — it is what the reader has to act on.
7567
+ *
7568
+ * `unit`, `low` and `high` are optional: a measurement with no published range (a culture, a
7569
+ * description) still belongs in the same list and simply gets no band.
7570
+ *
7571
+ * @layer block
7572
+ * @base group
7573
+ * @prim —
7574
+ * @widget assay
7575
+ * @example f.assay({ source: { results: f.group({ scope: 'nest', multiple: true, fields: { analyte: f.string({ label: '…' }), value: f.real({ label: '…' }) } }) }, analyte: 'analyte', value: 'value' })
7576
+ */
7577
+ function assay(opts) {
7578
+ return stampCollection("assay", opts.source, opts.label, {
7579
+ kind: "assay",
7580
+ analyte: opts.analyte,
7581
+ value: opts.value,
7582
+ ...opts.unit ? { unit: opts.unit } : {},
7583
+ ...opts.low ? { low: opts.low } : {},
7584
+ ...opts.high ? { high: opts.high } : {}
7585
+ });
7586
+ }
7587
+ /**
7588
+ * Value moving from one place to another, where the MOVEMENT is the subject — not one more
7589
+ * labelled row among the record's fields. The sum is set large in the machine voice, and the two
7590
+ * ends read as a path beneath it.
7591
+ *
7592
+ * amount — what moved. The one required role, and the reason the block exists
7593
+ * from — where it left. Absent on money that only arrived
7594
+ * to — where it arrived. Absent on money that only left
7595
+ * meta — the record's own remaining fields, under a rule: date, category, reference
7596
+ *
7597
+ * An absent end is not drawn, and that is the whole reading: money that left an account and
7598
+ * arrived nowhere IS an expense. It is read off which ends the record actually has, never
7599
+ * guessed from the shape of a value. What the movement MEANS beyond that — whether this
7600
+ * particular kind of transfer is good news — is carried by the classifier's own option `tone`,
7601
+ * the same declaration every other coloured value in the product uses.
7602
+ *
7603
+ * Distinct from `f.route`, which also has two ends: there the journey is the subject and the
7604
+ * ends are places, so it carries times and a duration; here the ends are accounts and the
7605
+ * subject is the quantity.
7606
+ *
7607
+ * @layer block
7608
+ * @base group
7609
+ * @prim —
7610
+ * @widget flow
7611
+ * @example f.flow({ amount: { amount: f.money({ label: '…' }) }, from: { source: f.relation({ label: '…' }) } })
7612
+ */
7613
+ function flow(opts) {
7614
+ const amount = slot("flow", "amount", opts.amount);
7615
+ const from = opts.from !== void 0 ? slot("flow", "from", opts.from) : void 0;
7616
+ const to = opts.to !== void 0 ? slot("flow", "to", opts.to) : void 0;
7617
+ return {
7618
+ ...group({
7619
+ label: opts.label,
7620
+ fields: merge("flow", amount, from, to, opts.meta),
7621
+ ui: { kind: "flow" }
7622
+ }),
7623
+ view: {
7624
+ kind: "flow",
7625
+ amount: Object.keys(amount)[0],
7626
+ from: from && Object.keys(from)[0],
7627
+ to: to && Object.keys(to)[0],
7628
+ meta: Object.keys(opts.meta ?? {})
7629
+ }
7630
+ };
7631
+ }
7632
+ /**
7410
7633
  * A packing/cargo manifest rendered from a single collection source — see `stampCollection`.
7411
7634
  * `quantity` and `item` are part KEYS inside each collection row, NOT `LayoutEl` positions —
7412
7635
  * the same reasoning as `journal`'s `date`/`text`.
@@ -7453,13 +7676,25 @@ function table(opts) {
7453
7676
  } : {},
7454
7677
  ...opts.groupBy ? { groupBy: opts.groupBy } : {},
7455
7678
  ...opts.totals ? { totals: opts.totals } : {},
7679
+ ...opts.summary ? { summary: opts.summary } : {},
7456
7680
  ...opts.numbered ? { numbered: true } : {}
7457
7681
  });
7458
7682
  }
7459
7683
  /**
7460
- * An ID-card header: an optional overline, a prominent number, and trailing meta fields.
7461
- * `view` names each role (Task 5) see `masthead`'s own doc comment for what `meta` means
7462
- * as an ordered name list.
7684
+ * An identity document, drawn as the card it is: a caption and its number across the top, a
7685
+ * portrait beside the holder's own fields, the issuing details under a rule, and the
7686
+ * machine-readable strip at the foot. `view` names each role (Task 5) — see `masthead`'s own
7687
+ * doc comment for what an ordered name list means.
7688
+ *
7689
+ * overline — the caption at the top left ("PASSPORT", "DRIVING LICENCE")
7690
+ * number — the document's number, set apart at the top right
7691
+ * photo — the portrait. One field, rendered in a portrait frame beside the body
7692
+ * meta — the card's OWN fields, labelled, at each field's declared span
7693
+ * footer — the fields below the rule: issued, authority, record number
7694
+ * mrz — the machine-readable strip, set in monospace at the foot
7695
+ *
7696
+ * Every role is optional but `number`: a bank card has no `mrz`, a library card no `photo`,
7697
+ * and the card simply omits the part it was given nothing for.
7463
7698
  *
7464
7699
  * @layer block
7465
7700
  * @base group
@@ -7470,16 +7705,21 @@ function table(opts) {
7470
7705
  function idcard(opts) {
7471
7706
  const overline = opts.overline !== void 0 ? slot("idcard", "overline", opts.overline) : void 0;
7472
7707
  const number = slot("idcard", "number", opts.number);
7708
+ const photo = opts.photo !== void 0 ? slot("idcard", "photo", opts.photo) : void 0;
7709
+ const mrz = opts.mrz !== void 0 ? slot("idcard", "mrz", opts.mrz) : void 0;
7473
7710
  return {
7474
7711
  ...group({
7475
- fields: merge("idcard", overline, number, opts.meta),
7712
+ fields: merge("idcard", overline, number, photo, opts.meta, opts.footer, mrz),
7476
7713
  ui: { kind: "idcard" }
7477
7714
  }),
7478
7715
  view: {
7479
7716
  kind: "idcard",
7480
7717
  overline: overline && Object.keys(overline)[0],
7481
7718
  number: Object.keys(number)[0],
7482
- meta: Object.keys(opts.meta ?? {})
7719
+ photo: photo && Object.keys(photo)[0],
7720
+ meta: Object.keys(opts.meta ?? {}),
7721
+ footer: Object.keys(opts.footer ?? {}),
7722
+ mrz: mrz && Object.keys(mrz)[0]
7483
7723
  }
7484
7724
  };
7485
7725
  }
@@ -7564,6 +7804,8 @@ var blocks = {
7564
7804
  nutrition,
7565
7805
  specimen,
7566
7806
  manifest,
7807
+ assay,
7808
+ flow,
7567
7809
  table,
7568
7810
  idcard,
7569
7811
  properties,
@@ -8426,18 +8668,23 @@ function group(o) {
8426
8668
  for (const f of fields) if (!("key" in f && f.key !== void 0) && !hasChildren(f)) throw new Error(`[field.group] a 'nest' group's children must be fields or groups, not layout elements`);
8427
8669
  for (const k of r.unique ?? []) if (!fields.some((f) => "key" in f && f.key === k)) throw new Error(`[field.group] unique key '${k}' is not a subfield`);
8428
8670
  } else if (o.value !== void 0) throw new Error(`[field.group] value requires scope 'nest' — a hoist group owns no children to write`);
8671
+ const title = v.title ?? [];
8672
+ if (title.length > 0) {
8673
+ if (!o.multiple) throw new Error(`[field.group] ui.title heads a collection's ROWS — it needs multiple: true`);
8674
+ for (const name of title) if (!fields.some((f) => "key" in f && f.key === name)) throw new Error(`[field.group] ui.title '${name}' is not one of its fields`);
8675
+ }
8429
8676
  return {
8430
8677
  el: "group",
8431
8678
  scope,
8432
8679
  multiple: o.multiple,
8433
8680
  compute: o.value,
8434
- required: o.required,
8435
8681
  unique: r.unique,
8436
8682
  label: o.label,
8437
8683
  icon: o.icon,
8438
8684
  display: v.display ?? "wrap",
8439
8685
  kind: v.kind,
8440
8686
  fixed: r.fixed,
8687
+ view: title.length > 0 ? { title } : void 0,
8441
8688
  fields
8442
8689
  };
8443
8690
  }
@@ -8463,7 +8710,6 @@ function row(o) {
8463
8710
  icon: o.icon,
8464
8711
  fields: o.fields,
8465
8712
  multiple: o.multiple,
8466
- required: o.required,
8467
8713
  rules: o.rules,
8468
8714
  ui: { display: "scroll" }
8469
8715
  });
@@ -8511,7 +8757,6 @@ function url(o) {
8511
8757
  return group({
8512
8758
  scope: "nest",
8513
8759
  label: o.label,
8514
- required: o.required,
8515
8760
  ui: { kind: "url" },
8516
8761
  fields: {
8517
8762
  scheme: string({}),
@@ -8563,13 +8808,11 @@ function keyed(o) {
8563
8808
  }),
8564
8809
  scope: "nest",
8565
8810
  multiple: true,
8566
- required: o.required,
8567
8811
  unique: o.unique ?? [role],
8568
8812
  fixed: o.fixed,
8569
8813
  view: { by: role }
8570
8814
  };
8571
8815
  }
8572
- var isStorageGroup = (g) => g.scope === "nest";
8573
8816
  var isCollectionGroup = (g) => g.scope === "nest" && g.multiple === true;
8574
8817
  var isEmbeddedGroup = (g) => g.scope === "nest" && g.multiple !== true;
8575
8818
  /**
@@ -8607,6 +8850,31 @@ function info(textKey) {
8607
8850
  };
8608
8851
  }
8609
8852
  /**
8853
+ * What else points AT this record — the inverse of a relation, which is often the more useful
8854
+ * direction on a record page: a box is more usefully "what is in it" than "what it is in".
8855
+ *
8856
+ * Stores NOTHING and has no column: it declares where to look, and the server answers by asking
8857
+ * the pointing shelf. That is why it is a pseudo-element beside `divider` and `info` rather than
8858
+ * a field — there is no value here to validate or save.
8859
+ *
8860
+ * `from` names the pointing side explicitly (which shelf, and WHICH of its fields), never
8861
+ * "everything that happens to point here": a shelf may point at the same target through two
8862
+ * fields — a transaction has a source account and a destination account — and a panel that
8863
+ * merged them would answer a question nobody asked.
8864
+ *
8865
+ * @layer primitive
8866
+ * @prim —
8867
+ * @widget backrefs
8868
+ * @example f.backrefs({ label: 'mod.fields.storedHere', from: { library: 'things', shelf: 'item', field: 'location' } })
8869
+ */
8870
+ function backrefs(o) {
8871
+ return {
8872
+ el: "backrefs",
8873
+ label: o.label,
8874
+ from: o.from
8875
+ };
8876
+ }
8877
+ /**
8610
8878
  * Action button: invokes the handler registered in actionRegistry under the key `value`.
8611
8879
  *
8612
8880
  * @layer primitive
@@ -8662,7 +8930,7 @@ registerType("string", {
8662
8930
  widget: "text",
8663
8931
  build(o) {
8664
8932
  const cfg = o.config ?? {};
8665
- const min = cfg.min ?? (o.required === true ? 1 : 0);
8933
+ const min = cfg.min ?? 0;
8666
8934
  return {
8667
8935
  column: "text",
8668
8936
  zod: stringContent((str) => {
@@ -8690,9 +8958,9 @@ registerType("string", {
8690
8958
  function string(o) {
8691
8959
  return declare("string", o);
8692
8960
  }
8693
- /** Shared by localDir/localFile's build(): a server path, min-length-1 when required. */
8694
- function pathZod(o) {
8695
- return stringContent((s) => o.required === true ? s.min(1, { message: vmsg("min_length", { min: 1 }) }) : s);
8961
+ /** Shared by localDir/localFile's build(): a server path, judged only when one was entered. */
8962
+ function pathZod(_o) {
8963
+ return stringContent((s) => s);
8696
8964
  }
8697
8965
  registerType("localDir", {
8698
8966
  kind: "localDir",
@@ -8931,20 +9199,22 @@ registerType("time", {
8931
9199
  prim: "time",
8932
9200
  widget: "time",
8933
9201
  build(o) {
8934
- const granularity = o.config?.granularity ?? "second";
9202
+ const granularity = o.config?.granularity ?? "minute";
9203
+ const s = string$1(reqErr()).regex({
9204
+ hour: /^([01]\d|2[0-3])$/,
9205
+ minute: /^([01]\d|2[0-3]):[0-5]\d(?::[0-5]\d)?$/,
9206
+ second: /^([01]\d|2[0-3]):[0-5]\d:[0-5]\d$/
9207
+ }[granularity], { message: vmsg("time_format") });
8935
9208
  return {
8936
9209
  column: "time",
8937
- zod: string$1(reqErr()).regex({
8938
- hour: /^([01]\d|2[0-3])$/,
8939
- minute: /^([01]\d|2[0-3]):[0-5]\d$/,
8940
- second: /^([01]\d|2[0-3]):[0-5]\d:[0-5]\d$/
8941
- }[granularity], { message: vmsg("time_format") }),
9210
+ zod: granularity === "minute" ? s.transform((v) => v.slice(0, 5)) : s,
8942
9211
  hints: { granularity }
8943
9212
  };
8944
9213
  }
8945
9214
  });
8946
9215
  /**
8947
- * Time-of-day value with configurable granularity ('hour' | 'minute' | 'second', default 'second').
9216
+ * Time-of-day value with configurable granularity ('hour' | 'minute' | 'second', default
9217
+ * 'minute' — the same default `f.datetime` has always had).
8948
9218
  *
8949
9219
  * @layer primitive
8950
9220
  * @prim time
@@ -9349,10 +9619,9 @@ var MEASURED_ROLES = (opts) => {
9349
9619
  accepts: ENUMERATED,
9350
9620
  normalize: (d) => {
9351
9621
  if (d && accepts(unitRole, d)) return d;
9352
- const { label, required, value } = d?.opts ?? {};
9622
+ const { label, value } = d?.opts ?? {};
9353
9623
  return declare("string", {
9354
9624
  label,
9355
- required,
9356
9625
  value,
9357
9626
  noSearch: d?.opts.noSearch,
9358
9627
  ui: uiOf(d?.opts),
@@ -9511,7 +9780,7 @@ registerType("geo", {
9511
9780
  label: string$1().nullish()
9512
9781
  }),
9513
9782
  structureCode: "geo_structure",
9514
- absenceRoles: ["lat", "lng"]
9783
+ acceptsEmptyRow: true
9515
9784
  });
9516
9785
  /**
9517
9786
  * Geographic point composite {lat, lng, label?}; lat/lng validated to valid coordinate
@@ -9589,7 +9858,7 @@ registerType("illustrated", {
9589
9858
  roles: ILLUSTRATED_ROLES,
9590
9859
  roleZod: () => defaultRoleZod(ILLUSTRATED_ROLES),
9591
9860
  structureCode: "illustrated_structure",
9592
- absenceRoles: ["text", "image"],
9861
+ acceptsEmptyRow: true,
9593
9862
  collectionView: "illustratedList",
9594
9863
  hints: (o) => o.config ? { config: o.config } : {}
9595
9864
  });
@@ -9633,7 +9902,7 @@ registerType("attachment", {
9633
9902
  roles: ATTACHMENT_ROLES,
9634
9903
  roleZod: () => defaultRoleZod(ATTACHMENT_ROLES),
9635
9904
  structureCode: "attachment_structure",
9636
- absenceRoles: ["file"],
9905
+ acceptsEmptyRow: true,
9637
9906
  collectionView: "attachmentList"
9638
9907
  });
9639
9908
  /**
@@ -9734,11 +10003,10 @@ function periodRole(key, label, mkSubDecl) {
9734
10003
  if (d && accepts(def, d)) return d;
9735
10004
  const canonical = mkSubDecl(label);
9736
10005
  if (!d) return canonical;
9737
- const { label: l, required, value } = d.opts;
10006
+ const { label: l, value } = d.opts;
9738
10007
  return declare(canonical.factory, {
9739
10008
  ...canonical.opts,
9740
10009
  label: l ?? canonical.opts.label,
9741
- required,
9742
10010
  value,
9743
10011
  noSearch: d.opts.noSearch,
9744
10012
  ui: uiOf(d.opts)
@@ -10022,8 +10290,7 @@ function keyValue(raw) {
10022
10290
  type: valueType
10023
10291
  }
10024
10292
  },
10025
- by: "key",
10026
- required: o.required
10293
+ by: "key"
10027
10294
  });
10028
10295
  }
10029
10296
  /** Roles of a range: the two endpoints, integer or real depending on `isInt`. */
@@ -10189,7 +10456,8 @@ var PRIMITIVES = {
10189
10456
  url,
10190
10457
  divider,
10191
10458
  info,
10192
- button
10459
+ button,
10460
+ backrefs
10193
10461
  };
10194
10462
  /** Assembles `f`, guaranteeing no preset/block shadows a primitive. */
10195
10463
  function composeF(presets, blocks) {
@@ -10209,12 +10477,11 @@ var field = new Proxy({}, {
10209
10477
  has: (_t, k) => k in composedField()
10210
10478
  });
10211
10479
  function toClient(field) {
10212
- const { kind, label, agent, required, prim, hints, options, strict, relation, json, hidden, derived, parts, cache } = field;
10480
+ const { kind, label, agent, prim, hints, options, strict, relation, json, hidden, derived, parts, cache } = field;
10213
10481
  return {
10214
10482
  kind,
10215
10483
  label,
10216
10484
  ...agent !== void 0 && { agent },
10217
- required,
10218
10485
  prim,
10219
10486
  hints,
10220
10487
  options,
@@ -10238,680 +10505,6 @@ function defineSettings(s) {
10238
10505
  return materializeDef(s);
10239
10506
  }
10240
10507
  //#endregion
10241
- //#region ../../node_modules/sift/es5m/index.js
10242
- /******************************************************************************
10243
- Copyright (c) Microsoft Corporation.
10244
-
10245
- Permission to use, copy, modify, and/or distribute this software for any
10246
- purpose with or without fee is hereby granted.
10247
-
10248
- THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10249
- REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10250
- AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
10251
- INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
10252
- LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
10253
- OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
10254
- PERFORMANCE OF THIS SOFTWARE.
10255
- ***************************************************************************** */
10256
- var extendStatics = function(d, b) {
10257
- extendStatics = Object.setPrototypeOf || { __proto__: [] } instanceof Array && function(d, b) {
10258
- d.__proto__ = b;
10259
- } || function(d, b) {
10260
- for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p];
10261
- };
10262
- return extendStatics(d, b);
10263
- };
10264
- function __extends(d, b) {
10265
- if (typeof b !== "function" && b !== null) throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
10266
- extendStatics(d, b);
10267
- function __() {
10268
- this.constructor = d;
10269
- }
10270
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
10271
- }
10272
- var typeChecker = function(type) {
10273
- var typeString = "[object " + type + "]";
10274
- return function(value) {
10275
- return getClassName(value) === typeString;
10276
- };
10277
- };
10278
- var getClassName = function(value) {
10279
- return Object.prototype.toString.call(value);
10280
- };
10281
- var comparable = function(value) {
10282
- if (value instanceof Date) return value.getTime();
10283
- else if (isArray(value)) return value.map(comparable);
10284
- else if (value && typeof value.toJSON === "function") return value.toJSON();
10285
- return value;
10286
- };
10287
- var coercePotentiallyNull = function(value) {
10288
- return value == null ? null : value;
10289
- };
10290
- var isArray = typeChecker("Array");
10291
- var isObject = typeChecker("Object");
10292
- var isFunction = typeChecker("Function");
10293
- var isProperty = function(item, key) {
10294
- return item.hasOwnProperty(key) && !isFunction(item[key]);
10295
- };
10296
- var isVanillaObject = function(value) {
10297
- return value && (value.constructor === Object || value.constructor === Array || value.constructor.toString() === "function Object() { [native code] }" || value.constructor.toString() === "function Array() { [native code] }") && !value.toJSON;
10298
- };
10299
- var equals = function(a, b) {
10300
- if (a == null && a == b) return true;
10301
- if (a === b) return true;
10302
- if (Object.prototype.toString.call(a) !== Object.prototype.toString.call(b)) return false;
10303
- if (isArray(a)) {
10304
- if (a.length !== b.length) return false;
10305
- for (var i = 0, length_1 = a.length; i < length_1; i++) if (!equals(a[i], b[i])) return false;
10306
- return true;
10307
- } else if (isObject(a)) {
10308
- if (Object.keys(a).length !== Object.keys(b).length) return false;
10309
- for (var key in a) if (!equals(a[key], b[key])) return false;
10310
- return true;
10311
- }
10312
- return false;
10313
- };
10314
- /**
10315
- * Walks through each value given the context - used for nested operations. E.g:
10316
- * { "person.address": { $eq: "blarg" }}
10317
- */
10318
- var walkKeyPathValues = function(item, keyPath, next, depth, key, owner) {
10319
- var currentKey = keyPath[depth];
10320
- if (isArray(item) && isNaN(Number(currentKey)) && !isProperty(item, currentKey)) {
10321
- for (var i = 0, length_1 = item.length; i < length_1; i++) if (!walkKeyPathValues(item[i], keyPath, next, depth, i, item)) return false;
10322
- }
10323
- if (depth === keyPath.length || item == null) return next(item, key, owner, depth === 0, depth === keyPath.length);
10324
- return walkKeyPathValues(item[currentKey], keyPath, next, depth + 1, currentKey, item);
10325
- };
10326
- var BaseOperation = function() {
10327
- function BaseOperation(params, owneryQuery, options, name) {
10328
- this.params = params;
10329
- this.owneryQuery = owneryQuery;
10330
- this.options = options;
10331
- this.name = name;
10332
- this.init();
10333
- }
10334
- BaseOperation.prototype.init = function() {};
10335
- BaseOperation.prototype.reset = function() {
10336
- this.done = false;
10337
- this.keep = false;
10338
- };
10339
- return BaseOperation;
10340
- }();
10341
- var GroupOperation = function(_super) {
10342
- __extends(GroupOperation, _super);
10343
- function GroupOperation(params, owneryQuery, options, children) {
10344
- var _this = _super.call(this, params, owneryQuery, options) || this;
10345
- _this.children = children;
10346
- return _this;
10347
- }
10348
- /**
10349
- */
10350
- GroupOperation.prototype.reset = function() {
10351
- this.keep = false;
10352
- this.done = false;
10353
- for (var i = 0, length_2 = this.children.length; i < length_2; i++) this.children[i].reset();
10354
- };
10355
- /**
10356
- */
10357
- GroupOperation.prototype.childrenNext = function(item, key, owner, root, leaf) {
10358
- var done = true;
10359
- var keep = true;
10360
- for (var i = 0, length_3 = this.children.length; i < length_3; i++) {
10361
- var childOperation = this.children[i];
10362
- if (!childOperation.done) childOperation.next(item, key, owner, root, leaf);
10363
- if (!childOperation.keep) keep = false;
10364
- if (childOperation.done) {
10365
- if (!childOperation.keep) break;
10366
- } else done = false;
10367
- }
10368
- this.done = done;
10369
- this.keep = keep;
10370
- };
10371
- return GroupOperation;
10372
- }(BaseOperation);
10373
- var NamedGroupOperation = function(_super) {
10374
- __extends(NamedGroupOperation, _super);
10375
- function NamedGroupOperation(params, owneryQuery, options, children, name) {
10376
- var _this = _super.call(this, params, owneryQuery, options, children) || this;
10377
- _this.name = name;
10378
- return _this;
10379
- }
10380
- return NamedGroupOperation;
10381
- }(GroupOperation);
10382
- var QueryOperation = function(_super) {
10383
- __extends(QueryOperation, _super);
10384
- function QueryOperation() {
10385
- var _this = _super !== null && _super.apply(this, arguments) || this;
10386
- _this.propop = true;
10387
- return _this;
10388
- }
10389
- /**
10390
- */
10391
- QueryOperation.prototype.next = function(item, key, parent, root) {
10392
- this.childrenNext(item, key, parent, root);
10393
- };
10394
- return QueryOperation;
10395
- }(GroupOperation);
10396
- var NestedOperation = function(_super) {
10397
- __extends(NestedOperation, _super);
10398
- function NestedOperation(keyPath, params, owneryQuery, options, children) {
10399
- var _this = _super.call(this, params, owneryQuery, options, children) || this;
10400
- _this.keyPath = keyPath;
10401
- _this.propop = true;
10402
- /**
10403
- */
10404
- _this._nextNestedValue = function(value, key, owner, root, leaf) {
10405
- _this.childrenNext(value, key, owner, root, leaf);
10406
- return !_this.done;
10407
- };
10408
- return _this;
10409
- }
10410
- /**
10411
- */
10412
- NestedOperation.prototype.next = function(item, key, parent) {
10413
- walkKeyPathValues(item, this.keyPath, this._nextNestedValue, 0, key, parent);
10414
- };
10415
- return NestedOperation;
10416
- }(GroupOperation);
10417
- var createTester = function(a, compare) {
10418
- if (a instanceof Function) return a;
10419
- if (a instanceof RegExp) return function(b) {
10420
- var result = typeof b === "string" && a.test(b);
10421
- a.lastIndex = 0;
10422
- return result;
10423
- };
10424
- var comparableA = comparable(a);
10425
- return function(b) {
10426
- return compare(comparableA, comparable(b));
10427
- };
10428
- };
10429
- var EqualsOperation = function(_super) {
10430
- __extends(EqualsOperation, _super);
10431
- function EqualsOperation() {
10432
- var _this = _super !== null && _super.apply(this, arguments) || this;
10433
- _this.propop = true;
10434
- return _this;
10435
- }
10436
- EqualsOperation.prototype.init = function() {
10437
- this._test = createTester(this.params, this.options.compare);
10438
- };
10439
- EqualsOperation.prototype.next = function(item, key, parent) {
10440
- if (!Array.isArray(parent) || parent.hasOwnProperty(key)) {
10441
- if (this._test(item, key, parent)) {
10442
- this.done = true;
10443
- this.keep = true;
10444
- }
10445
- }
10446
- };
10447
- return EqualsOperation;
10448
- }(BaseOperation);
10449
- var numericalOperationCreator = function(createNumericalOperation) {
10450
- return function(params, owneryQuery, options, name) {
10451
- return createNumericalOperation(params, owneryQuery, options, name);
10452
- };
10453
- };
10454
- var numericalOperation = function(createTester) {
10455
- return numericalOperationCreator(function(params, owneryQuery, options, name) {
10456
- var typeofParams = typeof comparable(params);
10457
- var test = createTester(params);
10458
- return new EqualsOperation(function(b) {
10459
- var actualValue = coercePotentiallyNull(b);
10460
- return typeof comparable(actualValue) === typeofParams && test(actualValue);
10461
- }, owneryQuery, options, name);
10462
- });
10463
- };
10464
- var createNamedOperation = function(name, params, parentQuery, options) {
10465
- var operationCreator = options.operations[name];
10466
- if (!operationCreator) throwUnsupportedOperation(name);
10467
- return operationCreator(params, parentQuery, options, name);
10468
- };
10469
- var throwUnsupportedOperation = function(name) {
10470
- throw new Error("Unsupported operation: ".concat(name));
10471
- };
10472
- var containsOperation = function(query, options) {
10473
- for (var key in query) if (options.operations.hasOwnProperty(key) || key.charAt(0) === "$") return true;
10474
- return false;
10475
- };
10476
- var createNestedOperation = function(keyPath, nestedQuery, parentKey, owneryQuery, options) {
10477
- if (containsOperation(nestedQuery, options)) {
10478
- var _a = createQueryOperations(nestedQuery, parentKey, options), selfOperations = _a[0];
10479
- if (_a[1].length) throw new Error("Property queries must contain only operations, or exact objects.");
10480
- return new NestedOperation(keyPath, nestedQuery, owneryQuery, options, selfOperations);
10481
- }
10482
- return new NestedOperation(keyPath, nestedQuery, owneryQuery, options, [new EqualsOperation(nestedQuery, owneryQuery, options)]);
10483
- };
10484
- var createQueryOperation = function(query, owneryQuery, _a) {
10485
- if (owneryQuery === void 0) owneryQuery = null;
10486
- var _b = _a === void 0 ? {} : _a, compare = _b.compare, operations = _b.operations;
10487
- var options = {
10488
- compare: compare || equals,
10489
- operations: Object.assign({}, operations || {})
10490
- };
10491
- var _c = createQueryOperations(query, null, options), selfOperations = _c[0], nestedOperations = _c[1];
10492
- var ops = [];
10493
- if (selfOperations.length) ops.push(new NestedOperation([], query, owneryQuery, options, selfOperations));
10494
- ops.push.apply(ops, nestedOperations);
10495
- if (ops.length === 1) return ops[0];
10496
- return new QueryOperation(query, owneryQuery, options, ops);
10497
- };
10498
- var createQueryOperations = function(query, parentKey, options) {
10499
- var selfOperations = [];
10500
- var nestedOperations = [];
10501
- if (!isVanillaObject(query)) {
10502
- selfOperations.push(new EqualsOperation(query, query, options));
10503
- return [selfOperations, nestedOperations];
10504
- }
10505
- for (var key in query) if (options.operations.hasOwnProperty(key)) {
10506
- var op = createNamedOperation(key, query[key], query, options);
10507
- if (op) {
10508
- if (!op.propop && parentKey && !options.operations[parentKey]) throw new Error("Malformed query. ".concat(key, " cannot be matched against property."));
10509
- }
10510
- if (op != null) selfOperations.push(op);
10511
- } else if (key.charAt(0) === "$") throwUnsupportedOperation(key);
10512
- else nestedOperations.push(createNestedOperation(key.split("."), query[key], key, query, options));
10513
- return [selfOperations, nestedOperations];
10514
- };
10515
- var createOperationTester = function(operation) {
10516
- return function(item, key, owner) {
10517
- operation.reset();
10518
- operation.next(item, key, owner);
10519
- return operation.keep;
10520
- };
10521
- };
10522
- var $Ne = function(_super) {
10523
- __extends($Ne, _super);
10524
- function $Ne() {
10525
- var _this = _super !== null && _super.apply(this, arguments) || this;
10526
- _this.propop = true;
10527
- return _this;
10528
- }
10529
- $Ne.prototype.init = function() {
10530
- this._test = createTester(this.params, this.options.compare);
10531
- };
10532
- $Ne.prototype.reset = function() {
10533
- _super.prototype.reset.call(this);
10534
- this.keep = true;
10535
- };
10536
- $Ne.prototype.next = function(item) {
10537
- if (this._test(item)) {
10538
- this.done = true;
10539
- this.keep = false;
10540
- }
10541
- };
10542
- return $Ne;
10543
- }(BaseOperation);
10544
- var $ElemMatch = function(_super) {
10545
- __extends($ElemMatch, _super);
10546
- function $ElemMatch() {
10547
- var _this = _super !== null && _super.apply(this, arguments) || this;
10548
- _this.propop = true;
10549
- return _this;
10550
- }
10551
- $ElemMatch.prototype.init = function() {
10552
- if (!this.params || typeof this.params !== "object") throw new Error("Malformed query. $elemMatch must by an object.");
10553
- this._queryOperation = createQueryOperation(this.params, this.owneryQuery, this.options);
10554
- };
10555
- $ElemMatch.prototype.reset = function() {
10556
- _super.prototype.reset.call(this);
10557
- this._queryOperation.reset();
10558
- };
10559
- $ElemMatch.prototype.next = function(item) {
10560
- if (isArray(item)) {
10561
- for (var i = 0, length_1 = item.length; i < length_1; i++) {
10562
- this._queryOperation.reset();
10563
- var child = item[i];
10564
- this._queryOperation.next(child, i, item, false);
10565
- this.keep = this.keep || this._queryOperation.keep;
10566
- }
10567
- this.done = true;
10568
- } else {
10569
- this.done = false;
10570
- this.keep = false;
10571
- }
10572
- };
10573
- return $ElemMatch;
10574
- }(BaseOperation);
10575
- var $Not = function(_super) {
10576
- __extends($Not, _super);
10577
- function $Not() {
10578
- var _this = _super !== null && _super.apply(this, arguments) || this;
10579
- _this.propop = true;
10580
- return _this;
10581
- }
10582
- $Not.prototype.init = function() {
10583
- this._queryOperation = createQueryOperation(this.params, this.owneryQuery, this.options);
10584
- };
10585
- $Not.prototype.reset = function() {
10586
- _super.prototype.reset.call(this);
10587
- this._queryOperation.reset();
10588
- };
10589
- $Not.prototype.next = function(item, key, owner, root) {
10590
- this._queryOperation.next(item, key, owner, root);
10591
- this.done = this._queryOperation.done;
10592
- this.keep = !this._queryOperation.keep;
10593
- };
10594
- return $Not;
10595
- }(BaseOperation);
10596
- var $Size = function(_super) {
10597
- __extends($Size, _super);
10598
- function $Size() {
10599
- var _this = _super !== null && _super.apply(this, arguments) || this;
10600
- _this.propop = true;
10601
- return _this;
10602
- }
10603
- $Size.prototype.init = function() {};
10604
- $Size.prototype.next = function(item) {
10605
- if (isArray(item) && item.length === this.params) {
10606
- this.done = true;
10607
- this.keep = true;
10608
- }
10609
- };
10610
- return $Size;
10611
- }(BaseOperation);
10612
- var assertGroupNotEmpty = function(values) {
10613
- if (values.length === 0) throw new Error("$and/$or/$nor must be a nonempty array");
10614
- };
10615
- var $Or = function(_super) {
10616
- __extends($Or, _super);
10617
- function $Or() {
10618
- var _this = _super !== null && _super.apply(this, arguments) || this;
10619
- _this.propop = false;
10620
- return _this;
10621
- }
10622
- $Or.prototype.init = function() {
10623
- var _this = this;
10624
- assertGroupNotEmpty(this.params);
10625
- this._ops = this.params.map(function(op) {
10626
- return createQueryOperation(op, null, _this.options);
10627
- });
10628
- };
10629
- $Or.prototype.reset = function() {
10630
- this.done = false;
10631
- this.keep = false;
10632
- for (var i = 0, length_2 = this._ops.length; i < length_2; i++) this._ops[i].reset();
10633
- };
10634
- $Or.prototype.next = function(item, key, owner) {
10635
- var done = false;
10636
- var success = false;
10637
- for (var i = 0, length_3 = this._ops.length; i < length_3; i++) {
10638
- var op = this._ops[i];
10639
- op.next(item, key, owner);
10640
- if (op.keep) {
10641
- done = true;
10642
- success = op.keep;
10643
- break;
10644
- }
10645
- }
10646
- this.keep = success;
10647
- this.done = done;
10648
- };
10649
- return $Or;
10650
- }(BaseOperation);
10651
- var $Nor = function(_super) {
10652
- __extends($Nor, _super);
10653
- function $Nor() {
10654
- var _this = _super !== null && _super.apply(this, arguments) || this;
10655
- _this.propop = false;
10656
- return _this;
10657
- }
10658
- $Nor.prototype.next = function(item, key, owner) {
10659
- _super.prototype.next.call(this, item, key, owner);
10660
- this.keep = !this.keep;
10661
- };
10662
- return $Nor;
10663
- }($Or);
10664
- var $In = function(_super) {
10665
- __extends($In, _super);
10666
- function $In() {
10667
- var _this = _super !== null && _super.apply(this, arguments) || this;
10668
- _this.propop = true;
10669
- return _this;
10670
- }
10671
- $In.prototype.init = function() {
10672
- var _this = this;
10673
- var params = Array.isArray(this.params) ? this.params : [this.params];
10674
- this._testers = params.map(function(value) {
10675
- if (containsOperation(value, _this.options)) throw new Error("cannot nest $ under ".concat(_this.name.toLowerCase()));
10676
- return createTester(value, _this.options.compare);
10677
- });
10678
- };
10679
- $In.prototype.next = function(item, key, owner) {
10680
- var done = false;
10681
- var success = false;
10682
- for (var i = 0, length_4 = this._testers.length; i < length_4; i++) {
10683
- var test = this._testers[i];
10684
- if (test(item)) {
10685
- done = true;
10686
- success = true;
10687
- break;
10688
- }
10689
- }
10690
- this.keep = success;
10691
- this.done = done;
10692
- };
10693
- return $In;
10694
- }(BaseOperation);
10695
- var $Nin = function(_super) {
10696
- __extends($Nin, _super);
10697
- function $Nin(params, ownerQuery, options, name) {
10698
- var _this = _super.call(this, params, ownerQuery, options, name) || this;
10699
- _this.propop = true;
10700
- _this._in = new $In(params, ownerQuery, options, name);
10701
- return _this;
10702
- }
10703
- $Nin.prototype.next = function(item, key, owner, root) {
10704
- this._in.next(item, key, owner);
10705
- if (isArray(owner) && !root) {
10706
- if (this._in.keep) {
10707
- this.keep = false;
10708
- this.done = true;
10709
- } else if (key == owner.length - 1) {
10710
- this.keep = true;
10711
- this.done = true;
10712
- }
10713
- } else {
10714
- this.keep = !this._in.keep;
10715
- this.done = true;
10716
- }
10717
- };
10718
- $Nin.prototype.reset = function() {
10719
- _super.prototype.reset.call(this);
10720
- this._in.reset();
10721
- };
10722
- return $Nin;
10723
- }(BaseOperation);
10724
- var $Exists = function(_super) {
10725
- __extends($Exists, _super);
10726
- function $Exists() {
10727
- var _this = _super !== null && _super.apply(this, arguments) || this;
10728
- _this.propop = true;
10729
- return _this;
10730
- }
10731
- $Exists.prototype.next = function(item, key, owner, root, leaf) {
10732
- if (!leaf) {
10733
- this.done = true;
10734
- this.keep = !this.params;
10735
- } else if (owner.hasOwnProperty(key) === this.params) {
10736
- this.done = true;
10737
- this.keep = true;
10738
- }
10739
- };
10740
- return $Exists;
10741
- }(BaseOperation);
10742
- var $And = function(_super) {
10743
- __extends($And, _super);
10744
- function $And(params, owneryQuery, options, name) {
10745
- var _this = _super.call(this, params, owneryQuery, options, params.map(function(query) {
10746
- return createQueryOperation(query, owneryQuery, options);
10747
- }), name) || this;
10748
- _this.propop = false;
10749
- assertGroupNotEmpty(params);
10750
- return _this;
10751
- }
10752
- $And.prototype.next = function(item, key, owner, root) {
10753
- this.childrenNext(item, key, owner, root);
10754
- };
10755
- return $And;
10756
- }(NamedGroupOperation);
10757
- var $All = function(_super) {
10758
- __extends($All, _super);
10759
- function $All(params, owneryQuery, options, name) {
10760
- var _this = _super.call(this, params, owneryQuery, options, params.map(function(query) {
10761
- return createQueryOperation(query, owneryQuery, options);
10762
- }), name) || this;
10763
- _this.propop = true;
10764
- return _this;
10765
- }
10766
- $All.prototype.next = function(item, key, owner, root) {
10767
- this.childrenNext(item, key, owner, root);
10768
- };
10769
- return $All;
10770
- }(NamedGroupOperation);
10771
- var $eq = function(params, owneryQuery, options) {
10772
- return new EqualsOperation(params, owneryQuery, options);
10773
- };
10774
- var $ne = function(params, owneryQuery, options, name) {
10775
- return new $Ne(params, owneryQuery, options, name);
10776
- };
10777
- var $or = function(params, owneryQuery, options, name) {
10778
- return new $Or(params, owneryQuery, options, name);
10779
- };
10780
- var $nor = function(params, owneryQuery, options, name) {
10781
- return new $Nor(params, owneryQuery, options, name);
10782
- };
10783
- var $elemMatch = function(params, owneryQuery, options, name) {
10784
- return new $ElemMatch(params, owneryQuery, options, name);
10785
- };
10786
- var $nin = function(params, owneryQuery, options, name) {
10787
- return new $Nin(params, owneryQuery, options, name);
10788
- };
10789
- var $in = function(params, owneryQuery, options, name) {
10790
- return new $In(params, owneryQuery, options, name);
10791
- };
10792
- var $lt = numericalOperation(function(params) {
10793
- return function(b) {
10794
- return b != null && b < params;
10795
- };
10796
- });
10797
- var $lte = numericalOperation(function(params) {
10798
- return function(b) {
10799
- return b === params || b <= params;
10800
- };
10801
- });
10802
- var $gt = numericalOperation(function(params) {
10803
- return function(b) {
10804
- return b != null && b > params;
10805
- };
10806
- });
10807
- var $gte = numericalOperation(function(params) {
10808
- return function(b) {
10809
- return b === params || b >= params;
10810
- };
10811
- });
10812
- var $mod = function(_a, owneryQuery, options) {
10813
- var mod = _a[0], equalsValue = _a[1];
10814
- return new EqualsOperation(function(b) {
10815
- return comparable(b) % mod === equalsValue;
10816
- }, owneryQuery, options);
10817
- };
10818
- var $exists = function(params, owneryQuery, options, name) {
10819
- return new $Exists(params, owneryQuery, options, name);
10820
- };
10821
- var $regex = function(pattern, owneryQuery, options) {
10822
- return new EqualsOperation(new RegExp(pattern, owneryQuery.$options), owneryQuery, options);
10823
- };
10824
- var $not = function(params, owneryQuery, options, name) {
10825
- return new $Not(params, owneryQuery, options, name);
10826
- };
10827
- var typeAliases = {
10828
- number: function(v) {
10829
- return typeof v === "number";
10830
- },
10831
- string: function(v) {
10832
- return typeof v === "string";
10833
- },
10834
- bool: function(v) {
10835
- return typeof v === "boolean";
10836
- },
10837
- array: function(v) {
10838
- return Array.isArray(v);
10839
- },
10840
- null: function(v) {
10841
- return v === null;
10842
- },
10843
- timestamp: function(v) {
10844
- return v instanceof Date;
10845
- }
10846
- };
10847
- var $type = function(clazz, owneryQuery, options) {
10848
- return new EqualsOperation(function(b) {
10849
- if (typeof clazz === "string") {
10850
- if (!typeAliases[clazz]) throw new Error("Type alias does not exist");
10851
- return typeAliases[clazz](b);
10852
- }
10853
- return b != null ? b instanceof clazz || b.constructor === clazz : false;
10854
- }, owneryQuery, options);
10855
- };
10856
- var $and = function(params, ownerQuery, options, name) {
10857
- return new $And(params, ownerQuery, options, name);
10858
- };
10859
- var $all = function(params, ownerQuery, options, name) {
10860
- return new $All(params, ownerQuery, options, name);
10861
- };
10862
- var $size = function(params, ownerQuery, options) {
10863
- return new $Size(params, ownerQuery, options, "$size");
10864
- };
10865
- var $options = function() {
10866
- return null;
10867
- };
10868
- var $where = function(params, ownerQuery, options) {
10869
- var test;
10870
- if (isFunction(params)) test = params;
10871
- else if (!process.env.CSP_ENABLED) test = new Function("obj", "return " + params);
10872
- else throw new Error("In CSP mode, sift does not support strings in \"$where\" condition");
10873
- return new EqualsOperation(function(b) {
10874
- return test.bind(b)(b);
10875
- }, ownerQuery, options);
10876
- };
10877
- var defaultOperations = /*#__PURE__*/ Object.freeze({
10878
- __proto__: null,
10879
- $Size,
10880
- $all,
10881
- $and,
10882
- $elemMatch,
10883
- $eq,
10884
- $exists,
10885
- $gt,
10886
- $gte,
10887
- $in,
10888
- $lt,
10889
- $lte,
10890
- $mod,
10891
- $ne,
10892
- $nin,
10893
- $nor,
10894
- $not,
10895
- $options,
10896
- $or,
10897
- $regex,
10898
- $size,
10899
- $type,
10900
- $where
10901
- });
10902
- var createDefaultQueryOperation = function(query, ownerQuery, _a) {
10903
- var _b = _a === void 0 ? {} : _a, compare = _b.compare, operations = _b.operations;
10904
- return createQueryOperation(query, ownerQuery, {
10905
- compare,
10906
- operations: Object.assign({}, defaultOperations, operations || {})
10907
- });
10908
- };
10909
- var createDefaultQueryTester = function(query, options) {
10910
- if (options === void 0) options = {};
10911
- return createOperationTester(createDefaultQueryOperation(query, null, options));
10912
- };
10913
- createDefaultQueryTester.createEqualsOperation;
10914
- //#endregion
10915
10508
  //#region ../sdk/src/shelf.ts
10916
10509
  /** Flat [storageKey, FieldMeta] pairs of the parent table's columns.
10917
10510
  * layout group → flattened; embedded group → prefix `key__`; collection → skipped. */
@@ -10949,43 +10542,6 @@ function assertNoKeylessCompute(items, owner) {
10949
10542
  }
10950
10543
  }
10951
10544
  /**
10952
- * Warns (does not throw) about the ONE composite shape mixed ownership is still wrong for: a
10953
- * MANDATED client-owned stored part beside a server-owned one — one computed-and-stored (the
10954
- * server writes that column) next to a plain stored one (the client writes that one) that the
10955
- * declaration demands a value for.
10956
- *
10957
- * Mixed ownership itself is supported: the write path MATERIALIZES such a composite and
10958
- * computes the server's part even when the write does not carry the field, leaving the
10959
- * client's columns as they were (THE ABSENT-COMPOSITE RULE in
10960
- * `@coffer-org/server/part-injection`). What a MANDATE adds is a demand the materialized value
10961
- * cannot honour: the half-value the server produces on its own — the computed column filled,
10962
- * the client's empty — reads back as `{value: 5, unit: null}` and is rejected by the
10963
- * composite's own zod, yet it is non-null enough to satisfy the required check on the next
10964
- * PATCH. The write path therefore refuses to materialize that one combination, and this names
10965
- * it rather than leaving the author to wonder why the computed part never fills.
10966
- *
10967
- * The mandate is read through `mandatedClientParts`, which counts the FIELD's `required` AND
10968
- * each OVERRIDDEN FILLING's own — `partsRowShape` honours both, so a check that read only the
10969
- * field let `fields: { unit: f.string({ required: true }) }` through silently.
10970
- *
10971
- * Recurses into every group — an embedded group's composite and a collection row's carry
10972
- * exactly the same mandate as a top-level one.
10973
- */
10974
- function warnMixedOwnedParts(items, owner, path = "") {
10975
- for (const it of items) {
10976
- if (hasChildren(it)) {
10977
- warnMixedOwnedParts(it.fields, owner, isStorageGroup(it) ? `${path}${it.key}.` : path);
10978
- continue;
10979
- }
10980
- if (!(hasValue(it) && it.key !== void 0) || !it.type.parts) continue;
10981
- const mixed = mixedOwnedStoredParts(it.type.parts);
10982
- if (!mixed) continue;
10983
- const mandated = mandatedClientParts(it.type.required, it.type.parts);
10984
- if (!mandated.length) continue;
10985
- console.warn(`${owner}: composite '${path}${it.key}' mandates a client-owned stored part (${mandated.join(", ")}) and mixes it with a server-owned stored part (${mixed.server.join(", ")}) — a mandated part is the client's to declare, so the write path does NOT materialize this composite and the computed part stays absent until a client sends the field. Either drop \`required\` (from the field or from the ${mandated.join("/")} filling), or give every stored part a \`value\` (the composite becomes fully server-owned).`);
10986
- }
10987
- }
10988
- /**
10989
10545
  * The declaration-time checks that are about a FIELD LIST, not about a shelf: they read
10990
10546
  * `fields` and nothing else, so they are equally true of an extend's fields — an extend
10991
10547
  * stores composites in its own table through the very same write path (`upsertExtendRecord`
@@ -10999,7 +10555,6 @@ function warnMixedOwnedParts(items, owner, path = "") {
10999
10555
  */
11000
10556
  function checkFieldDeclaration(fields, owner) {
11001
10557
  assertNoKeylessCompute(fields, owner);
11002
- warnMixedOwnedParts(fields, owner);
11003
10558
  }
11004
10559
  function defineShelf(m) {
11005
10560
  const built = materializeDef(m);
@@ -11068,42 +10623,42 @@ var bus_route_default = defineShelf({
11068
10623
  ] },
11069
10624
  fields: {
11070
10625
  name: field.title({ label: "core.fields.name" }),
10626
+ /**
10627
+ * What this route IS, read before anything else: its number, its two ends and the
10628
+ * stop these timetables are measured at. They already drew as one row — the spans
10629
+ * said so — but below the description and with their labels above. A stats cell
10630
+ * inverts that and makes them the record's headline, which is what they are.
10631
+ */
10632
+ route: field.stats({ fields: {
10633
+ route_number: field.int({ label: "transit.bus_route.fields.route_number" }),
10634
+ from: field.string({
10635
+ label: "transit.bus_route.fields.from",
10636
+ rules: { max: 100 }
10637
+ }),
10638
+ to: field.string({
10639
+ label: "transit.bus_route.fields.to",
10640
+ rules: { max: 100 }
10641
+ }),
10642
+ stop: field.string({
10643
+ label: "transit.bus_route.fields.stop",
10644
+ rules: { max: 200 }
10645
+ })
10646
+ } }),
10647
+ hairline: field.divider({}),
11071
10648
  description: field.text({
11072
10649
  label: "transit.bus_route.fields.description",
11073
10650
  rules: { max: 1e3 }
11074
10651
  }),
11075
- route_number: field.int({
11076
- label: "transit.bus_route.fields.route_number",
11077
- ui: { span: 3 }
11078
- }),
11079
- from: field.string({
11080
- label: "transit.bus_route.fields.from",
11081
- rules: { max: 100 },
11082
- ui: { span: 3 }
11083
- }),
11084
- to: field.string({
11085
- label: "transit.bus_route.fields.to",
11086
- rules: { max: 100 },
11087
- ui: { span: 3 }
11088
- }),
11089
- stop: field.string({
11090
- label: "transit.bus_route.fields.stop",
11091
- rules: { max: 200 },
11092
- ui: { span: 3 }
11093
- }),
11094
- weekday_times: field.time({
10652
+ weekday_times: field.timetable({
11095
10653
  label: "transit.bus_route.fields.weekday_times",
11096
- multiple: true,
11097
10654
  ui: { span: 12 }
11098
10655
  }),
11099
- saturday_times: field.time({
10656
+ saturday_times: field.timetable({
11100
10657
  label: "transit.bus_route.fields.saturday_times",
11101
- multiple: true,
11102
10658
  ui: { span: 6 }
11103
10659
  }),
11104
- sunday_times: field.time({
10660
+ sunday_times: field.timetable({
11105
10661
  label: "transit.bus_route.fields.sunday_times",
11106
- multiple: true,
11107
10662
  ui: { span: 6 }
11108
10663
  }),
11109
10664
  tags: field.tags({ label: "transit.bus_route.fields.tags" }),
@@ -11119,6 +10674,8 @@ var src_default = definePlugin({
11119
10674
  id: "transit",
11120
10675
  version: "1.0.0",
11121
10676
  dependsOn: [],
10677
+ label: "transit.plugin.label",
10678
+ description: "transit.plugin.description",
11122
10679
  libraries: [{
11123
10680
  meta: defineLibrary({
11124
10681
  id: "transit",