@mulmoclaude/core 1.12.0 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/assets/helps/error-recovery.md +110 -1
  2. package/dist/{calendarGrid-CaS9er8i.cjs → calendarGrid-BQYqOCnF.cjs} +8 -9
  3. package/dist/{calendarGrid-CaS9er8i.cjs.map → calendarGrid-BQYqOCnF.cjs.map} +1 -1
  4. package/dist/{calendarGrid-CrGxeCOG.js → calendarGrid-DivcNKkX.js} +8 -9
  5. package/dist/{calendarGrid-CrGxeCOG.js.map → calendarGrid-DivcNKkX.js.map} +1 -1
  6. package/dist/collection/core/project.d.ts +11 -0
  7. package/dist/collection/core/schemaZ.d.ts +4 -4
  8. package/dist/collection/index.cjs +5 -4
  9. package/dist/collection/index.js +5 -5
  10. package/dist/collection/registry/server/index.cjs +3 -3
  11. package/dist/collection/registry/server/index.cjs.map +1 -1
  12. package/dist/collection/registry/server/index.js +3 -3
  13. package/dist/collection/registry/server/index.js.map +1 -1
  14. package/dist/collection/server/csvQuery.d.ts +7 -2
  15. package/dist/collection/server/csvStore.d.ts +2 -1
  16. package/dist/collection/server/index.cjs +3 -3
  17. package/dist/collection/server/index.js +3 -3
  18. package/dist/collection-watchers/index.cjs +10 -25
  19. package/dist/collection-watchers/index.cjs.map +1 -1
  20. package/dist/collection-watchers/index.js +10 -25
  21. package/dist/collection-watchers/index.js.map +1 -1
  22. package/dist/{discovery-NRy3tyUA.cjs → discovery-CRDx6TS4.cjs} +88 -47
  23. package/dist/discovery-CRDx6TS4.cjs.map +1 -0
  24. package/dist/{discovery-B4CZvrXR.js → discovery-D6p4ITNR.js} +88 -47
  25. package/dist/discovery-D6p4ITNR.js.map +1 -0
  26. package/dist/{dist-pWpC-b04.cjs → dist-CsgSfWwR.cjs} +66 -1
  27. package/dist/{dist-pWpC-b04.cjs.map → dist-CsgSfWwR.cjs.map} +1 -1
  28. package/dist/{dist-Cwk0e12G.js → dist-D8zokgGo.js} +25 -2
  29. package/dist/{dist-Cwk0e12G.js.map → dist-D8zokgGo.js.map} +1 -1
  30. package/dist/feeds/index.cjs +2 -2
  31. package/dist/feeds/index.js +2 -2
  32. package/dist/feeds/server/engine.d.ts +27 -0
  33. package/dist/feeds/server/index.cjs +97 -26
  34. package/dist/feeds/server/index.cjs.map +1 -1
  35. package/dist/feeds/server/index.d.ts +1 -1
  36. package/dist/feeds/server/index.js +96 -27
  37. package/dist/feeds/server/index.js.map +1 -1
  38. package/dist/google/calendar.d.ts +23 -9
  39. package/dist/google/calendarStateLock.d.ts +23 -0
  40. package/dist/google/collectionProjection.d.ts +0 -8
  41. package/dist/google/collectionPush.d.ts +13 -8
  42. package/dist/google/collectionSync.d.ts +80 -10
  43. package/dist/google/index.cjs +366 -98
  44. package/dist/google/index.cjs.map +1 -1
  45. package/dist/google/index.d.ts +6 -4
  46. package/dist/google/index.js +357 -99
  47. package/dist/google/index.js.map +1 -1
  48. package/dist/{ingestTypes-D1GQdG8e.cjs → ingestTypes-C7IJSqv4.cjs} +2 -2
  49. package/dist/{ingestTypes-D1GQdG8e.cjs.map → ingestTypes-C7IJSqv4.cjs.map} +1 -1
  50. package/dist/{ingestTypes-XReG-li7.js → ingestTypes-DmtDLRmt.js} +2 -2
  51. package/dist/{ingestTypes-XReG-li7.js.map → ingestTypes-DmtDLRmt.js.map} +1 -1
  52. package/dist/notifier/index.cjs +1 -1
  53. package/dist/notifier/index.js +1 -1
  54. package/dist/notifier/store.d.ts +4 -4
  55. package/dist/{notifier-BdA5qzhe.js → notifier-Dxh8mEFh.js} +39 -25
  56. package/dist/notifier-Dxh8mEFh.js.map +1 -0
  57. package/dist/{notifier-tMsAXyXp.cjs → notifier-Wc4M24Bi.cjs} +39 -25
  58. package/dist/notifier-Wc4M24Bi.cjs.map +1 -0
  59. package/dist/plugin-vue/index.cjs +4 -2
  60. package/dist/plugin-vue/index.cjs.map +1 -1
  61. package/dist/plugin-vue/index.js +4 -2
  62. package/dist/plugin-vue/index.js.map +1 -1
  63. package/dist/project-B0j_Vf_Q.js +33 -0
  64. package/dist/project-B0j_Vf_Q.js.map +1 -0
  65. package/dist/project-C1ep9pvo.cjs +44 -0
  66. package/dist/project-C1ep9pvo.cjs.map +1 -0
  67. package/dist/{promptSafety-FD8cQn3e.cjs → promptSafety-B52GvaJc.cjs} +99 -85
  68. package/dist/promptSafety-B52GvaJc.cjs.map +1 -0
  69. package/dist/{promptSafety-C--op_6a.js → promptSafety-DJgaDjM4.js} +99 -85
  70. package/dist/promptSafety-DJgaDjM4.js.map +1 -0
  71. package/dist/remote-host/index.cjs +2 -1
  72. package/dist/remote-host/index.d.ts +10 -0
  73. package/dist/remote-host/index.js +2 -2
  74. package/dist/remote-host/server/index.cjs +2 -2
  75. package/dist/remote-host/server/index.cjs.map +1 -1
  76. package/dist/remote-host/server/index.js +2 -2
  77. package/dist/remote-host/server/index.js.map +1 -1
  78. package/dist/{remote-host-DkDVxNim.cjs → remote-host-CtQjagPt.cjs} +53 -1
  79. package/dist/remote-host-CtQjagPt.cjs.map +1 -0
  80. package/dist/remote-host-Dba4lF3Z.js +82 -0
  81. package/dist/remote-host-Dba4lF3Z.js.map +1 -0
  82. package/dist/remote-view/index.cjs +6 -4
  83. package/dist/remote-view/index.cjs.map +1 -1
  84. package/dist/remote-view/index.js +6 -4
  85. package/dist/remote-view/index.js.map +1 -1
  86. package/dist/scheduler/index.cjs +1 -1
  87. package/dist/scheduler/index.js +1 -1
  88. package/dist/{server-DdyIvFhz.js → server-D6EUf3BF.js} +46 -27
  89. package/dist/server-D6EUf3BF.js.map +1 -0
  90. package/dist/{server-7U-3DE2e.cjs → server-D7_oRbTJ.cjs} +46 -27
  91. package/dist/server-D7_oRbTJ.cjs.map +1 -0
  92. package/dist/utils/index.cjs +1 -1
  93. package/dist/utils/index.js +1 -1
  94. package/dist/whisper/index.cjs +2 -6
  95. package/dist/whisper/index.cjs.map +1 -1
  96. package/dist/whisper/index.js +2 -6
  97. package/dist/whisper/index.js.map +1 -1
  98. package/dist/wiki/index.cjs +2 -2
  99. package/dist/wiki/index.cjs.map +1 -1
  100. package/dist/wiki/index.js +2 -2
  101. package/dist/wiki/index.js.map +1 -1
  102. package/package.json +2 -2
  103. package/dist/discovery-B4CZvrXR.js.map +0 -1
  104. package/dist/discovery-NRy3tyUA.cjs.map +0 -1
  105. package/dist/notifier-BdA5qzhe.js.map +0 -1
  106. package/dist/notifier-tMsAXyXp.cjs.map +0 -1
  107. package/dist/project-BWI5w_BT.cjs +0 -17
  108. package/dist/project-BWI5w_BT.cjs.map +0 -1
  109. package/dist/project-bU98ycsy.js +0 -12
  110. package/dist/project-bU98ycsy.js.map +0 -1
  111. package/dist/promptSafety-C--op_6a.js.map +0 -1
  112. package/dist/promptSafety-FD8cQn3e.cjs.map +0 -1
  113. package/dist/remote-host-D_BRFHcI.js +0 -36
  114. package/dist/remote-host-D_BRFHcI.js.map +0 -1
  115. package/dist/remote-host-DkDVxNim.cjs.map +0 -1
  116. package/dist/server-7U-3DE2e.cjs.map +0 -1
  117. package/dist/server-DdyIvFhz.js.map +0 -1
@@ -1,6 +1,7 @@
1
+ import { a as isErrorWithCode, l as isUnknownArray, r as hasNumberProp, s as isRecord } from "./dist-D8zokgGo.js";
1
2
  import { t as writeFileAtomic } from "./atomic-DPpdrJzO.js";
2
- import { A as fieldTextOrNull, C as FEED_SCHEDULES, D as isReadOnlySchema, O as storageKindFor, S as COMPUTED_TYPES, g as SAFE_SLUG_PATTERN, h as SAFE_RECORD_ID_PATTERN, k as fieldText, v as isSafeRecordId, w as INGEST_KINDS, x as AGENT_INGEST_KIND, y as isSafeSlug } from "./calendarGrid-CrGxeCOG.js";
3
- import { t as projectRecordFields } from "./project-bU98ycsy.js";
3
+ import { A as fieldTextOrNull, C as FEED_SCHEDULES, D as isReadOnlySchema, O as storageKindFor, S as COMPUTED_TYPES, g as SAFE_SLUG_PATTERN, h as SAFE_RECORD_ID_PATTERN, k as fieldText, v as isSafeRecordId, w as INGEST_KINDS, x as AGENT_INGEST_KIND, y as isSafeSlug } from "./calendarGrid-DivcNKkX.js";
4
+ import { n as projectRecordFields } from "./project-B0j_Vf_Q.js";
4
5
  import { n as isSafeCustomViewI18nPath, r as isSafeCustomViewPath, t as isSafeActionTemplatePath } from "./templatePath-k_WNbL_Q.js";
5
6
  import { realpathSync, watch } from "node:fs";
6
7
  import path from "node:path";
@@ -141,7 +142,7 @@ function realpathClosestAncestor(absPath) {
141
142
  while (cursor !== path.dirname(cursor)) try {
142
143
  return realpathSync(cursor);
143
144
  } catch (err) {
144
- if (err.code === "ENOENT") {
145
+ if (isErrorWithCode(err) && err.code === "ENOENT") {
145
146
  cursor = path.dirname(cursor);
146
147
  continue;
147
148
  }
@@ -245,8 +246,7 @@ async function isRegularFile(filePath) {
245
246
  * null when it isn't a JSON object (array / scalar / null). */
246
247
  function parseRecordJson(raw) {
247
248
  const parsed = JSON.parse(raw);
248
- if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
249
- return null;
249
+ return isRecord(parsed) ? parsed : null;
250
250
  }
251
251
  async function tryReadRecord(filePath) {
252
252
  if (!await isRegularFile(filePath)) return null;
@@ -271,7 +271,7 @@ async function listItems(dataDir, opts = {}) {
271
271
  try {
272
272
  entries = await readdir(dataDir);
273
273
  } catch (err) {
274
- if (err.code === "ENOENT") return [];
274
+ if (isErrorWithCode(err) && err.code === "ENOENT") return [];
275
275
  throw err;
276
276
  }
277
277
  const results = [];
@@ -301,7 +301,7 @@ async function readItem(dataDir, itemId, opts = {}) {
301
301
  try {
302
302
  return parseRecordJson(await readFile(filePath, "utf-8"));
303
303
  } catch (err) {
304
- if (err.code === "ENOENT") return null;
304
+ if (isErrorWithCode(err) && err.code === "ENOENT") return null;
305
305
  throw err;
306
306
  }
307
307
  }
@@ -371,7 +371,7 @@ async function writeItem(dataDir, itemId, item, opts = {}) {
371
371
  try {
372
372
  handle = await open(filePath, "wx");
373
373
  } catch (err) {
374
- if (err.code === "EEXIST") return {
374
+ if (isErrorWithCode(err) && err.code === "EEXIST") return {
375
375
  kind: "conflict",
376
376
  itemId: safeId
377
377
  };
@@ -417,7 +417,7 @@ async function deleteItem(dataDir, itemId, opts = {}) {
417
417
  itemId: safeId
418
418
  };
419
419
  } catch (err) {
420
- if (err.code === "ENOENT") return {
420
+ if (isErrorWithCode(err) && err.code === "ENOENT") return {
421
421
  kind: "not-found",
422
422
  itemId: safeId
423
423
  };
@@ -595,7 +595,7 @@ function whereFragment(cond) {
595
595
  }
596
596
  if (cond.op === "contains") return {
597
597
  sql: `contains(${asText}, ?)`,
598
- params: [String(cond.value)]
598
+ params: [String(scalarValue(cond))]
599
599
  };
600
600
  const operator = {
601
601
  eq: "=",
@@ -607,9 +607,16 @@ function whereFragment(cond) {
607
607
  }[cond.op];
608
608
  return {
609
609
  sql: `${typeof cond.value === "string" && (cond.op === "eq" || cond.op === "ne") ? asText : column} ${operator} ?`,
610
- params: [cond.value]
610
+ params: [scalarValue(cond)]
611
611
  };
612
612
  }
613
+ /** `CollectionQueryZ` refines "`in` ⇔ array value", so an array reaching a
614
+ * scalar op means the query was compiled without being validated first —
615
+ * binding it would send an array to a single `?`. */
616
+ function scalarValue(cond) {
617
+ if (Array.isArray(cond.value)) throw new Error(`where condition on '${cond.field}' uses op '${cond.op}', which requires a scalar value, not an array`);
618
+ return cond.value;
619
+ }
613
620
  /** Compile a validated query against `fromSql` (a table-function call
614
621
  * whose FIRST placeholder is the source path — the executor binds it).
615
622
  * Returns the SQL and the where-value parameters that follow the path.
@@ -831,7 +838,7 @@ async function safeCsvStat(absPath, workspaceRoot) {
831
838
  try {
832
839
  info = await lstat(absPath);
833
840
  } catch (err) {
834
- if (err.code === "ENOENT") return null;
841
+ if (isErrorWithCode(err) && err.code === "ENOENT") return null;
835
842
  throw err;
836
843
  }
837
844
  if (!info.isFile()) {
@@ -1045,7 +1052,7 @@ function projectItemFields(items, fields, primaryKey) {
1045
1052
  * tests. `limit: 0` is a valid "count only" page. */
1046
1053
  function pageFromFullRead(items, opts, primaryKey, truncated) {
1047
1054
  const offset = Math.max(0, opts.offset ?? 0);
1048
- const end = opts.limit === void 0 ? void 0 : offset + Math.max(0, opts.limit);
1055
+ const end = opts.limit === void 0 ? items.length : offset + Math.max(0, opts.limit);
1049
1056
  return {
1050
1057
  items: projectItemFields(items.slice(offset, end), opts.fields, primaryKey),
1051
1058
  total: items.length,
@@ -1054,14 +1061,23 @@ function pageFromFullRead(items, opts, primaryKey, truncated) {
1054
1061
  }
1055
1062
  //#endregion
1056
1063
  //#region src/collection/server/sqliteStore.ts
1064
+ /** A constructor's parameter and return types are not observable at runtime,
1065
+ * so the check stops at "DatabaseSync is constructible" — the only member of
1066
+ * the module this store ever touches. */
1067
+ function isSqliteModule(mod) {
1068
+ return isRecord(mod) && typeof mod.DatabaseSync === "function";
1069
+ }
1057
1070
  var sqliteModule = null;
1071
+ /** Drops the memo first so a later call can retry (e.g. tests stubbing the
1072
+ * runtime), then reports why the backend is unusable. */
1073
+ function sqliteUnavailable(reason) {
1074
+ sqliteModule = null;
1075
+ throw new BackendUnavailableError(`sqlite storage needs the node:sqlite module (Node.js >= 22.5) — this runtime cannot load it: ${reason}`);
1076
+ }
1058
1077
  /** Lazy-load node:sqlite once. A runtime without it (Node < 22.5) throws a
1059
1078
  * clearly-worded error the caller surfaces — never a bare MODULE_NOT_FOUND. */
1060
1079
  function loadSqlite() {
1061
- sqliteModule ??= import("node:sqlite").then((mod) => mod, (err) => {
1062
- sqliteModule = null;
1063
- throw new BackendUnavailableError(`sqlite storage needs the node:sqlite module (Node.js >= 22.5) — this runtime cannot load it: ${String(err)}`);
1064
- });
1080
+ sqliteModule ??= import("node:sqlite").then((mod) => isSqliteModule(mod) ? mod : sqliteUnavailable("the module exposes no DatabaseSync constructor"), (err) => sqliteUnavailable(String(err)));
1065
1081
  return sqliteModule;
1066
1082
  }
1067
1083
  /** The db file's on-disk state. A symlink or non-regular file is refused
@@ -1073,7 +1089,7 @@ async function dbFileState(absPath) {
1073
1089
  try {
1074
1090
  return (await lstat(absPath)).isFile() ? "file" : "refused";
1075
1091
  } catch (err) {
1076
- if (err.code === "ENOENT") return "missing";
1092
+ if (isErrorWithCode(err) && err.code === "ENOENT") return "missing";
1077
1093
  throw err;
1078
1094
  }
1079
1095
  }
@@ -1125,27 +1141,39 @@ async function withDb(absPath, workspaceRoot, mode, onUnavailable, operation) {
1125
1141
  handle.database.close();
1126
1142
  }
1127
1143
  }
1144
+ var SQLITE_CONSTRAINT_PRIMARYKEY = 1555;
1145
+ var SQLITE_CONSTRAINT_UNIQUE = 2067;
1128
1146
  /** node:sqlite throws ERR_SQLITE_ERROR with the SQLite extended result
1129
- * code on `errcode`. Our INSERT's duplicate-id failure is
1130
- * SQLITE_CONSTRAINT_PRIMARYKEY (1555); SQLITE_CONSTRAINT_UNIQUE (2067)
1131
- * covers a plain UNIQUE index. Checked structurally (message text kept
1132
- * only as a fallback for runtimes that don't expose `errcode`). */
1147
+ * code on `errcode`. Checked structurally (message text kept only as a
1148
+ * fallback for runtimes that don't expose `errcode`). */
1133
1149
  function isUniqueConstraintError(err) {
1134
- const { errcode } = err;
1135
- if (errcode !== void 0) return errcode === 1555 || errcode === 2067;
1150
+ if (hasNumberProp(err, "errcode")) return err.errcode === SQLITE_CONSTRAINT_PRIMARYKEY || err.errcode === SQLITE_CONSTRAINT_UNIQUE;
1136
1151
  return String(err).includes("UNIQUE constraint");
1137
1152
  }
1138
1153
  function parseRow(raw) {
1139
1154
  if (typeof raw !== "string") return null;
1140
1155
  try {
1141
1156
  const parsed = JSON.parse(raw);
1142
- return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : null;
1157
+ return isRecord(parsed) ? parsed : null;
1143
1158
  } catch {
1144
1159
  return null;
1145
1160
  }
1146
1161
  }
1162
+ /** One column of a result row. node:sqlite types rows as `unknown`, so a
1163
+ * value that is not a row object yields no column at all. */
1164
+ function readColumn(row, column) {
1165
+ return isRecord(row) ? row[column] : void 0;
1166
+ }
1147
1167
  function rowsToItems(rows) {
1148
- return rows.map((row) => parseRow(row.record)).filter((item) => item !== null);
1168
+ return rows.map((row) => parseRow(readColumn(row, "record"))).filter((item) => item !== null);
1169
+ }
1170
+ /** node:sqlite hands back an integer column as `number`, or as `bigint` once
1171
+ * it leaves the safe-integer range — COUNT(*) can be either. */
1172
+ function countRecords(database) {
1173
+ const count = readColumn(database.prepare("SELECT COUNT(*) AS n FROM records").get(), "n");
1174
+ if (typeof count === "number") return count;
1175
+ if (typeof count === "bigint") return Number(count);
1176
+ throw new Error(`sqlite COUNT(*) returned no numeric row count (got ${typeof count})`);
1149
1177
  }
1150
1178
  async function sqliteList(absPath, workspaceRoot) {
1151
1179
  return withDb(absPath, workspaceRoot, "read", () => [], (database) => rowsToItems(database.prepare("SELECT record FROM records ORDER BY id").all()));
@@ -1157,7 +1185,7 @@ async function sqlitePage(absPath, primaryKey, opts, workspaceRoot) {
1157
1185
  truncated: false
1158
1186
  };
1159
1187
  return withDb(absPath, workspaceRoot, "read", () => emptyPage, (database) => {
1160
- const total = Number(database.prepare("SELECT COUNT(*) AS n FROM records").get().n);
1188
+ const total = countRecords(database);
1161
1189
  const offset = Math.max(0, opts.offset ?? 0);
1162
1190
  const limit = opts.limit === void 0 ? -1 : Math.max(0, opts.limit);
1163
1191
  return {
@@ -1171,8 +1199,7 @@ async function sqliteRead(absPath, itemId, workspaceRoot) {
1171
1199
  const safeId = safeRecordId(itemId);
1172
1200
  if (safeId === null) return null;
1173
1201
  return withDb(absPath, workspaceRoot, "read", () => null, (database) => {
1174
- const row = database.prepare("SELECT record FROM records WHERE id = ?").get(safeId);
1175
- return row === void 0 ? null : parseRow(row.record);
1202
+ return parseRow(readColumn(database.prepare("SELECT record FROM records WHERE id = ?").get(safeId), "record"));
1176
1203
  });
1177
1204
  }
1178
1205
  async function sqliteWrite(absPath, itemId, item, opts) {
@@ -2150,7 +2177,7 @@ var SpawnZ = z.object({
2150
2177
  * items natively and ignore it — so no kind-specific requirement here. */
2151
2178
  var DeclarativeIngestZ = z.object({
2152
2179
  kind: z.enum(INGEST_KINDS),
2153
- url: z.string().url(),
2180
+ url: z.url(),
2154
2181
  schedule: z.enum(FEED_SCHEDULES),
2155
2182
  atHour: z.number().int().min(0).max(23).optional(),
2156
2183
  itemsAt: z.string().trim().min(1).optional(),
@@ -2405,23 +2432,38 @@ function ownPrototypeKey(value) {
2405
2432
  for (const key of PROTOTYPE_KEYS) if (Object.hasOwn(value, key)) return key;
2406
2433
  return null;
2407
2434
  }
2435
+ /** Own enumerable entries of an object (arrays keyed by index), none for
2436
+ * anything else — the raw input is unvalidated, so `fields` may be junk. */
2437
+ function ownEntries(value) {
2438
+ if (isUnknownArray(value)) return value.map((entry, index) => [String(index), entry]);
2439
+ return isRecord(value) ? Object.entries(value) : [];
2440
+ }
2441
+ /** The name-defining sub-record a raw field spec (`of`) or action (`params`)
2442
+ * carries, or undefined when the holder isn't an object at all. */
2443
+ function nameDefiningSubRecord(holder, key) {
2444
+ return isRecord(holder) ? holder[key] : void 0;
2445
+ }
2446
+ /** Dotted path of the first prototype-sensitive `params` name across both
2447
+ * action lists, or null. */
2448
+ function prototypeActionParamPath(input) {
2449
+ for (const [listName, list] of [["actions", input.actions], ["collectionActions", input.collectionActions]]) for (const action of isUnknownArray(list) ? list : []) {
2450
+ const badParam = ownPrototypeKey(nameDefiningSubRecord(action, "params"));
2451
+ if (badParam !== null) return `${listName}.params.${badParam}`;
2452
+ }
2453
+ return null;
2454
+ }
2408
2455
  /** Dotted path of the first prototype-sensitive field name in the raw
2409
2456
  * schema input — top-level `fields`, each table field's `of`, and each
2410
2457
  * action's `params` (the three records that DEFINE names) — or null. */
2411
2458
  function prototypeFieldKeyPath(input) {
2412
- if (input === null || typeof input !== "object") return null;
2413
- const schema = input;
2414
- const bad = ownPrototypeKey(schema.fields);
2459
+ if (!isRecord(input)) return null;
2460
+ const bad = ownPrototypeKey(input.fields);
2415
2461
  if (bad !== null) return `fields.${bad}`;
2416
- for (const [key, spec] of Object.entries(schema.fields ?? {})) {
2417
- const badSub = ownPrototypeKey(spec?.of);
2462
+ for (const [key, spec] of ownEntries(input.fields)) {
2463
+ const badSub = ownPrototypeKey(nameDefiningSubRecord(spec, "of"));
2418
2464
  if (badSub !== null) return `fields.${key}.of.${badSub}`;
2419
2465
  }
2420
- for (const [listName, list] of [["actions", schema.actions], ["collectionActions", schema.collectionActions]]) for (const action of Array.isArray(list) ? list : []) {
2421
- const badParam = ownPrototypeKey(action?.params);
2422
- if (badParam !== null) return `${listName}.params.${badParam}`;
2423
- }
2424
- return null;
2466
+ return prototypeActionParamPath(input);
2425
2467
  }
2426
2468
  var CollectionSchemaZ = z.preprocess((input, ctx) => {
2427
2469
  const bad = prototypeFieldKeyPath(input);
@@ -2437,11 +2479,10 @@ var CollectionSchemaZ = z.preprocess((input, ctx) => {
2437
2479
  //#endregion
2438
2480
  //#region src/collection/server/discovery.ts
2439
2481
  function applyFeedSchemaDefaults(parsed, slug) {
2440
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return parsed;
2441
- const obj = parsed;
2442
- const icon = typeof obj.icon === "string" && obj.icon.trim().length > 0 ? obj.icon : "dynamic_feed";
2482
+ if (!isRecord(parsed)) return parsed;
2483
+ const icon = typeof parsed.icon === "string" && parsed.icon.trim().length > 0 ? parsed.icon : "dynamic_feed";
2443
2484
  return {
2444
- ...obj,
2485
+ ...parsed,
2445
2486
  icon,
2446
2487
  dataPath: `data/feeds/${slug}`
2447
2488
  };
@@ -2544,7 +2585,7 @@ async function loadOneCollection(skillsRoot, slug, source, workspaceRoot) {
2544
2585
  if (!(await stat(schemaPath)).isFile()) return null;
2545
2586
  raw = await readFile(schemaPath, "utf-8");
2546
2587
  } catch (err) {
2547
- if (err.code !== "ENOENT") log.warn("collections", "failed to read schema.json, skipping", {
2588
+ if (!isErrorWithCode(err) || err.code !== "ENOENT") log.warn("collections", "failed to read schema.json, skipping", {
2548
2589
  slug: safeName,
2549
2590
  path: schemaPath,
2550
2591
  error: String(err)
@@ -2598,7 +2639,7 @@ async function collectFromDir(skillsRoot, source, workspaceRoot) {
2598
2639
  try {
2599
2640
  entries = await readdir(skillsRoot);
2600
2641
  } catch (err) {
2601
- if (err.code === "ENOENT") return [];
2642
+ if (isErrorWithCode(err) && err.code === "ENOENT") return [];
2602
2643
  log.warn("collections", "failed to list skills dir, returning empty", {
2603
2644
  root: skillsRoot,
2604
2645
  error: String(err)
@@ -2674,4 +2715,4 @@ function toDetail(collection) {
2674
2715
  //#endregion
2675
2716
  export { skillsStagingDir as $, deleteItem as A, itemFilePath as B, compileCsvQuery as C, MAX_QUERY_ROWS as D, DEFAULT_QUERY_ROWS as E, resolveCreateItemId as F, archiveDir as G, resolveTemplatePath as H, writeItem as I, getWorkspaceRoot as J, collectionsRegistriesConfigPath as K, SCHEMA_FILE as L, isRegularFile as M, listItems as N, BackendUnavailableError as O, readItem as P, setCollectionChangePublisher as Q, isContainedInRoot as R, queryCsv as S, CollectionQueryZ as T, safeRecordId as U, resolveDataDir as V, safeSlugName as W, log as X, isPresetSlug as Y, publishCollectionChange as Z, csvRowToItem as _, toDetail as a, encodeCsvRecordId as b, resolveMutateSet as c, storeFor as d, createForwardingLogger as et, checkpointSqliteDatabase as f, cacheDir as g, MAX_CSV_ROWS as h, resolvePrimaryField as i, generateItemId as j, isBackendUnavailable as k, collectionWritable as l, projectItemFields as m, discoverCollections as n, toSummary as o, pageFromFullRead as p, configureCollectionHost as q, loadCollection as r, CollectionSchemaZ as s, acceptParsedSchema as t, createHostSlot as tt, readOnlyRefusal as u, decodeCsvRecordId as v, compileJsonlQuery as w, normalizeCsvValue as x, dedupeByRecordId as y, isContainedInWorkspace as z };
2676
2717
 
2677
- //# sourceMappingURL=discovery-B4CZvrXR.js.map
2718
+ //# sourceMappingURL=discovery-D6p4ITNR.js.map