@mulmoclaude/collection-plugin 0.5.2 → 0.5.3
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/core/schema.d.ts +24 -2
- package/dist/core/schema.d.ts.map +1 -1
- package/dist/index.cjs +2 -1
- package/dist/index.js +2 -2
- package/dist/{schema-LrmrgW12.cjs → schema-5P5ZJPrY.cjs} +11 -1
- package/dist/schema-5P5ZJPrY.cjs.map +1 -0
- package/dist/{schema-DyfSfVzh.js → schema-BcnitlL8.js} +6 -2
- package/dist/schema-BcnitlL8.js.map +1 -0
- package/dist/server/discovery.d.ts +14 -2
- package/dist/server/discovery.d.ts.map +1 -1
- package/dist/server/spawn.d.ts +8 -1
- package/dist/server/spawn.d.ts.map +1 -1
- package/dist/server.cjs +85 -9
- package/dist/server.cjs.map +1 -1
- package/dist/server.js +85 -10
- package/dist/server.js.map +1 -1
- package/package.json +1 -1
- package/dist/schema-DyfSfVzh.js.map +0 -1
- package/dist/schema-LrmrgW12.cjs.map +0 -1
package/dist/server.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as INGEST_KINDS, t as FEED_SCHEDULES } from "./schema-
|
|
1
|
+
import { n as INGEST_KINDS, r as isFieldDrivenEvery, t as FEED_SCHEDULES } from "./schema-BcnitlL8.js";
|
|
2
2
|
import { t as deriveAll } from "./deriveAll-BHcs1erT.js";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { promises, realpathSync } from "node:fs";
|
|
@@ -777,7 +777,7 @@ var CustomViewSchema = z.object({
|
|
|
777
777
|
file: z.string().trim().min(1).refine(isSafeCustomViewPath, "must be a safe path under `views/` ending in `.html` (e.g. `views/year.html`; no `..`, no leading `/`, no backslash)"),
|
|
778
778
|
capabilities: z.array(z.enum(["read", "write"])).optional()
|
|
779
779
|
});
|
|
780
|
-
var
|
|
780
|
+
var EveryLiteralSchema = z.object({
|
|
781
781
|
unit: z.enum([
|
|
782
782
|
"day",
|
|
783
783
|
"week",
|
|
@@ -786,7 +786,12 @@ var EverySchema = z.object({
|
|
|
786
786
|
]),
|
|
787
787
|
interval: z.number().int().min(1),
|
|
788
788
|
dayOfMonth: z.union([z.number().int().min(1).max(31), z.literal("last")]).optional()
|
|
789
|
-
});
|
|
789
|
+
}).strict();
|
|
790
|
+
var EveryFieldDrivenSchema = z.object({
|
|
791
|
+
fromField: z.string().trim().min(1),
|
|
792
|
+
map: z.record(z.string(), EveryLiteralSchema)
|
|
793
|
+
}).strict();
|
|
794
|
+
var EverySchema = z.union([EveryLiteralSchema, EveryFieldDrivenSchema]);
|
|
790
795
|
var SpawnSchema = z.object({
|
|
791
796
|
when: WhenSchema.optional(),
|
|
792
797
|
every: EverySchema,
|
|
@@ -828,6 +833,36 @@ function spawnSuccessorStartsInert(schema) {
|
|
|
828
833
|
if (spawn.set && Object.prototype.hasOwnProperty.call(spawn.set, field)) return !values.includes(String(spawn.set[field]));
|
|
829
834
|
return !(spawn.carry ?? []).includes(field);
|
|
830
835
|
}
|
|
836
|
+
function fieldDrivenSpawnEvery(schema) {
|
|
837
|
+
const every = schema.spawn?.every;
|
|
838
|
+
if (!every || !isFieldDrivenEvery(every)) return null;
|
|
839
|
+
return every;
|
|
840
|
+
}
|
|
841
|
+
function fieldDrivenFromFieldIsEnum(schema) {
|
|
842
|
+
const driven = fieldDrivenSpawnEvery(schema);
|
|
843
|
+
if (!driven) return true;
|
|
844
|
+
return schema.fields[driven.fromField]?.type === "enum";
|
|
845
|
+
}
|
|
846
|
+
function fieldDrivenMapCoversValues(schema) {
|
|
847
|
+
const driven = fieldDrivenSpawnEvery(schema);
|
|
848
|
+
if (!driven) return true;
|
|
849
|
+
const target = schema.fields[driven.fromField];
|
|
850
|
+
if (!target || target.type !== "enum" || target.values === void 0) return true;
|
|
851
|
+
const values = new Set(target.values);
|
|
852
|
+
const keys = Object.keys(driven.map);
|
|
853
|
+
return keys.length === values.size && keys.every((key) => values.has(key));
|
|
854
|
+
}
|
|
855
|
+
function fieldDrivenFromFieldCarried(schema) {
|
|
856
|
+
const driven = fieldDrivenSpawnEvery(schema);
|
|
857
|
+
if (!driven) return true;
|
|
858
|
+
const { carry, set } = schema.spawn ?? {};
|
|
859
|
+
if (set && Object.prototype.hasOwnProperty.call(set, driven.fromField)) {
|
|
860
|
+
const raw = set[driven.fromField];
|
|
861
|
+
if (raw === void 0 || raw === null || raw === "") return false;
|
|
862
|
+
return Object.prototype.hasOwnProperty.call(driven.map, String(raw));
|
|
863
|
+
}
|
|
864
|
+
return (carry ?? []).includes(driven.fromField);
|
|
865
|
+
}
|
|
831
866
|
var IngestSchemaZ = z.object({
|
|
832
867
|
kind: z.enum(INGEST_KINDS),
|
|
833
868
|
url: z.string().url(),
|
|
@@ -904,6 +939,15 @@ var CollectionSchemaZ = z.object({
|
|
|
904
939
|
}).refine((schema) => spawnSuccessorStartsInert(schema), {
|
|
905
940
|
message: "`spawn` must leave the successor in a non-matching state (e.g. `set` the status to a pending value); seeding the predicate field to a matching value via `set`/`carry` would respawn forever",
|
|
906
941
|
path: ["spawn"]
|
|
942
|
+
}).refine((schema) => fieldDrivenFromFieldIsEnum(schema), {
|
|
943
|
+
message: "`spawn.every.fromField` must name a top-level `enum` field declared in `fields`",
|
|
944
|
+
path: ["spawn"]
|
|
945
|
+
}).refine((schema) => fieldDrivenMapCoversValues(schema), {
|
|
946
|
+
message: "`spawn.every.map` keys must exactly cover the `values` of the `enum` named by `fromField` (no missing or extra keys)",
|
|
947
|
+
path: ["spawn"]
|
|
948
|
+
}).refine((schema) => fieldDrivenFromFieldCarried(schema), {
|
|
949
|
+
message: "`spawn.every.fromField` must appear in `spawn.carry`, or be written by `spawn.set` to a value present in `spawn.every.map`, so the successor keeps a resolvable recurrence interval",
|
|
950
|
+
path: ["spawn"]
|
|
907
951
|
}).refine((schema) => schema.calendarField === void 0 || isDateLike(schema.fields[schema.calendarField]?.type), {
|
|
908
952
|
message: "schema `calendarField` must name a top-level `date` or `datetime` field declared in `fields`",
|
|
909
953
|
path: ["calendarField"]
|
|
@@ -1310,6 +1354,18 @@ function matchesWhen(when, schema, item) {
|
|
|
1310
1354
|
const raw = item[completionField];
|
|
1311
1355
|
return raw !== void 0 && raw !== null && completionDoneValues.includes(String(raw));
|
|
1312
1356
|
}
|
|
1357
|
+
/** Resolve the literal `every` that applies to `sourceItem`. Literal-arm
|
|
1358
|
+
* `spawn.every` passes through unchanged. Field-driven `spawn.every` reads
|
|
1359
|
+
* `sourceItem[fromField]` and looks it up in `map`; an empty field or a
|
|
1360
|
+
* value with no map entry yields null (caller skips + logs — see plan §5).
|
|
1361
|
+
* Discovery rejects a map that doesn't cover the enum's values, so null
|
|
1362
|
+
* here means a record that predates a map/enum edit. */
|
|
1363
|
+
function resolveEvery(every, sourceItem) {
|
|
1364
|
+
if (!isFieldDrivenEvery(every)) return every;
|
|
1365
|
+
const raw = sourceItem[every.fromField];
|
|
1366
|
+
if (raw === void 0 || raw === null || raw === "") return null;
|
|
1367
|
+
return every.map[String(raw)] ?? null;
|
|
1368
|
+
}
|
|
1313
1369
|
/** Build the successor record purely from (schema, source record, source
|
|
1314
1370
|
* id). Returns null when the schema has no spawn/triggerField or the
|
|
1315
1371
|
* source's trigger date is unparseable. */
|
|
@@ -1318,7 +1374,9 @@ function computeSuccessor(schema, sourceItem, sourceId) {
|
|
|
1318
1374
|
if (!spawn || !triggerField) return null;
|
|
1319
1375
|
const srcDate = parseCivil(sourceItem[triggerField]);
|
|
1320
1376
|
if (srcDate === null) return null;
|
|
1321
|
-
const
|
|
1377
|
+
const every = resolveEvery(spawn.every, sourceItem);
|
|
1378
|
+
if (every === null) return null;
|
|
1379
|
+
const next = advanceTriggerDate(srcDate, every);
|
|
1322
1380
|
const nextId = successorId(sourceId, next);
|
|
1323
1381
|
const record = {};
|
|
1324
1382
|
for (const field of spawn.carry ?? []) if (Object.prototype.hasOwnProperty.call(sourceItem, field)) record[field] = sourceItem[field];
|
|
@@ -1330,6 +1388,27 @@ function computeSuccessor(schema, sourceItem, sourceId) {
|
|
|
1330
1388
|
record
|
|
1331
1389
|
};
|
|
1332
1390
|
}
|
|
1391
|
+
/** Warn precisely about which of `computeSuccessor`'s two null causes fired
|
|
1392
|
+
* (plan §5): an unparseable source trigger date (the original cause), or a
|
|
1393
|
+
* field-driven `every` whose record value has no `map` entry (a record that
|
|
1394
|
+
* predates a map/enum edit — discovery rejects this statically otherwise). */
|
|
1395
|
+
function logSpawnSkip(slug, triggerField, every, sourceItem, sourceId) {
|
|
1396
|
+
if (parseCivil(sourceItem[triggerField]) === null) {
|
|
1397
|
+
log.warn("collections", "spawn skipped: source trigger date unparseable", {
|
|
1398
|
+
slug,
|
|
1399
|
+
sourceId,
|
|
1400
|
+
triggerField
|
|
1401
|
+
});
|
|
1402
|
+
return;
|
|
1403
|
+
}
|
|
1404
|
+
const fromField = isFieldDrivenEvery(every) ? every.fromField : void 0;
|
|
1405
|
+
log.warn("collections", "spawn skipped: no `every` mapping for frequency value", {
|
|
1406
|
+
slug,
|
|
1407
|
+
sourceId,
|
|
1408
|
+
fromField,
|
|
1409
|
+
value: fromField === void 0 ? void 0 : sourceItem[fromField]
|
|
1410
|
+
});
|
|
1411
|
+
}
|
|
1333
1412
|
/** Idempotently create the successor for `sourceItem` when it matches the
|
|
1334
1413
|
* spawn predicate. No-op when the schema declares no spawn, the
|
|
1335
1414
|
* predicate doesn't match, the trigger date is unparseable, or the
|
|
@@ -1341,11 +1420,7 @@ async function maybeSpawnSuccessor(slug, schema, dataDir, sourceItem, sourceId,
|
|
|
1341
1420
|
if (!matchesWhen(spawn.when, schema, sourceItem)) return;
|
|
1342
1421
|
const computed = computeSuccessor(schema, sourceItem, sourceId);
|
|
1343
1422
|
if (computed === null) {
|
|
1344
|
-
|
|
1345
|
-
slug,
|
|
1346
|
-
sourceId,
|
|
1347
|
-
triggerField: schema.triggerField
|
|
1348
|
-
});
|
|
1423
|
+
logSpawnSkip(slug, schema.triggerField, spawn.every, sourceItem, sourceId);
|
|
1349
1424
|
return;
|
|
1350
1425
|
}
|
|
1351
1426
|
if (matchesWhen(spawn.when, schema, computed.record)) {
|
|
@@ -1607,6 +1682,6 @@ async function deleteCustomView(collection, viewId, opts = {}) {
|
|
|
1607
1682
|
};
|
|
1608
1683
|
}
|
|
1609
1684
|
//#endregion
|
|
1610
|
-
export { COMPUTED_TYPES, CollectionSchemaZ, SCHEMA_FILE, acceptParsedSchema, advanceTriggerDate, buildActionSeedPrompt, buildCollectionActionSeedPrompt, computeSuccessor, configureCollectionHost, daysInMonth, deleteCollection, deleteCollectionRefusalMessage, deleteCustomView, deleteItem, discoverCollections, enrichItems, formatCivil, generateItemId, getWorkspaceRoot, isContainedInRoot, isContainedInWorkspace, isSafeActionTemplatePath, isSafeCustomViewPath, isSafeTemplatePath, isTriggerDue, itemFilePath, listItems, loadCollection, log, maybeSpawnSuccessor, parseCivil, readCustomViewHtml, readItem, readSkillTemplate, resolveCreateItemId, resolveDataDir, resolveTemplatePath, safeRecordId, safeSlugName, successorId, toDetail, toSummary, validateCollectionRecords, validateRecordObject, writeItem };
|
|
1685
|
+
export { COMPUTED_TYPES, CollectionSchemaZ, SCHEMA_FILE, acceptParsedSchema, advanceTriggerDate, buildActionSeedPrompt, buildCollectionActionSeedPrompt, computeSuccessor, configureCollectionHost, daysInMonth, deleteCollection, deleteCollectionRefusalMessage, deleteCustomView, deleteItem, discoverCollections, enrichItems, formatCivil, generateItemId, getWorkspaceRoot, isContainedInRoot, isContainedInWorkspace, isSafeActionTemplatePath, isSafeCustomViewPath, isSafeTemplatePath, isTriggerDue, itemFilePath, listItems, loadCollection, log, maybeSpawnSuccessor, parseCivil, readCustomViewHtml, readItem, readSkillTemplate, resolveCreateItemId, resolveDataDir, resolveEvery, resolveTemplatePath, safeRecordId, safeSlugName, successorId, toDetail, toSummary, validateCollectionRecords, validateRecordObject, writeItem };
|
|
1611
1686
|
|
|
1612
1687
|
//# sourceMappingURL=server.js.map
|