@mulmoclaude/core 1.0.1 → 1.2.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 (169) hide show
  1. package/assets/helps/collection-skills.md +28 -0
  2. package/dist/artifacts/paths.cjs +68 -0
  3. package/dist/artifacts/paths.cjs.map +1 -0
  4. package/dist/artifacts/paths.d.ts +45 -0
  5. package/dist/artifacts/paths.js +62 -0
  6. package/dist/artifacts/paths.js.map +1 -0
  7. package/dist/atomic-C_7YpMiM.cjs +102 -0
  8. package/dist/atomic-C_7YpMiM.cjs.map +1 -0
  9. package/dist/atomic-DPpdrJzO.js +89 -0
  10. package/dist/atomic-DPpdrJzO.js.map +1 -0
  11. package/dist/collection/core/completion.d.ts +21 -0
  12. package/dist/collection/core/ids.d.ts +5 -0
  13. package/dist/collection/core/linkTargets.d.ts +14 -0
  14. package/dist/collection/core/ownProp.d.ts +9 -0
  15. package/dist/collection/core/recordKeys.d.ts +14 -0
  16. package/dist/collection/core/schemaRules.d.ts +167 -0
  17. package/dist/collection/core/schemaZ.d.ts +489 -9
  18. package/dist/collection/core/sortValueOf.d.ts +21 -0
  19. package/dist/collection/core/textSearch.d.ts +12 -0
  20. package/dist/collection/index.cjs +109 -29
  21. package/dist/collection/index.cjs.map +1 -1
  22. package/dist/collection/index.d.ts +4 -0
  23. package/dist/collection/index.js +98 -29
  24. package/dist/collection/index.js.map +1 -1
  25. package/dist/collection/registry/server/index.cjs +14 -42
  26. package/dist/collection/registry/server/index.cjs.map +1 -1
  27. package/dist/collection/registry/server/index.js +6 -34
  28. package/dist/collection/registry/server/index.js.map +1 -1
  29. package/dist/collection/server/discovery.d.ts +7 -0
  30. package/dist/collection/server/host.d.ts +3 -8
  31. package/dist/collection/server/index.cjs +10 -9
  32. package/dist/collection/server/index.d.ts +1 -0
  33. package/dist/collection/server/index.js +4 -4
  34. package/dist/collection/server/io.d.ts +0 -88
  35. package/dist/collection/server/skillAssets.d.ts +90 -0
  36. package/dist/collection-watchers/config.d.ts +2 -4
  37. package/dist/collection-watchers/index.cjs +5 -5
  38. package/dist/collection-watchers/index.cjs.map +1 -1
  39. package/dist/collection-watchers/index.js +5 -5
  40. package/dist/collection-watchers/index.js.map +1 -1
  41. package/dist/{discovery-BYIsLqag.js → discovery-DYlEOa9a.js} +426 -392
  42. package/dist/discovery-DYlEOa9a.js.map +1 -0
  43. package/dist/{discovery-Dpz6SWwe.cjs → discovery-UxkvBxV-.cjs} +444 -434
  44. package/dist/discovery-UxkvBxV-.cjs.map +1 -0
  45. package/dist/dist-Cwk0e12G.js +50 -0
  46. package/dist/dist-Cwk0e12G.js.map +1 -0
  47. package/dist/dist-pWpC-b04.cjs +61 -0
  48. package/dist/dist-pWpC-b04.cjs.map +1 -0
  49. package/dist/feeds/index.cjs +2 -2
  50. package/dist/feeds/index.js +2 -2
  51. package/dist/feeds/server/host.d.ts +5 -8
  52. package/dist/feeds/server/index.cjs +143 -61
  53. package/dist/feeds/server/index.cjs.map +1 -1
  54. package/dist/feeds/server/index.js +141 -59
  55. package/dist/feeds/server/index.js.map +1 -1
  56. package/dist/files/atomic.d.ts +34 -0
  57. package/dist/files/index.cjs +47 -0
  58. package/dist/files/index.cjs.map +1 -0
  59. package/dist/files/index.d.ts +3 -0
  60. package/dist/files/index.js +40 -0
  61. package/dist/files/index.js.map +1 -0
  62. package/dist/files/json.d.ts +4 -0
  63. package/dist/files/safe.d.ts +9 -0
  64. package/dist/google/auth.d.ts +1 -1
  65. package/dist/google/collectionDateTime.d.ts +5 -0
  66. package/dist/google/collectionSync.d.ts +2 -2
  67. package/dist/google/fsJson.d.ts +3 -2
  68. package/dist/google/host.d.ts +3 -6
  69. package/dist/google/index.cjs +64 -91
  70. package/dist/google/index.cjs.map +1 -1
  71. package/dist/google/index.d.ts +1 -0
  72. package/dist/google/index.js +57 -85
  73. package/dist/google/index.js.map +1 -1
  74. package/dist/host/hostSlot.d.ts +27 -0
  75. package/dist/{ids-DWmHjm17.cjs → ids-BJ7rdrDN.cjs} +17 -1
  76. package/dist/{ids-DWmHjm17.cjs.map → ids-BJ7rdrDN.cjs.map} +1 -1
  77. package/dist/{ids-D1M1T6KJ.js → ids-Bv6AOW9Z.js} +12 -2
  78. package/dist/{ids-D1M1T6KJ.js.map → ids-Bv6AOW9Z.js.map} +1 -1
  79. package/dist/{ingestTypes-DbuQNuK6.cjs → ingestTypes-CkbM-zrO.cjs} +2 -2
  80. package/dist/{ingestTypes-DbuQNuK6.cjs.map → ingestTypes-CkbM-zrO.cjs.map} +1 -1
  81. package/dist/{ingestTypes-Ci4eOgv-.js → ingestTypes-DuCiZqye.js} +2 -2
  82. package/dist/{ingestTypes-Ci4eOgv-.js.map → ingestTypes-DuCiZqye.js.map} +1 -1
  83. package/dist/notifier/engine.d.ts +2 -4
  84. package/dist/notifier/index.cjs +1 -1
  85. package/dist/notifier/index.js +1 -1
  86. package/dist/{notifier-ChpY0XrY.js → notifier-BdA5qzhe.js} +14 -16
  87. package/dist/notifier-BdA5qzhe.js.map +1 -0
  88. package/dist/{notifier-bS8IEeLA.cjs → notifier-tMsAXyXp.cjs} +14 -16
  89. package/dist/notifier-tMsAXyXp.cjs.map +1 -0
  90. package/dist/plugin-vue/fileWatch.d.ts +5 -0
  91. package/dist/plugin-vue/i18n.cjs +45 -0
  92. package/dist/plugin-vue/i18n.cjs.map +1 -0
  93. package/dist/plugin-vue/i18n.js +44 -0
  94. package/dist/plugin-vue/i18n.js.map +1 -0
  95. package/dist/plugin-vue/index.cjs +98 -0
  96. package/dist/plugin-vue/index.cjs.map +1 -0
  97. package/dist/plugin-vue/index.d.ts +5 -0
  98. package/dist/plugin-vue/index.js +91 -0
  99. package/dist/plugin-vue/index.js.map +1 -0
  100. package/dist/plugin-vue/markdownDoc.d.ts +10 -0
  101. package/dist/plugin-vue/pluginI18n.d.ts +22 -0
  102. package/dist/plugin-vue/useClipboardCopy.d.ts +6 -0
  103. package/dist/plugin-vue/useFileWatch.d.ts +8 -0
  104. package/dist/plugin-vue/useMarkdownDoc.d.ts +3 -0
  105. package/dist/{promptSafety-Bugq2kqL.js → promptSafety-BNelBKhh.js} +79 -7
  106. package/dist/promptSafety-BNelBKhh.js.map +1 -0
  107. package/dist/{promptSafety-DbE6eZmP.cjs → promptSafety-QAtADXUF.cjs} +108 -6
  108. package/dist/promptSafety-QAtADXUF.cjs.map +1 -0
  109. package/dist/remote-host/server/hostRunner.d.ts +2 -1
  110. package/dist/remote-host/server/index.cjs +6 -5
  111. package/dist/remote-host/server/index.cjs.map +1 -1
  112. package/dist/remote-host/server/index.js +3 -2
  113. package/dist/remote-host/server/index.js.map +1 -1
  114. package/dist/scheduler/index.cjs +3 -3
  115. package/dist/scheduler/index.cjs.map +1 -1
  116. package/dist/scheduler/index.js +2 -2
  117. package/dist/scheduler/index.js.map +1 -1
  118. package/dist/scheduler/task-manager.d.ts +2 -5
  119. package/dist/{server-CtCBLYgZ.js → server-CnwgECW-.js} +209 -41
  120. package/dist/server-CnwgECW-.js.map +1 -0
  121. package/dist/{server-BjWrcc5T.cjs → server-DFo3zFYB.cjs} +250 -46
  122. package/dist/server-DFo3zFYB.cjs.map +1 -0
  123. package/dist/skill-bridge/index.cjs +1 -1
  124. package/dist/skill-bridge/index.js +1 -1
  125. package/dist/translation/client.cjs +16 -0
  126. package/dist/translation/client.cjs.map +1 -1
  127. package/dist/translation/client.d.ts +6 -0
  128. package/dist/translation/client.js +16 -1
  129. package/dist/translation/client.js.map +1 -1
  130. package/dist/utils/errors.d.ts +2 -1
  131. package/dist/utils/fetch.cjs +36 -0
  132. package/dist/utils/fetch.cjs.map +1 -0
  133. package/dist/{collection/registry/server → utils}/fetch.d.ts +5 -3
  134. package/dist/utils/fetch.js +34 -0
  135. package/dist/utils/fetch.js.map +1 -0
  136. package/dist/utils/index.cjs +11 -3
  137. package/dist/utils/index.cjs.map +1 -0
  138. package/dist/utils/index.js +9 -1
  139. package/dist/utils/index.js.map +1 -0
  140. package/dist/whisper/index.cjs +9 -9
  141. package/dist/whisper/index.cjs.map +1 -1
  142. package/dist/whisper/index.js +3 -3
  143. package/dist/wiki/index.cjs +3 -16
  144. package/dist/wiki/index.cjs.map +1 -1
  145. package/dist/wiki/index.js +1 -14
  146. package/dist/wiki/index.js.map +1 -1
  147. package/dist/wiki/render.d.ts +2 -4
  148. package/dist/wiki/server/frontmatter.d.ts +2 -6
  149. package/dist/wiki/server/index.cjs +4 -40
  150. package/dist/wiki/server/index.cjs.map +1 -1
  151. package/dist/wiki/server/index.js +2 -38
  152. package/dist/wiki/server/index.js.map +1 -1
  153. package/dist/workspace-setup/index.js +22 -6
  154. package/dist/workspace-setup/index.js.map +1 -1
  155. package/package.json +50 -8
  156. package/dist/collection/server/atomic.d.ts +0 -1
  157. package/dist/discovery-BYIsLqag.js.map +0 -1
  158. package/dist/discovery-Dpz6SWwe.cjs.map +0 -1
  159. package/dist/errors-7P5eMOSX.cjs +0 -30
  160. package/dist/errors-7P5eMOSX.cjs.map +0 -1
  161. package/dist/errors-eid6Mes3.js +0 -19
  162. package/dist/errors-eid6Mes3.js.map +0 -1
  163. package/dist/google/fetch.d.ts +0 -8
  164. package/dist/notifier-ChpY0XrY.js.map +0 -1
  165. package/dist/notifier-bS8IEeLA.cjs.map +0 -1
  166. package/dist/promptSafety-Bugq2kqL.js.map +0 -1
  167. package/dist/promptSafety-DbE6eZmP.cjs.map +0 -1
  168. package/dist/server-BjWrcc5T.cjs.map +0 -1
  169. package/dist/server-CtCBLYgZ.js.map +0 -1
@@ -1,38 +1,30 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_rolldown_runtime = require("../rolldown-runtime-D6vf50IK.cjs");
3
- const require_errors = require("../errors-7P5eMOSX.cjs");
3
+ const require_dist = require("../dist-pWpC-b04.cjs");
4
4
  const require_text = require("../text-CaAh9ZSO.cjs");
5
- const require_discovery = require("../discovery-Dpz6SWwe.cjs");
5
+ const require_atomic = require("../atomic-C_7YpMiM.cjs");
6
+ const require_utils_fetch = require("../utils/fetch.cjs");
7
+ const require_discovery = require("../discovery-UxkvBxV-.cjs");
8
+ let node_fs = require("node:fs");
6
9
  let node_path = require("node:path");
7
10
  node_path = require_rolldown_runtime.__toESM(node_path, 1);
8
- let node_fs = require("node:fs");
9
- let node_fs_promises = require("node:fs/promises");
10
11
  let node_crypto = require("node:crypto");
12
+ let node_fs_promises = require("node:fs/promises");
11
13
  let node_os = require("node:os");
12
14
  let _receptron_task_scheduler = require("@receptron/task-scheduler");
13
15
  let node_http = require("node:http");
14
16
  node_http = require_rolldown_runtime.__toESM(node_http, 1);
15
17
  let google_auth_library = require("google-auth-library");
16
18
  //#region src/google/host.ts
17
- var hostLog = {
18
- error: () => void 0,
19
- warn: () => void 0,
20
- info: () => void 0,
21
- debug: () => void 0
22
- };
19
+ var hostSlot = require_discovery.createHostSlot("@mulmoclaude/core/google: configureGoogleHost()");
23
20
  function configureGoogleHost(binding) {
24
- hostLog = binding.log;
21
+ hostSlot.set(binding.log);
25
22
  }
26
- var log = {
27
- error: (prefix, message, data) => hostLog.error(prefix, message, data),
28
- warn: (prefix, message, data) => hostLog.warn(prefix, message, data),
29
- info: (prefix, message, data) => hostLog.info(prefix, message, data),
30
- debug: (prefix, message, data) => hostLog.debug(prefix, message, data)
31
- };
23
+ var log = require_discovery.createForwardingLogger(() => hostSlot.peek());
32
24
  //#endregion
33
25
  //#region src/google/datetime.ts
34
26
  var ISO_DATE_TIME_WITH_OFFSET_RE = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:Z|[+-](\d{2}):(\d{2}))$/;
35
- var FRACTIONAL_SECONDS_RE = /\.\d+(?=Z|[+-])/;
27
+ var FRACTIONAL_SECONDS_RE$1 = /\.\d+(?=Z|[+-])/;
36
28
  var MAX_HOUR = 23;
37
29
  var MAX_MINUTE = 59;
38
30
  var MAX_SECOND = 59;
@@ -41,7 +33,7 @@ var isRealCalendarDate = (year, month, day) => {
41
33
  return roundTrip.getUTCFullYear() === year && roundTrip.getUTCMonth() === month - 1 && roundTrip.getUTCDate() === day;
42
34
  };
43
35
  var isIsoDateTimeWithOffset = (value) => {
44
- const match = ISO_DATE_TIME_WITH_OFFSET_RE.exec(value.replace(FRACTIONAL_SECONDS_RE, ""));
36
+ const match = ISO_DATE_TIME_WITH_OFFSET_RE.exec(value.replace(FRACTIONAL_SECONDS_RE$1, ""));
45
37
  if (!match) return false;
46
38
  const [year = 0, month = 0, day = 0, hour = 0, minute = 0, second = 0] = match.slice(1, 7).map(Number);
47
39
  const timeInRange = hour <= MAX_HOUR && minute <= MAX_MINUTE && second <= MAX_SECOND;
@@ -118,6 +110,7 @@ async function loadClientSecret(home) {
118
110
  }
119
111
  //#endregion
120
112
  //#region src/google/fsJson.ts
113
+ var JSON_INDENT = 2;
121
114
  async function readJsonOrNull(filePath) {
122
115
  try {
123
116
  const raw = await node_fs.promises.readFile(filePath, "utf-8");
@@ -126,39 +119,14 @@ async function readJsonOrNull(filePath) {
126
119
  return null;
127
120
  }
128
121
  }
129
- var IS_WINDOWS = process.platform === "win32";
130
- var RENAME_RETRY_DELAYS_MS = [
131
- 30,
132
- 100,
133
- 300
134
- ];
135
- var hasErrnoCode = (err) => typeof err === "object" && err !== null && "code" in err && typeof err.code === "string";
136
- var isTransientRenameError = (err) => IS_WINDOWS && hasErrnoCode(err) && (err.code === "EPERM" || err.code === "EBUSY" || err.code === "EACCES");
137
- async function renameWithWindowsRetry(fromPath, toPath) {
138
- for (const delayMs of RENAME_RETRY_DELAYS_MS) try {
139
- await node_fs.promises.rename(fromPath, toPath);
140
- return;
141
- } catch (err) {
142
- if (!isTransientRenameError(err)) throw err;
143
- await new Promise((resolve) => setTimeout(resolve, delayMs));
144
- }
145
- await node_fs.promises.rename(fromPath, toPath);
146
- }
147
- /** tmp-write + rename so readers never see a half-written file; `mode`
148
- * applies to the tmp file and survives the rename. */
122
+ /** tmp-write + rename so readers never see a half-written file; `mode` applies
123
+ * to the tmp file and survives the rename. A single-writer token file, so the
124
+ * staging name is stable (`uniqueTmp: false`). */
149
125
  async function writeJsonAtomicWithMode(filePath, data, mode) {
150
- const tmp = `${filePath}.tmp`;
151
- await node_fs.promises.mkdir(node_path.default.dirname(filePath), { recursive: true });
152
- try {
153
- await node_fs.promises.writeFile(tmp, JSON.stringify(data, null, 2), {
154
- encoding: "utf-8",
155
- mode
156
- });
157
- await renameWithWindowsRetry(tmp, filePath);
158
- } catch (err) {
159
- await node_fs.promises.unlink(tmp).catch(() => void 0);
160
- throw err;
161
- }
126
+ await require_atomic.writeFileAtomic(filePath, JSON.stringify(data, null, JSON_INDENT), {
127
+ mode,
128
+ uniqueTmp: false
129
+ });
162
130
  }
163
131
  //#endregion
164
132
  //#region src/google/tokenStore.ts
@@ -200,36 +168,6 @@ async function deleteGoogleTokens(home) {
200
168
  await (0, node_fs_promises.rm)(googleTokenPath(home), { force: true });
201
169
  }
202
170
  //#endregion
203
- //#region src/google/fetch.ts
204
- var DEFAULT_FETCH_TIMEOUT_MS = 10 * ONE_SECOND_MS;
205
- /** `fetch` with a finite timeout. Rejects with a `TimeoutError` once
206
- * `timeoutMs` elapses. Composes with a caller-supplied `signal` so external
207
- * cancellation still works. */
208
- async function fetchWithTimeout(url, init = {}) {
209
- const { timeoutMs = DEFAULT_FETCH_TIMEOUT_MS, signal: callerSignal, ...rest } = init;
210
- if (callerSignal?.aborted) throw callerSignal.reason ?? new DOMException("Aborted", "AbortError");
211
- const controller = new AbortController();
212
- const timer = setTimeout(() => {
213
- controller.abort(new DOMException(`fetch timed out after ${timeoutMs}ms`, "TimeoutError"));
214
- }, timeoutMs);
215
- const unsubscribeCaller = bridgeExternalSignal(callerSignal, controller);
216
- try {
217
- return await fetch(url, {
218
- ...rest,
219
- signal: controller.signal
220
- });
221
- } finally {
222
- clearTimeout(timer);
223
- unsubscribeCaller?.();
224
- }
225
- }
226
- function bridgeExternalSignal(external, controller) {
227
- if (!external) return null;
228
- const onAbort = () => controller.abort(external.reason);
229
- external.addEventListener("abort", onAbort, { once: true });
230
- return () => external.removeEventListener("abort", onAbort);
231
- }
232
- //#endregion
233
171
  //#region src/google/broker.ts
234
172
  var DEFAULT_BROKER_BASE_URL = "https://asia-northeast1-mulmoserver.cloudfunctions.net";
235
173
  var BROKER_TIMEOUT_MS = 20 * ONE_SECOND_MS;
@@ -266,13 +204,13 @@ var brokerFetch = async (url, init = {}) => {
266
204
  assertSecureBrokerUrl(url);
267
205
  let response;
268
206
  try {
269
- response = await fetchWithTimeout(url, {
207
+ response = await require_utils_fetch.fetchWithTimeout(url, {
270
208
  ...init,
271
209
  timeoutMs: BROKER_TIMEOUT_MS,
272
210
  headers: { "Content-Type": "application/json" }
273
211
  });
274
212
  } catch (err) {
275
- throw new Error(`Google sign-in service unreachable — check the network connection and retry (${require_errors.errorMessage(err)})`);
213
+ throw new Error(`Google sign-in service unreachable — check the network connection and retry (${require_dist.errorMessage(err)})`);
276
214
  }
277
215
  if (!response.ok) throw new Error(`Google sign-in service returned HTTP ${response.status}`);
278
216
  try {
@@ -403,7 +341,7 @@ var REVOKE_URL = "https://oauth2.googleapis.com/revoke";
403
341
  * Revoke failures are logged but never block the local delete — Google may
404
342
  * already consider the token invalid, and keeping the file would leave the
405
343
  * user unable to unlink. */
406
- async function unlinkGoogle(home, revokeFetch = fetchWithTimeout) {
344
+ async function unlinkGoogle(home, revokeFetch = require_utils_fetch.fetchWithTimeout) {
407
345
  const saved = await loadGoogleTokens(home);
408
346
  const token = saved?.refresh_token ?? saved?.access_token;
409
347
  if (token) try {
@@ -414,7 +352,7 @@ async function unlinkGoogle(home, revokeFetch = fetchWithTimeout) {
414
352
  });
415
353
  if (!response.ok) log.warn("google", "token revoke returned non-ok", { status: response.status });
416
354
  } catch (err) {
417
- log.warn("google", "token revoke failed, deleting local tokens anyway", { error: require_errors.errorMessage(err) });
355
+ log.warn("google", "token revoke failed, deleting local tokens anyway", { error: require_dist.errorMessage(err) });
418
356
  }
419
357
  await deleteGoogleTokens(home);
420
358
  }
@@ -565,7 +503,7 @@ var createGoogleAuthFlow = (authorize) => {
565
503
  signal: controller.signal
566
504
  }).then(() => log.info("google", "authorize flow completed")).catch((err) => {
567
505
  if (!controller.signal.aborted) {
568
- lastError = require_errors.errorMessage(err);
506
+ lastError = require_dist.errorMessage(err);
569
507
  log.warn("google", "authorize flow failed", { error: lastError });
570
508
  }
571
509
  reject(err instanceof Error ? err : new Error(String(err)));
@@ -621,7 +559,7 @@ var googleApiError = (apiLabel, status, body) => {
621
559
  };
622
560
  async function googleRequest(apiLabel, accessToken, url, init = {}) {
623
561
  const { contentType = "application/json", expectText = false, ...rest } = init;
624
- const response = await fetchWithTimeout(url, {
562
+ const response = await require_utils_fetch.fetchWithTimeout(url, {
625
563
  ...rest,
626
564
  timeoutMs: GOOGLE_API_TIMEOUT_MS,
627
565
  headers: {
@@ -630,7 +568,7 @@ async function googleRequest(apiLabel, accessToken, url, init = {}) {
630
568
  }
631
569
  });
632
570
  if (!response.ok) {
633
- const body = await response.text().catch((err) => require_errors.errorMessage(err));
571
+ const body = await response.text().catch((err) => require_dist.errorMessage(err));
634
572
  throw googleApiError(apiLabel, response.status, body);
635
573
  }
636
574
  if (expectText) return await response.text();
@@ -848,15 +786,49 @@ async function clearCalendarSyncToken(calendarId, workspaceRoot) {
848
786
  await updateState((tokens) => Object.fromEntries(Object.entries(tokens).filter(([key]) => key !== dropped)), workspaceRoot);
849
787
  }
850
788
  //#endregion
789
+ //#region src/google/collectionDateTime.ts
790
+ /** All-day events carry no clock; midnight is where the day view places them. */
791
+ var ALL_DAY_CLOCK = "00:00";
792
+ var DATE_ONLY_RE = /^\d{4}-\d{2}-\d{2}$/;
793
+ /** RFC3339 zone designator: `Z`, `+09:00`, `-0500`. */
794
+ var ZONE_SUFFIX_RE = /(?:Z|[+-]\d{2}:?\d{2})$/;
795
+ /** Fractional seconds have no place in the collection datetime shape. */
796
+ var FRACTIONAL_SECONDS_RE = /\.\d+$/;
797
+ /** Normalise one Google Calendar time value for storage in a `datetime` field.
798
+ * Anything that isn't a recognisable Google shape (empty string, non-string,
799
+ * free text) is returned untouched — the record lint should report it rather
800
+ * than have this function invent a value. */
801
+ function toCollectionDateTime(value) {
802
+ if (typeof value !== "string") return value;
803
+ const trimmed = value.trim();
804
+ if (DATE_ONLY_RE.test(trimmed)) return `${trimmed}T${ALL_DAY_CLOCK}`;
805
+ if (!trimmed.includes("T")) return value;
806
+ return trimmed.replace(ZONE_SUFFIX_RE, "").replace(FRACTIONAL_SECONDS_RE, "");
807
+ }
808
+ //#endregion
851
809
  //#region src/google/collectionSync.ts
852
810
  var GOOGLE_CALENDAR_SYNC_TASK_ID = "system:google-calendar-sync";
853
811
  var DEFAULT_SYNC_INTERVAL_MS = 3600 * 1e3;
854
812
  var CANCELLED_STATUS = "cancelled";
813
+ var DATETIME_FIELD_TYPE = "datetime";
814
+ /** Own-property lookup, mirroring what the record lint reads: a spec reachable
815
+ * only through the prototype chain is not a DECLARED field, so it must not
816
+ * decide how a value is stored. */
817
+ function declaredSpec(fields, field) {
818
+ return Object.hasOwn(fields, field) ? fields[field] : void 0;
819
+ }
820
+ /** Google's raw value is normalised only into a field the schema declares as
821
+ * `datetime` — that is the type whose stored shape the record lint, the
822
+ * calendar grid and the day view all parse (#2310). A user who maps `start`
823
+ * onto a `string` field asked for Google's value verbatim and keeps it. */
824
+ function projectValue(fields, field, value) {
825
+ return declaredSpec(fields, field)?.type === DATETIME_FIELD_TYPE ? toCollectionDateTime(value) : value;
826
+ }
855
827
  /** Project one Google event onto the collection's own field names. The
856
828
  * primary field always takes the event id — upsert-by-id is what keeps the
857
829
  * sync idempotent, so it is deliberately not remappable. */
858
- function toCollectionRecord(event, map, primaryKey) {
859
- const mapped = Object.entries(map).map(([field, source]) => [field, event[source]]);
830
+ function toCollectionRecord(event, map, primaryKey, fields) {
831
+ const mapped = Object.entries(map).map(([field, source]) => [field, projectValue(fields, field, event[source])]);
860
832
  return {
861
833
  ...Object.fromEntries(mapped),
862
834
  [primaryKey]: event.id
@@ -897,7 +869,7 @@ async function applyEvent(collection, event, workspaceRoot) {
897
869
  const deleted = await store.delete(event.id);
898
870
  return classifyDelete(event.id, deleted.kind);
899
871
  }
900
- const record = toCollectionRecord(event, schema.googleCalendar?.map ?? {}, schema.primaryKey);
872
+ const record = toCollectionRecord(event, schema.googleCalendar?.map ?? {}, schema.primaryKey, schema.fields);
901
873
  const written = await store.write(event.id, record);
902
874
  return classifyWrite(event.id, written.kind);
903
875
  } catch (error) {
@@ -1231,6 +1203,7 @@ exports.syncCalendarEvents = syncCalendarEvents;
1231
1203
  exports.syncCalendarGroup = syncCalendarGroup;
1232
1204
  exports.syncDueCalendarCollections = syncDueCalendarCollections;
1233
1205
  exports.toCalendarSummary = toCalendarSummary;
1206
+ exports.toCollectionDateTime = toCollectionDateTime;
1234
1207
  exports.toCollectionRecord = toCollectionRecord;
1235
1208
  exports.toDriveFileSummary = toDriveFileSummary;
1236
1209
  exports.toEventSummary = toEventSummary;