@mulmoclaude/core 0.15.0 → 0.18.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/assets/helps/collection-skills.md +74 -13
- package/assets/helps/custom-view.md +28 -0
- package/assets/helps/egress-sync.md +158 -0
- package/assets/helps/index.md +1 -0
- package/dist/{calendarGrid-CaR-q36Y.js → calendarGrid-B3dtFN-m.js} +51 -11
- package/dist/calendarGrid-B3dtFN-m.js.map +1 -0
- package/dist/{calendarGrid-gN4sei7s.cjs → calendarGrid-CRaMAguM.cjs} +68 -10
- package/dist/calendarGrid-CRaMAguM.cjs.map +1 -0
- package/dist/collection/core/actionVisible.d.ts +15 -4
- package/dist/collection/core/backlinks.d.ts +20 -0
- package/dist/collection/core/mutateAction.d.ts +14 -0
- package/dist/collection/core/recordZ.d.ts +3 -2
- package/dist/collection/core/schema.d.ts +20 -10
- package/dist/collection/core/schemaZ.d.ts +177 -16
- package/dist/collection/core/uiTypes.d.ts +4 -0
- package/dist/collection/index.cjs +7 -3
- package/dist/collection/index.cjs.map +1 -1
- package/dist/collection/index.js +5 -4
- package/dist/collection/index.js.map +1 -1
- package/dist/collection/registry/server/index.cjs +1 -1
- package/dist/collection/registry/server/index.js +1 -1
- package/dist/collection/server/index.cjs +4 -2
- package/dist/collection/server/index.d.ts +1 -0
- package/dist/collection/server/index.js +3 -3
- package/dist/collection/server/mutate.d.ts +24 -0
- package/dist/collection/server/ontology.d.ts +1 -1
- package/dist/collection-watchers/index.cjs +2 -2
- package/dist/collection-watchers/index.js +2 -2
- package/dist/feeds/index.cjs +2 -2
- package/dist/feeds/index.js +2 -2
- package/dist/feeds/server/index.cjs +3 -3
- package/dist/feeds/server/index.js +3 -3
- package/dist/{ingestTypes-DG8RExWt.cjs → ingestTypes-Cq65Bv-N.cjs} +2 -2
- package/dist/{ingestTypes-DG8RExWt.cjs.map → ingestTypes-Cq65Bv-N.cjs.map} +1 -1
- package/dist/{ingestTypes-V4upKeBx.js → ingestTypes-DvsJhqMx.js} +2 -2
- package/dist/{ingestTypes-V4upKeBx.js.map → ingestTypes-DvsJhqMx.js.map} +1 -1
- package/dist/{server-BNTR7ySK.js → server-C3FOqabY.js} +204 -28
- package/dist/server-C3FOqabY.js.map +1 -0
- package/dist/{server-bsNrf2yW.cjs → server-Cd6HA_3-.cjs} +216 -28
- package/dist/server-Cd6HA_3-.cjs.map +1 -0
- package/package.json +1 -1
- package/dist/calendarGrid-CaR-q36Y.js.map +0 -1
- package/dist/calendarGrid-gN4sei7s.cjs.map +0 -1
- package/dist/server-BNTR7ySK.js.map +0 -1
- package/dist/server-bsNrf2yW.cjs.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_rolldown_runtime = require("./rolldown-runtime-D6vf50IK.cjs");
|
|
2
|
-
const require_calendarGrid = require("./calendarGrid-
|
|
2
|
+
const require_calendarGrid = require("./calendarGrid-CRaMAguM.cjs");
|
|
3
3
|
const require_collection_paths = require("./collection/paths.cjs");
|
|
4
4
|
let node_path = require("node:path");
|
|
5
5
|
node_path = require_rolldown_runtime.__toESM(node_path, 1);
|
|
@@ -648,15 +648,6 @@ function enforcedProblem(key, spec, value) {
|
|
|
648
648
|
if (!empty && spec.type === "enum" && !spec.values.includes(String(value))) return `'${key}' = '${String(value)}' is not one of [${spec.values.join(", ")}]`;
|
|
649
649
|
return null;
|
|
650
650
|
}
|
|
651
|
-
/** Numeric coercion for the strict `number`/`money` check: a plain number,
|
|
652
|
-
* or a non-blank numeric string (renderers coerce those via `Number(...)`,
|
|
653
|
-
* so they display fine). Anything else — arrays (`[]` stringifies to `""`
|
|
654
|
-
* = 0, `[42]` to `"42"`), booleans, objects — is NaN and gets flagged. */
|
|
655
|
-
function coerceNumeric(value) {
|
|
656
|
-
if (typeof value === "number") return value;
|
|
657
|
-
if (typeof value === "string" && value.trim() !== "") return Number(value);
|
|
658
|
-
return NaN;
|
|
659
|
-
}
|
|
660
651
|
/** Report-only per-type checks on a PRESENT value. Date / datetime reuse the
|
|
661
652
|
* calendar's STRICT civil parsers (`parseIsoDate` / `parseIsoDateTime`), so
|
|
662
653
|
* the lint flags exactly the values the calendar / trigger / spawn code
|
|
@@ -667,7 +658,7 @@ function coerceNumeric(value) {
|
|
|
667
658
|
function strictTypeProblem(key, spec, value) {
|
|
668
659
|
switch (spec.type) {
|
|
669
660
|
case "number":
|
|
670
|
-
case "money": return Number.isFinite(coerceNumeric(value)) ? null : `'${key}' = '${String(value)}' is not numeric (a '${spec.type}' field stores a plain number)`;
|
|
661
|
+
case "money": return Number.isFinite(require_calendarGrid.coerceNumeric(value)) ? null : `'${key}' = '${String(value)}' is not numeric (a '${spec.type}' field stores a plain number)`;
|
|
671
662
|
case "boolean": return value === true || value === false ? null : `'${key}' = '${String(value)}' is not a boolean (store true or false, unquoted)`;
|
|
672
663
|
case "date": return require_calendarGrid.parseIsoDate(value) !== null ? null : `'${key}' = '${String(value)}' is not a real YYYY-MM-DD date`;
|
|
673
664
|
case "datetime": return require_calendarGrid.parseIsoDateTime(value) !== null ? null : `'${key}' = '${String(value)}' is not a YYYY-MM-DDTHH:MM datetime (seconds optional, no timezone suffix — the shape the calendar parses)`;
|
|
@@ -824,6 +815,96 @@ function validateRecordObject(record, itemId, schema, tier = "enforced") {
|
|
|
824
815
|
if (typeof idValue !== "string" || idValue !== itemId) return `'${schema.primaryKey}' is '${String(idValue ?? "")}' but must equal the filename ('${itemId}'), or the record can't be opened`;
|
|
825
816
|
return firstRecordProblem(record, schema, tier);
|
|
826
817
|
}
|
|
818
|
+
/** The param name a `set` value references, or null when the value is a
|
|
819
|
+
* literal (non-strings can never be references). A bare/empty prefix
|
|
820
|
+
* (`"$params."`) returns the empty string — the schema refine rejects
|
|
821
|
+
* it as an undeclared param, never silently treats it as a literal. */
|
|
822
|
+
function paramRefName(value) {
|
|
823
|
+
if (typeof value !== "string" || !value.startsWith("$params.")) return null;
|
|
824
|
+
return value.slice(8);
|
|
825
|
+
}
|
|
826
|
+
/** Resolve a mutate action's `set` map against the submitted params:
|
|
827
|
+
* literals pass through, `$params.<name>` reads the param value. An
|
|
828
|
+
* ABSENT referenced param omits the key entirely (merge semantics —
|
|
829
|
+
* the stored value survives), mirroring how the record form omits
|
|
830
|
+
* empty optionals rather than writing empty strings. */
|
|
831
|
+
function resolveMutateSet(set, params) {
|
|
832
|
+
const resolved = {};
|
|
833
|
+
for (const [key, value] of Object.entries(set)) {
|
|
834
|
+
const ref = paramRefName(value);
|
|
835
|
+
if (ref === null) {
|
|
836
|
+
resolved[key] = value;
|
|
837
|
+
continue;
|
|
838
|
+
}
|
|
839
|
+
const paramValue = params[ref];
|
|
840
|
+
if (paramValue !== void 0 && paramValue !== null && paramValue !== "") resolved[key] = paramValue;
|
|
841
|
+
}
|
|
842
|
+
return resolved;
|
|
843
|
+
}
|
|
844
|
+
//#endregion
|
|
845
|
+
//#region src/collection/server/mutate.ts
|
|
846
|
+
/** First problem with the submitted params, or null. Every declared param
|
|
847
|
+
* is checked by the shared record-field validator; keys the action never
|
|
848
|
+
* declared are rejected outright — a stray key would otherwise ride the
|
|
849
|
+
* resolved `set` semantics silently. */
|
|
850
|
+
function firstMutateParamProblem(action, params) {
|
|
851
|
+
const declared = action.params ?? {};
|
|
852
|
+
for (const key of Object.keys(params)) if (declared[key] === void 0) return `unknown param '${key}' — not declared by action '${action.id}'`;
|
|
853
|
+
for (const [key, spec] of Object.entries(declared)) {
|
|
854
|
+
const problem = recordFieldProblem(key, spec, params[key], "strict");
|
|
855
|
+
if (problem) return problem;
|
|
856
|
+
}
|
|
857
|
+
return null;
|
|
858
|
+
}
|
|
859
|
+
/** Apply one mutate action to one record. Never throws for data-shaped
|
|
860
|
+
* failures — the outcome's `problem` doubles as agent feedback when an
|
|
861
|
+
* LLM (not the user) pressed the button. */
|
|
862
|
+
async function applyMutateAction(collection, action, itemId, params, opts = {}) {
|
|
863
|
+
const paramProblem = firstMutateParamProblem(action, params);
|
|
864
|
+
if (paramProblem) return {
|
|
865
|
+
ok: false,
|
|
866
|
+
status: "invalid-params",
|
|
867
|
+
problem: paramProblem
|
|
868
|
+
};
|
|
869
|
+
const existing = await readItem(collection.dataDir, itemId, opts);
|
|
870
|
+
if (!existing) return {
|
|
871
|
+
ok: false,
|
|
872
|
+
status: "not-found",
|
|
873
|
+
problem: `item '${itemId}' not found`
|
|
874
|
+
};
|
|
875
|
+
if (!require_calendarGrid.actionVisible(action, existing)) return {
|
|
876
|
+
ok: false,
|
|
877
|
+
status: "require-unmet",
|
|
878
|
+
problem: `action '${action.id}' is not available for item '${itemId}' in its current state`
|
|
879
|
+
};
|
|
880
|
+
const stored = Object.entries(existing).filter(([key]) => {
|
|
881
|
+
const spec = collection.schema.fields[key];
|
|
882
|
+
return !spec || !require_calendarGrid.COMPUTED_TYPES.has(spec.type);
|
|
883
|
+
});
|
|
884
|
+
const merged = {
|
|
885
|
+
...Object.fromEntries(stored),
|
|
886
|
+
...resolveMutateSet(action.set, params)
|
|
887
|
+
};
|
|
888
|
+
const invalid = validateRecordObject(merged, itemId, collection.schema);
|
|
889
|
+
if (invalid) return {
|
|
890
|
+
ok: false,
|
|
891
|
+
status: "invalid-record",
|
|
892
|
+
problem: invalid
|
|
893
|
+
};
|
|
894
|
+
const result = await writeItem(collection.dataDir, itemId, merged, {
|
|
895
|
+
workspaceRoot: opts.workspaceRoot,
|
|
896
|
+
slug: collection.slug
|
|
897
|
+
});
|
|
898
|
+
if (result.kind !== "ok") return {
|
|
899
|
+
ok: false,
|
|
900
|
+
status: "write-refused",
|
|
901
|
+
problem: `write refused (${result.kind})`
|
|
902
|
+
};
|
|
903
|
+
return {
|
|
904
|
+
ok: true,
|
|
905
|
+
item: merged
|
|
906
|
+
};
|
|
907
|
+
}
|
|
827
908
|
//#endregion
|
|
828
909
|
//#region src/collection/core/schemaZ.ts
|
|
829
910
|
/** Optional visibility predicate shared by actions and fields: the target
|
|
@@ -993,7 +1074,7 @@ var EmbedFieldZ = zod.z.object({
|
|
|
993
1074
|
message: "fields with type 'embed' must declare a `to` (valid collection slug) and exactly one of `id` (a fixed record's primary key) or `idField` (a sibling field naming the per-record target)",
|
|
994
1075
|
path: ["id"]
|
|
995
1076
|
});
|
|
996
|
-
/** Display-only REVERSE refs (plan step ② of plans/collection-ontology.md):
|
|
1077
|
+
/** Display-only REVERSE refs (plan step ② of plans/done/collection-ontology.md):
|
|
997
1078
|
* a read-only sub-table of the records in collection `from` whose `via`
|
|
998
1079
|
* ref field stores THIS record's primary key. Stores nothing (joins
|
|
999
1080
|
* `COMPUTED_TYPES`); resolution is shared server/client via
|
|
@@ -1011,6 +1092,30 @@ var BacklinksFieldZ = zod.z.object({
|
|
|
1011
1092
|
display: zod.z.array(zod.z.string().trim().min(1)).min(1),
|
|
1012
1093
|
filter: WhenZ.optional()
|
|
1013
1094
|
}).refine((spec) => require_calendarGrid.isSafeSlug(spec.from), slugMessage("from"));
|
|
1095
|
+
/** A cross-collection AGGREGATE over a backlink relation (plan step ⑤ of
|
|
1096
|
+
* plans/done/collection-ontology.md): a computed number — never stored — that
|
|
1097
|
+
* sums a source column (or counts rows) over the records in `from` whose
|
|
1098
|
+
* `via` ref points at this record. Same `from`/`via`/`filter` vocabulary
|
|
1099
|
+
* and reverse-loading machinery as `backlinks`; resolution shared
|
|
1100
|
+
* server/client via `core/backlinks.ts`'s `rollupValue`. Deliberately a
|
|
1101
|
+
* STRUCTURED field, not `sumOver(...)` formula syntax — the derived
|
|
1102
|
+
* evaluator's no-string-literals boundary stays untouched — and
|
|
1103
|
+
* deliberately just `sum` | `count`. Rollups resolve BEFORE the formula
|
|
1104
|
+
* pass, so a sibling `derived` formula may read them as identifiers
|
|
1105
|
+
* (`played = homePlayed + awayPlayed`). Fail-soft: an unresolvable
|
|
1106
|
+
* `from` renders em-dash; an empty match set is a real 0. */
|
|
1107
|
+
var RollupFieldZ = zod.z.object({
|
|
1108
|
+
type: zod.z.literal("rollup"),
|
|
1109
|
+
...fieldBase,
|
|
1110
|
+
from: zod.z.string().min(1),
|
|
1111
|
+
via: zod.z.string().trim().min(1),
|
|
1112
|
+
op: zod.z.enum(["sum", "count"]),
|
|
1113
|
+
column: zod.z.string().trim().min(1).optional(),
|
|
1114
|
+
filter: WhenZ.optional()
|
|
1115
|
+
}).refine((spec) => require_calendarGrid.isSafeSlug(spec.from), slugMessage("from")).refine((spec) => spec.op === "sum" === (spec.column !== void 0), {
|
|
1116
|
+
message: "a rollup's `column` names the source column to aggregate: required for op \"sum\", meaningless for op \"count\"",
|
|
1117
|
+
path: ["column"]
|
|
1118
|
+
});
|
|
1014
1119
|
/** A checkbox that is a pure PROJECTION of an `enum` field — it stores
|
|
1015
1120
|
* nothing of its own. Checked when the enum named by `field` equals
|
|
1016
1121
|
* `onValue`; toggling writes `onValue` / `offValue` back to that enum
|
|
@@ -1034,23 +1139,60 @@ var FieldSpecZ = zod.z.discriminatedUnion("type", [
|
|
|
1034
1139
|
DerivedFieldZ,
|
|
1035
1140
|
EmbedFieldZ,
|
|
1036
1141
|
BacklinksFieldZ,
|
|
1142
|
+
RollupFieldZ,
|
|
1037
1143
|
ToggleFieldZ
|
|
1038
1144
|
]);
|
|
1039
|
-
|
|
1040
|
-
* detail view. Domain-free: the host validates the shape; the meaning
|
|
1041
|
-
* (which role, which template) is data. v1 ships only `kind: "chat"` —
|
|
1042
|
-
* start a new chat in `role` with a templated seed prompt; the enum
|
|
1043
|
-
* reserves room for a future `"mutate"` (status transitions) without
|
|
1044
|
-
* another schema-shape change. */
|
|
1045
|
-
var ActionSpecZ = zod.z.object({
|
|
1145
|
+
var actionBase = {
|
|
1046
1146
|
id: zod.z.string().trim().min(1),
|
|
1047
1147
|
label: zod.z.string().trim().min(1),
|
|
1048
|
-
icon: zod.z.string().trim().min(1).optional()
|
|
1049
|
-
|
|
1148
|
+
icon: zod.z.string().trim().min(1).optional()
|
|
1149
|
+
};
|
|
1150
|
+
/** The LLM-seeded action kinds — same shape, different visibility:
|
|
1151
|
+
* - `"chat"` — start a new VISIBLE chat in `role` with the templated
|
|
1152
|
+
* seed prompt (judgment work: drafting, planning, conversation).
|
|
1153
|
+
* - `"agent"` — dispatch a HIDDEN worker (origin `system`) with the SAME
|
|
1154
|
+
* seed; it edits records via manageCollection and finishes silently
|
|
1155
|
+
* (mechanical enrichment: refresh a price, fetch metadata). Spinner
|
|
1156
|
+
* while running, deduped failure bell on error — see
|
|
1157
|
+
* server/api/routes/collectionAgentActions.ts. */
|
|
1158
|
+
var SeededActionZ = zod.z.object({
|
|
1159
|
+
kind: zod.z.enum(["chat", "agent"]),
|
|
1160
|
+
...actionBase,
|
|
1050
1161
|
role: zod.z.string().trim().min(1),
|
|
1051
1162
|
template: zod.z.string().trim().min(1).refine(require_collection_paths.isSafeActionTemplatePath, "must be a safe path under `templates/` (e.g. `templates/invoice.md`; no `..`, no leading `/`, no backslash)"),
|
|
1052
1163
|
when: WhenZ.optional()
|
|
1053
1164
|
});
|
|
1165
|
+
/** `kind: "mutate"` — a declarative, HOST-executed write; no LLM, no
|
|
1166
|
+
* tokens (plan step ④ of plans/done/collection-ontology.md). Clicking the
|
|
1167
|
+
* button (after an optional `params` mini-form) merges `set` into the
|
|
1168
|
+
* record: values are literals or `$params.<name>` references. `require`
|
|
1169
|
+
* is the state gate — the standard `when` shape, both the visibility
|
|
1170
|
+
* rule AND the server-side authorization rule, exactly like `when` on
|
|
1171
|
+
* the seeded kinds. `params` reuses the table sub-field DSL, and the
|
|
1172
|
+
* form is validated by the SAME compiled record checks `putItems` uses
|
|
1173
|
+
* (`recordFieldProblem`), not a third mechanism. Record-level only —
|
|
1174
|
+
* a collection-level mutate has no record to write (schema refine
|
|
1175
|
+
* below). Merge semantics make half-states unconstructible THROUGH
|
|
1176
|
+
* THIS PATH; the raw file stays editable by design (lint, not lock). */
|
|
1177
|
+
var MutateActionZ = zod.z.object({
|
|
1178
|
+
kind: zod.z.literal("mutate"),
|
|
1179
|
+
...actionBase,
|
|
1180
|
+
require: WhenZ.optional(),
|
|
1181
|
+
params: zod.z.record(zod.z.string().trim().min(1), SubFieldSpecZ).optional(),
|
|
1182
|
+
set: zod.z.record(zod.z.string().trim().min(1), zod.z.union([
|
|
1183
|
+
zod.z.string(),
|
|
1184
|
+
zod.z.number(),
|
|
1185
|
+
zod.z.boolean()
|
|
1186
|
+
]))
|
|
1187
|
+
}).refine((spec) => Object.keys(spec.set).length > 0, {
|
|
1188
|
+
message: "a mutate action's `set` must name at least one field to write",
|
|
1189
|
+
path: ["set"]
|
|
1190
|
+
});
|
|
1191
|
+
/** A schema-declared record action, rendered as a button in the read-only
|
|
1192
|
+
* detail view. Domain-free: the host validates the shape; the meaning
|
|
1193
|
+
* (role + template prose, or the declarative `set`) is data. A
|
|
1194
|
+
* discriminated union on `kind` — each kind declares only its own keys. */
|
|
1195
|
+
var ActionSpecZ = zod.z.discriminatedUnion("kind", [SeededActionZ, MutateActionZ]);
|
|
1054
1196
|
/** A custom (LLM-authored) HTML view registration. Domain-free: the host
|
|
1055
1197
|
* validates the shape; the view's behaviour lives in the HTML file. `file`
|
|
1056
1198
|
* is constrained to `views/*.html` (path-safe) so the view-file reader can
|
|
@@ -1291,6 +1433,21 @@ var CollectionSchemaZ = zod.z.object({
|
|
|
1291
1433
|
}).refine((schema) => schema.collectionActions === void 0 || new Set(schema.collectionActions.map((action) => action.id)).size === schema.collectionActions.length, {
|
|
1292
1434
|
message: "schema `collectionActions` must have unique `id`s",
|
|
1293
1435
|
path: ["collectionActions"]
|
|
1436
|
+
}).refine((schema) => (schema.actions ?? []).every((action) => action.kind !== "mutate" || Object.keys(action.set).every((key) => {
|
|
1437
|
+
const target = schema.fields[key];
|
|
1438
|
+
return target !== void 0 && !require_calendarGrid.COMPUTED_TYPES.has(target.type) && key !== schema.primaryKey;
|
|
1439
|
+
})), {
|
|
1440
|
+
message: "a mutate action's `set` keys must name declared, non-computed fields (and never the primaryKey)",
|
|
1441
|
+
path: ["actions"]
|
|
1442
|
+
}).refine((schema) => (schema.actions ?? []).every((action) => action.kind !== "mutate" || Object.values(action.set).every((value) => {
|
|
1443
|
+
const ref = paramRefName(value);
|
|
1444
|
+
return ref === null || (action.params ?? {})[ref] !== void 0;
|
|
1445
|
+
})), {
|
|
1446
|
+
message: "a mutate action's `$params.<name>` references must name keys declared in its `params`",
|
|
1447
|
+
path: ["actions"]
|
|
1448
|
+
}).refine((schema) => (schema.collectionActions ?? []).every((action) => action.kind !== "mutate"), {
|
|
1449
|
+
message: "`collectionActions` cannot contain `kind: \"mutate\"` — a collection-level action has no record to write",
|
|
1450
|
+
path: ["collectionActions"]
|
|
1294
1451
|
}).refine((schema) => collectCurrencyFieldRefs(schema.fields).every((name) => CODE_FIELD_TYPES.has(schema.fields[name]?.type ?? "")), {
|
|
1295
1452
|
message: "a money field's `currencyField` must name a top-level `string`, `text`, or `enum` field that holds the currency code",
|
|
1296
1453
|
path: ["fields"]
|
|
@@ -1577,9 +1734,9 @@ function schemaRelations(schema) {
|
|
|
1577
1734
|
kind: spec.type,
|
|
1578
1735
|
to: spec.to
|
|
1579
1736
|
});
|
|
1580
|
-
if (spec.type === "backlinks") relations.push({
|
|
1737
|
+
if (spec.type === "backlinks" || spec.type === "rollup") relations.push({
|
|
1581
1738
|
field: key,
|
|
1582
|
-
kind:
|
|
1739
|
+
kind: spec.type,
|
|
1583
1740
|
to: spec.from
|
|
1584
1741
|
});
|
|
1585
1742
|
if (spec.type !== "table") continue;
|
|
@@ -1652,11 +1809,12 @@ function uniqueEmbedTargets(schema) {
|
|
|
1652
1809
|
for (const field of Object.values(schema.fields)) if (field.type === "embed" && typeof field.to === "string" && field.to.length > 0) targets.add(field.to);
|
|
1653
1810
|
return [...targets];
|
|
1654
1811
|
}
|
|
1655
|
-
/** Slugs of every SOURCE collection a `backlinks`
|
|
1656
|
-
* loaded exactly like ref/embed targets (whole
|
|
1812
|
+
/** Slugs of every SOURCE collection a `backlinks` or `rollup` field
|
|
1813
|
+
* reverses over — loaded exactly like ref/embed targets (whole
|
|
1814
|
+
* collection, once; the two field kinds share one load). */
|
|
1657
1815
|
function uniqueBacklinkSources(schema) {
|
|
1658
1816
|
const sources = /* @__PURE__ */ new Set();
|
|
1659
|
-
for (const field of Object.values(schema.fields)) if (field.type === "backlinks" && field.from.length > 0) sources.add(field.from);
|
|
1817
|
+
for (const field of Object.values(schema.fields)) if ((field.type === "backlinks" || field.type === "rollup") && field.from.length > 0) sources.add(field.from);
|
|
1660
1818
|
return [...sources];
|
|
1661
1819
|
}
|
|
1662
1820
|
async function loadTarget(slug, opts) {
|
|
@@ -1719,6 +1877,24 @@ function projectComputed(schema, enriched, linked) {
|
|
|
1719
1877
|
}
|
|
1720
1878
|
return enriched;
|
|
1721
1879
|
}
|
|
1880
|
+
/** Resolve every rollup field onto a COPY of the record, BEFORE the
|
|
1881
|
+
* formula pass — a `derived` formula may reference rollup values as
|
|
1882
|
+
* plain identifiers (`played = homePlayed + awayPlayed`). Same reverse
|
|
1883
|
+
* machinery as backlinks, collapsed to a number: an unresolvable source
|
|
1884
|
+
* is null (a formula reading it fails soft to em-dash); an empty match
|
|
1885
|
+
* set is a real 0. Returns `record` unchanged when the schema declares
|
|
1886
|
+
* no rollups. */
|
|
1887
|
+
function projectRollups(schema, record, linked) {
|
|
1888
|
+
let out = record;
|
|
1889
|
+
for (const [key, field] of Object.entries(schema.fields)) {
|
|
1890
|
+
if (field.type !== "rollup") continue;
|
|
1891
|
+
if (out === record) out = { ...record };
|
|
1892
|
+
const source = linked[field.from];
|
|
1893
|
+
const selfId = String(record[schema.primaryKey] ?? "");
|
|
1894
|
+
out[key] = source ? require_calendarGrid.rollupValue(field, selfId, Object.values(source.byId)) : null;
|
|
1895
|
+
}
|
|
1896
|
+
return out;
|
|
1897
|
+
}
|
|
1722
1898
|
/** Enrich records with every host-computed field: derived formulas
|
|
1723
1899
|
* evaluated (cross-collection derefs included), toggles projected,
|
|
1724
1900
|
* embeds resolved. Loads each linked collection ONCE per call. Input
|
|
@@ -1727,7 +1903,7 @@ async function enrichItems(collection, items, opts = {}) {
|
|
|
1727
1903
|
const { schema } = collection;
|
|
1728
1904
|
const linked = await loadLinkedTargets(schema, opts);
|
|
1729
1905
|
const refRecords = toRefRecords(linked);
|
|
1730
|
-
return items.map((item) => projectComputed(schema, require_calendarGrid.deriveAll(schema, item, refRecords), linked));
|
|
1906
|
+
return items.map((item) => projectComputed(schema, require_calendarGrid.deriveAll(schema, projectRollups(schema, item, linked), refRecords), linked));
|
|
1731
1907
|
}
|
|
1732
1908
|
//#endregion
|
|
1733
1909
|
//#region src/collection/server/dynamicIcon.ts
|
|
@@ -2282,6 +2458,12 @@ Object.defineProperty(exports, "advanceTriggerDate", {
|
|
|
2282
2458
|
return advanceTriggerDate;
|
|
2283
2459
|
}
|
|
2284
2460
|
});
|
|
2461
|
+
Object.defineProperty(exports, "applyMutateAction", {
|
|
2462
|
+
enumerable: true,
|
|
2463
|
+
get: function() {
|
|
2464
|
+
return applyMutateAction;
|
|
2465
|
+
}
|
|
2466
|
+
});
|
|
2285
2467
|
Object.defineProperty(exports, "buildActionSeedPrompt", {
|
|
2286
2468
|
enumerable: true,
|
|
2287
2469
|
get: function() {
|
|
@@ -2378,6 +2560,12 @@ Object.defineProperty(exports, "errorMessage", {
|
|
|
2378
2560
|
return errorMessage;
|
|
2379
2561
|
}
|
|
2380
2562
|
});
|
|
2563
|
+
Object.defineProperty(exports, "firstMutateParamProblem", {
|
|
2564
|
+
enumerable: true,
|
|
2565
|
+
get: function() {
|
|
2566
|
+
return firstMutateParamProblem;
|
|
2567
|
+
}
|
|
2568
|
+
});
|
|
2381
2569
|
Object.defineProperty(exports, "formatCivil", {
|
|
2382
2570
|
enumerable: true,
|
|
2383
2571
|
get: function() {
|
|
@@ -2589,4 +2777,4 @@ Object.defineProperty(exports, "writeItem", {
|
|
|
2589
2777
|
}
|
|
2590
2778
|
});
|
|
2591
2779
|
|
|
2592
|
-
//# sourceMappingURL=server-
|
|
2780
|
+
//# sourceMappingURL=server-Cd6HA_3-.cjs.map
|