@rubytech/create-maxy-code 0.1.475 → 0.1.477

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 (89) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/storage-broker/dist/cf-exec.d.ts.map +1 -1
  3. package/payload/platform/lib/storage-broker/dist/cf-exec.js +3 -2
  4. package/payload/platform/lib/storage-broker/dist/cf-exec.js.map +1 -1
  5. package/payload/platform/lib/storage-broker/src/cf-exec.ts +3 -2
  6. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +3 -3
  7. package/payload/platform/plugins/cloudflare/skills/calendar-site/template/schema.sql +13 -3
  8. package/payload/platform/plugins/docs/references/calendar-booking.md +2 -2
  9. package/payload/platform/plugins/scheduling/PLUGIN.md +2 -2
  10. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block-sqlite.test.d.ts +2 -0
  11. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block-sqlite.test.d.ts.map +1 -0
  12. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block-sqlite.test.js +150 -0
  13. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block-sqlite.test.js.map +1 -0
  14. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block.test.d.ts +2 -0
  15. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block.test.d.ts.map +1 -0
  16. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block.test.js +248 -0
  17. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block.test.js.map +1 -0
  18. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-reconcile.test.js +158 -23
  19. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-reconcile.test.js.map +1 -1
  20. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-schema-migration.test.d.ts +2 -0
  21. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-schema-migration.test.d.ts.map +1 -0
  22. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-schema-migration.test.js +200 -0
  23. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-schema-migration.test.js.map +1 -0
  24. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/connector-sync-provision.test.d.ts +2 -0
  25. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/connector-sync-provision.test.d.ts.map +1 -0
  26. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/connector-sync-provision.test.js +309 -0
  27. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/connector-sync-provision.test.js.map +1 -0
  28. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/d1-command.test.js +219 -19
  29. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/d1-command.test.js.map +1 -1
  30. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.d.ts +184 -11
  31. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.d.ts.map +1 -1
  32. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.js +211 -22
  33. package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.js.map +1 -1
  34. package/payload/platform/plugins/scheduling/mcp/dist/lib/connector-sync-provision.d.ts +109 -0
  35. package/payload/platform/plugins/scheduling/mcp/dist/lib/connector-sync-provision.d.ts.map +1 -0
  36. package/payload/platform/plugins/scheduling/mcp/dist/lib/connector-sync-provision.js +213 -0
  37. package/payload/platform/plugins/scheduling/mcp/dist/lib/connector-sync-provision.js.map +1 -0
  38. package/payload/platform/plugins/scheduling/mcp/dist/lib/d1-command.d.ts +80 -8
  39. package/payload/platform/plugins/scheduling/mcp/dist/lib/d1-command.d.ts.map +1 -1
  40. package/payload/platform/plugins/scheduling/mcp/dist/lib/d1-command.js +202 -8
  41. package/payload/platform/plugins/scheduling/mcp/dist/lib/d1-command.js.map +1 -1
  42. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.d.ts +6 -4
  43. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.d.ts.map +1 -1
  44. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js +411 -141
  45. package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js.map +1 -1
  46. package/payload/platform/scripts/cpu-triage.sh +313 -0
  47. package/payload/server/{chunk-Y6V45R6M.js → chunk-JECAP3Z2.js} +296 -63
  48. package/payload/server/maxy-edge.js +1 -1
  49. package/payload/server/public/activity.html +6 -6
  50. package/payload/server/public/assets/{AdminLoginScreens-B44KbEuC.js → AdminLoginScreens-mEKP4pDi.js} +1 -1
  51. package/payload/server/public/assets/{AdminShell-BjxV2qkb.js → AdminShell-BwSaZ88d.js} +1 -1
  52. package/payload/server/public/assets/{Checkbox-DSeZNGNA.js → Checkbox-CN4uQ80w.js} +1 -1
  53. package/payload/server/public/assets/activity-CIQ8ozNZ.js +1 -0
  54. package/payload/server/public/assets/{admin-B4LpTCur.js → admin-BVhgfgs1.js} +1 -1
  55. package/payload/server/public/assets/{browser-CEFvaWwI.js → browser-D-HMmHcM.js} +1 -1
  56. package/payload/server/public/assets/{calendar-B3zo6V34.js → calendar-tGWvFyFn.js} +1 -1
  57. package/payload/server/public/assets/chat-DkttVxAz.js +1 -0
  58. package/payload/server/public/assets/chevron-left-CZ4ez9G5.js +1 -0
  59. package/payload/server/public/assets/data-P-mcmnNi.js +1 -0
  60. package/payload/server/public/assets/{graph-labels-GlTEIYJj.js → graph-labels-qnBleOE6.js} +1 -1
  61. package/payload/server/public/assets/{graph-RaOrnSf2.js → graph-zEw610xK.js} +1 -1
  62. package/payload/server/public/assets/{maximize-2-CHDa8p5h.js → maximize-2-BlTjXT_Y.js} +1 -1
  63. package/payload/server/public/assets/{operator-DtqHY4jV.js → operator-C7oIw2PG.js} +1 -1
  64. package/payload/server/public/assets/{page-DxnWgPtd.js → page-CTP7OFZa.js} +1 -1
  65. package/payload/server/public/assets/{page-CkhFj8GQ.js → page-DL6Zsdvk.js} +1 -1
  66. package/payload/server/public/assets/{public-D5TUfQ3I.js → public-r1A9dqR_.js} +1 -1
  67. package/payload/server/public/assets/{rotate-ccw-BJjVAQQY.js → rotate-ccw-CzkkKx4-.js} +1 -1
  68. package/payload/server/public/assets/{tasks-CM9pYYG1.js → tasks-fIJwYFWG.js} +1 -1
  69. package/payload/server/public/assets/{time-entry-format-C5RYEYjM.js → time-entry-format-j669DgXj.js} +1 -1
  70. package/payload/server/public/assets/{triangle-alert-DLfIsuo_.js → triangle-alert-BPgCgIef.js} +1 -1
  71. package/payload/server/public/assets/{useCopyFeedback-7yKeiAzV.js → useCopyFeedback-BmLa2aes.js} +1 -1
  72. package/payload/server/public/assets/{useSelectionMode-CWlh6AEq.js → useSelectionMode-DnnM7A8-.js} +1 -1
  73. package/payload/server/public/assets/{useSubAccountSwitcher-B2n9QPVI.css → useSubAccountSwitcher-C_E8h07P.css} +1 -1
  74. package/payload/server/public/assets/{useVoiceRecorder-j3ZArZ2H.js → useVoiceRecorder-u2dwZfen.js} +1 -1
  75. package/payload/server/public/browser.html +5 -5
  76. package/payload/server/public/calendar.html +6 -6
  77. package/payload/server/public/chat.html +12 -12
  78. package/payload/server/public/data.html +11 -11
  79. package/payload/server/public/graph.html +11 -11
  80. package/payload/server/public/index.html +14 -14
  81. package/payload/server/public/operator.html +14 -14
  82. package/payload/server/public/public.html +12 -12
  83. package/payload/server/public/tasks.html +5 -5
  84. package/payload/server/server.js +358 -189
  85. package/payload/server/public/assets/activity-Mz1BhgB1.js +0 -1
  86. package/payload/server/public/assets/chat-5tJdo9i_.js +0 -1
  87. package/payload/server/public/assets/chevron-left-BJllrPBj.js +0 -1
  88. package/payload/server/public/assets/data-Du79EgR-.js +0 -1
  89. /package/payload/server/public/assets/{useSubAccountSwitcher-CBlKgwms.js → useSubAccountSwitcher-CFa6ZAT1.js} +0 -0
@@ -1,12 +1,84 @@
1
1
  /**
2
- * Build the argv for a D1 SQL read. Resolves wrangler via `npx` — the brand
3
- * server PATH carries no global `wrangler`, only `npx wrangler` resolves, the
4
- * same form the proven forward-path availability publisher uses. Keeping this
5
- * pure lets the argv be asserted without importing the side-effectful
6
- * dispatcher script (Task 1350).
2
+ * D1 access for booking reconcile, over Cloudflare's HTTP query API.
3
+ *
4
+ * Task 1805 this module used to return an argv for `npx wrangler d1 execute`,
5
+ * which `reconcile-bookings.ts` ran through spawnSync once per SQL statement. On
6
+ * a four-brand laptop that was a continuous ~0.6 of a core: every statement paid
7
+ * a cold start of npm, then node, then wrangler's cli.js and its esbuild service.
8
+ * Nothing was failing; the cost was the transport choice, and it scaled with
9
+ * accounts x statements x tick rate. Task 1350's ENOENT finding (only `npx
10
+ * wrangler` resolves on the brand server PATH) is not reopened — it is retired,
11
+ * because no binary is resolved here at all.
12
+ *
13
+ * The two bounds the spawn carried are preserved deliberately, not incidentally:
14
+ * its `timeout: 60_000` becomes a request bound, and its `maxBuffer: 16MB`
15
+ * becomes a cap enforced *while streaming* the response. Both exist so a stalled
16
+ * Cloudflare cannot wedge a run that is holding the reconcile PID lock.
17
+ *
18
+ * The endpoint is keyed by database uuid but every caller holds a database name,
19
+ * so the client resolves name -> uuid once per run and caches it.
7
20
  */
8
- export declare function buildD1Command(dbName: string, sql: string): {
9
- command: string;
10
- args: string[];
21
+ /** The bound each request runs under. Inherited from the spawnSync `timeout`. */
22
+ export declare const D1_TIMEOUT_MS = 60000;
23
+ /** The most response bytes we will read. Inherited from the spawnSync `maxBuffer`. */
24
+ export declare const D1_RESPONSE_CAP_BYTES: number;
25
+ export interface D1Response {
26
+ ok: boolean;
27
+ status: number;
28
+ /**
29
+ * The body as byte chunks. Deliberately not a `text()`: the cap has to be able
30
+ * to stop the read part-way, which is the property `maxBuffer` provided. A
31
+ * whole-body accessor could only check the size after paying for it.
32
+ */
33
+ chunks: AsyncIterable<Uint8Array>;
34
+ }
35
+ export interface D1RequestInit {
36
+ method: string;
37
+ headers: Record<string, string>;
38
+ body?: string;
39
+ signal: AbortSignal;
40
+ }
41
+ /** The HTTP seam. Injectable so the transport is unit-testable without a network. */
42
+ export type D1Fetch = (url: string, init: D1RequestInit) => Promise<D1Response>;
43
+ /** Compose the request that resolves a database name to its uuid. Pure. */
44
+ export declare function buildD1LookupRequest(accountId: string, dbName: string, token: string): {
45
+ url: string;
46
+ init: Omit<D1RequestInit, "signal">;
11
47
  };
48
+ /**
49
+ * Compose the request that runs one SQL statement. Pure.
50
+ *
51
+ * `params` binds the statement's values server-side (`?1`, `?2`, … in the SQL).
52
+ * The spawn this replaced had no parameter channel, so every value reached D1
53
+ * folded into the statement text; the HTTP API takes them separately, so the
54
+ * sweep's bookingId is bound rather than interpolated.
55
+ *
56
+ * Typed `string[]` because the API documents this field as an array of string.
57
+ * A wider type would let a caller pass a value the endpoint rejects at runtime,
58
+ * on a periodic path where that surfaces as a failed tick rather than a build error.
59
+ */
60
+ export declare function buildD1QueryRequest(accountId: string, databaseId: string, sql: string, token: string, params?: string[]): {
61
+ url: string;
62
+ init: Omit<D1RequestInit, "signal">;
63
+ };
64
+ export interface D1Client {
65
+ /**
66
+ * Run one SQL statement against the named database and return its rows.
67
+ * Values go in `params` (`?1`, `?2`, … in the SQL), never in the statement text.
68
+ */
69
+ query(dbName: string, sql: string, params?: string[]): Promise<Record<string, unknown>[]>;
70
+ /** SQL statements issued so far. Name lookups are not statements and are excluded. */
71
+ readonly statements: number;
72
+ /** Total wall time spent in this client, lookups included. */
73
+ readonly elapsedMs: number;
74
+ }
75
+ /**
76
+ * One client per account run. It owns the name -> uuid cache, so a tick pays one
77
+ * lookup for its database and then issues statements against the resolved id.
78
+ */
79
+ export declare function createD1Client(opts: {
80
+ accountId: string;
81
+ token: string;
82
+ fetchFn?: D1Fetch;
83
+ }): D1Client;
12
84
  //# sourceMappingURL=d1-command.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"d1-command.d.ts","sourceRoot":"","sources":["../../src/lib/d1-command.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,MAAM,EACd,GAAG,EAAE,MAAM,GACV;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,EAAE,CAAA;CAAE,CAKrC"}
1
+ {"version":3,"file":"d1-command.d.ts","sourceRoot":"","sources":["../../src/lib/d1-command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,iFAAiF;AACjF,eAAO,MAAM,aAAa,QAAS,CAAC;AAEpC,sFAAsF;AACtF,eAAO,MAAM,qBAAqB,QAAmB,CAAC;AAItD,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,MAAM,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,WAAW,CAAC;CACrB;AAED,qFAAqF;AACrF,MAAM,MAAM,OAAO,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;AAehF,2EAA2E;AAC3E,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GACZ;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAA;CAAE,CAMtD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,MAAM,GAAE,MAAM,EAAO,GACpB;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,IAAI,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAA;CAAE,CAStD;AAkFD,MAAM,WAAW,QAAQ;IACvB;;;OAGG;IACH,KAAK,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;IAC1F,sFAAsF;IACtF,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,8DAA8D;IAC9D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,GAAG,QAAQ,CAwEX"}
@@ -1,14 +1,208 @@
1
1
  /**
2
- * Build the argv for a D1 SQL read. Resolves wrangler via `npx` — the brand
3
- * server PATH carries no global `wrangler`, only `npx wrangler` resolves, the
4
- * same form the proven forward-path availability publisher uses. Keeping this
5
- * pure lets the argv be asserted without importing the side-effectful
6
- * dispatcher script (Task 1350).
2
+ * D1 access for booking reconcile, over Cloudflare's HTTP query API.
3
+ *
4
+ * Task 1805 this module used to return an argv for `npx wrangler d1 execute`,
5
+ * which `reconcile-bookings.ts` ran through spawnSync once per SQL statement. On
6
+ * a four-brand laptop that was a continuous ~0.6 of a core: every statement paid
7
+ * a cold start of npm, then node, then wrangler's cli.js and its esbuild service.
8
+ * Nothing was failing; the cost was the transport choice, and it scaled with
9
+ * accounts x statements x tick rate. Task 1350's ENOENT finding (only `npx
10
+ * wrangler` resolves on the brand server PATH) is not reopened — it is retired,
11
+ * because no binary is resolved here at all.
12
+ *
13
+ * The two bounds the spawn carried are preserved deliberately, not incidentally:
14
+ * its `timeout: 60_000` becomes a request bound, and its `maxBuffer: 16MB`
15
+ * becomes a cap enforced *while streaming* the response. Both exist so a stalled
16
+ * Cloudflare cannot wedge a run that is holding the reconcile PID lock.
17
+ *
18
+ * The endpoint is keyed by database uuid but every caller holds a database name,
19
+ * so the client resolves name -> uuid once per run and caches it.
7
20
  */
8
- export function buildD1Command(dbName, sql) {
21
+ /** The bound each request runs under. Inherited from the spawnSync `timeout`. */
22
+ export const D1_TIMEOUT_MS = 60_000;
23
+ /** The most response bytes we will read. Inherited from the spawnSync `maxBuffer`. */
24
+ export const D1_RESPONSE_CAP_BYTES = 16 * 1024 * 1024;
25
+ const API = "https://api.cloudflare.com/client/v4";
26
+ const defaultFetch = async (url, init) => {
27
+ const res = await fetch(url, init);
28
+ return { ok: res.ok, status: res.status, chunks: bodyChunks(res) };
29
+ };
30
+ async function* bodyChunks(res) {
31
+ // A bodyless response (204, or a HEAD-shaped reply) yields nothing. That reads
32
+ // as an empty body, which the envelope guard below rejects with its status —
33
+ // never as a silently empty row set.
34
+ if (!res.body)
35
+ return;
36
+ for await (const chunk of res.body)
37
+ yield chunk;
38
+ }
39
+ /** Compose the request that resolves a database name to its uuid. Pure. */
40
+ export function buildD1LookupRequest(accountId, dbName, token) {
41
+ const qs = new URLSearchParams({ name: dbName });
42
+ return {
43
+ url: `${API}/accounts/${accountId}/d1/database?${qs}`,
44
+ init: { method: "GET", headers: { Authorization: `Bearer ${token}` } },
45
+ };
46
+ }
47
+ /**
48
+ * Compose the request that runs one SQL statement. Pure.
49
+ *
50
+ * `params` binds the statement's values server-side (`?1`, `?2`, … in the SQL).
51
+ * The spawn this replaced had no parameter channel, so every value reached D1
52
+ * folded into the statement text; the HTTP API takes them separately, so the
53
+ * sweep's bookingId is bound rather than interpolated.
54
+ *
55
+ * Typed `string[]` because the API documents this field as an array of string.
56
+ * A wider type would let a caller pass a value the endpoint rejects at runtime,
57
+ * on a periodic path where that surfaces as a failed tick rather than a build error.
58
+ */
59
+ export function buildD1QueryRequest(accountId, databaseId, sql, token, params = []) {
60
+ return {
61
+ url: `${API}/accounts/${accountId}/d1/database/${databaseId}/query`,
62
+ init: {
63
+ method: "POST",
64
+ headers: { Authorization: `Bearer ${token}`, "Content-Type": "application/json" },
65
+ body: JSON.stringify({ sql, params }),
66
+ },
67
+ };
68
+ }
69
+ /** Read the body, refusing to hold more than the cap. Stops mid-stream. */
70
+ async function readCapped(chunks) {
71
+ const parts = [];
72
+ let total = 0;
73
+ for await (const chunk of chunks) {
74
+ total += chunk.byteLength;
75
+ if (total > D1_RESPONSE_CAP_BYTES) {
76
+ throw new Error(`response exceeded the ${D1_RESPONSE_CAP_BYTES}-byte cap`);
77
+ }
78
+ parts.push(chunk);
79
+ }
80
+ return Buffer.concat(parts).toString("utf-8");
81
+ }
82
+ /**
83
+ * Issue one request under the bound and return its parsed success envelope.
84
+ *
85
+ * Every failure surfaces the body it came with. A 2xx that is not a well-formed
86
+ * `success:true` envelope is an anomaly to report, never a silently empty result:
87
+ * this transport's callers read row sets, and an empty one means "no bookings".
88
+ */
89
+ async function d1ApiJson(fetchFn, url, init, context) {
90
+ // An explicit controller rather than `AbortSignal.timeout`, for two reasons.
91
+ // It is cancellable, so a request that finishes early clears its timer instead
92
+ // of leaving a 60 s handle holding this short-lived dispatcher's event loop
93
+ // open past its work; and its timer is the ordinary global one, so the bound is
94
+ // reachable from a test rather than only from a real 60 s wall-clock wait.
95
+ const controller = new AbortController();
96
+ const timer = setTimeout(() => controller.abort(new Error(`scheduling: ${context} timed out after ${D1_TIMEOUT_MS}ms`)), D1_TIMEOUT_MS);
97
+ try {
98
+ let res;
99
+ try {
100
+ res = await fetchFn(url, { ...init, signal: controller.signal });
101
+ }
102
+ catch (err) {
103
+ // The bound is load-bearing for the PID lock, so name it explicitly rather
104
+ // than relaying a bare DOMException the journal cannot be read for.
105
+ if (controller.signal.aborted) {
106
+ throw new Error(`scheduling: ${context} timed out after ${D1_TIMEOUT_MS}ms`);
107
+ }
108
+ throw new Error(`scheduling: ${context} failed: ${err.message}`);
109
+ }
110
+ let body;
111
+ try {
112
+ // Inside the bound deliberately: a response whose headers arrive promptly
113
+ // but whose body then stalls would otherwise hang here unbounded, which is
114
+ // the same wedge the timeout exists to prevent.
115
+ body = await readCapped(res.chunks);
116
+ }
117
+ catch (err) {
118
+ if (controller.signal.aborted) {
119
+ throw new Error(`scheduling: ${context} timed out after ${D1_TIMEOUT_MS}ms`);
120
+ }
121
+ // Keep the status: without it a capped error response is unattributable.
122
+ throw new Error(`scheduling: ${context} (status ${res.status}) ${err.message}`);
123
+ }
124
+ if (!res.ok) {
125
+ throw new Error(`scheduling: ${context} failed: ${res.status}\n${body}`);
126
+ }
127
+ let parsed;
128
+ try {
129
+ parsed = JSON.parse(body);
130
+ }
131
+ catch {
132
+ throw new Error(`scheduling: ${context} returned non-JSON: ${body}`);
133
+ }
134
+ if (parsed?.success !== true) {
135
+ throw new Error(`scheduling: ${context} API error: ${body}`);
136
+ }
137
+ return parsed;
138
+ }
139
+ finally {
140
+ clearTimeout(timer);
141
+ }
142
+ }
143
+ /**
144
+ * One client per account run. It owns the name -> uuid cache, so a tick pays one
145
+ * lookup for its database and then issues statements against the resolved id.
146
+ */
147
+ export function createD1Client(opts) {
148
+ const fetchFn = opts.fetchFn ?? defaultFetch;
149
+ const ids = new Map();
150
+ let statements = 0;
151
+ let elapsedMs = 0;
152
+ async function resolveId(dbName) {
153
+ const cached = ids.get(dbName);
154
+ if (cached !== undefined)
155
+ return cached;
156
+ const { url, init } = buildD1LookupRequest(opts.accountId, dbName, opts.token);
157
+ const parsed = (await d1ApiJson(fetchFn, url, init, "d1 database lookup"));
158
+ // Exact match on the name, never "the first result". `?name=` is a filter
159
+ // whose matching rule is not doc-confirmed; taking position on trust would
160
+ // let a near-name run this account's statements against the wrong database.
161
+ const hit = (parsed.result ?? []).find((d) => d.name === dbName);
162
+ if (!hit || typeof hit.uuid !== "string") {
163
+ throw new Error(`scheduling: no D1 database named "${dbName}" in account ${opts.accountId}`);
164
+ }
165
+ ids.set(dbName, hit.uuid);
166
+ return hit.uuid;
167
+ }
9
168
  return {
10
- command: "npx",
11
- args: ["wrangler", "d1", "execute", dbName, "--remote", "--json", "--command", sql],
169
+ async query(dbName, sql, params = []) {
170
+ const started = Date.now();
171
+ try {
172
+ const databaseId = await resolveId(dbName);
173
+ const { url, init } = buildD1QueryRequest(opts.accountId, databaseId, sql, opts.token, params);
174
+ // Counted before the await, not after: `statements` is what reached D1,
175
+ // and a statement that was issued and then errored still went. Counting
176
+ // successes only would report 0 both for "the lookup failed so nothing
177
+ // was sent" and for "one statement was sent and errored", which is
178
+ // exactly the distinction the op=d1 line exists to make.
179
+ statements++;
180
+ const parsed = (await d1ApiJson(fetchFn, url, init, "d1 query"));
181
+ // Same shape wrangler --json printed: an array of per-statement results,
182
+ // each carrying its own `results` row array. One statement in, so [0].
183
+ const first = parsed.result?.[0];
184
+ // The per-statement flag is authoritative *when present*. Its REST
185
+ // semantics are undocumented, so this refuses to let an unmeasured fact
186
+ // decide correctness: if a failed statement can arrive inside a
187
+ // success:true envelope, reading it as success would make a failed sweep
188
+ // look swept and leave the row behind silently. Absent is not false — an
189
+ // envelope that omits the flag is accepted, since requiring an
190
+ // undocumented field would reject healthy responses.
191
+ if (first?.success === false) {
192
+ throw new Error(`scheduling: d1 query statement failed: ${first.error ?? JSON.stringify(first)}`);
193
+ }
194
+ return (Array.isArray(first?.results) ? first.results : []);
195
+ }
196
+ finally {
197
+ elapsedMs += Date.now() - started;
198
+ }
199
+ },
200
+ get statements() {
201
+ return statements;
202
+ },
203
+ get elapsedMs() {
204
+ return elapsedMs;
205
+ },
12
206
  };
13
207
  }
14
208
  //# sourceMappingURL=d1-command.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"d1-command.js","sourceRoot":"","sources":["../../src/lib/d1-command.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,UAAU,cAAc,CAC5B,MAAc,EACd,GAAW;IAEX,OAAO;QACL,OAAO,EAAE,KAAK;QACd,IAAI,EAAE,CAAC,UAAU,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,CAAC;KACpF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"d1-command.js","sourceRoot":"","sources":["../../src/lib/d1-command.ts"],"names":[],"mappings":"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"}
@@ -1,11 +1,13 @@
1
1
  /**
2
2
  * Dispatcher script: reconcile-bookings
3
3
  *
4
- * Self-contained, like check-due-events. Pulls accepted booking rows from each
4
+ * Self-contained, like check-due-events. Pulls eligible booking rows from each
5
5
  * account's Cloudflare D1 `bookings` table (written by the booking site's Pages
6
- * Function), writes each into Neo4j as a :Meeting so it appears on the admin
7
- * calendar, links the booker as a :Person, and notifies the admin. Marks each
8
- * row swept so it reconciles exactly once. Ends with the standing audit.
6
+ * Function), claims the ones with no :Meeting in the graph, writes each into
7
+ * Neo4j as a :Meeting so it appears on the admin calendar, links the booker as
8
+ * a :Person, and notifies the admin. A row is claimed on the absence of its
9
+ * meeting, not on a flag, so a meeting that later goes missing self-heals.
10
+ * Ends with the standing audit over that same population.
9
11
  *
10
12
  * Task 1654 — for each booking-site account it also create-only provisions the
11
13
  * recurring connector-sync :Event (the admin turn that reads the calendar connector
@@ -1 +1 @@
1
- {"version":3,"file":"reconcile-bookings.d.ts","sourceRoot":"","sources":["../../src/scripts/reconcile-bookings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG"}
1
+ {"version":3,"file":"reconcile-bookings.d.ts","sourceRoot":"","sources":["../../src/scripts/reconcile-bookings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG"}