@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.
- package/package.json +1 -1
- package/payload/platform/lib/storage-broker/dist/cf-exec.d.ts.map +1 -1
- package/payload/platform/lib/storage-broker/dist/cf-exec.js +3 -2
- package/payload/platform/lib/storage-broker/dist/cf-exec.js.map +1 -1
- package/payload/platform/lib/storage-broker/src/cf-exec.ts +3 -2
- package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +3 -3
- package/payload/platform/plugins/cloudflare/skills/calendar-site/template/schema.sql +13 -3
- package/payload/platform/plugins/docs/references/calendar-booking.md +2 -2
- package/payload/platform/plugins/scheduling/PLUGIN.md +2 -2
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block-sqlite.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block-sqlite.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block-sqlite.test.js +150 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block-sqlite.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block.test.js +248 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-block.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-reconcile.test.js +158 -23
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-reconcile.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-schema-migration.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-schema-migration.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-schema-migration.test.js +200 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/booking-schema-migration.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/connector-sync-provision.test.d.ts +2 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/connector-sync-provision.test.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/connector-sync-provision.test.js +309 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/connector-sync-provision.test.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/d1-command.test.js +219 -19
- package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/d1-command.test.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.d.ts +184 -11
- package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.js +211 -22
- package/payload/platform/plugins/scheduling/mcp/dist/lib/booking-reconcile.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/connector-sync-provision.d.ts +109 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/connector-sync-provision.d.ts.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/connector-sync-provision.js +213 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/connector-sync-provision.js.map +1 -0
- package/payload/platform/plugins/scheduling/mcp/dist/lib/d1-command.d.ts +80 -8
- package/payload/platform/plugins/scheduling/mcp/dist/lib/d1-command.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/lib/d1-command.js +202 -8
- package/payload/platform/plugins/scheduling/mcp/dist/lib/d1-command.js.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.d.ts +6 -4
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.d.ts.map +1 -1
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js +411 -141
- package/payload/platform/plugins/scheduling/mcp/dist/scripts/reconcile-bookings.js.map +1 -1
- package/payload/platform/scripts/cpu-triage.sh +313 -0
- package/payload/server/{chunk-Y6V45R6M.js → chunk-JECAP3Z2.js} +296 -63
- package/payload/server/maxy-edge.js +1 -1
- package/payload/server/public/activity.html +6 -6
- package/payload/server/public/assets/{AdminLoginScreens-B44KbEuC.js → AdminLoginScreens-mEKP4pDi.js} +1 -1
- package/payload/server/public/assets/{AdminShell-BjxV2qkb.js → AdminShell-BwSaZ88d.js} +1 -1
- package/payload/server/public/assets/{Checkbox-DSeZNGNA.js → Checkbox-CN4uQ80w.js} +1 -1
- package/payload/server/public/assets/activity-CIQ8ozNZ.js +1 -0
- package/payload/server/public/assets/{admin-B4LpTCur.js → admin-BVhgfgs1.js} +1 -1
- package/payload/server/public/assets/{browser-CEFvaWwI.js → browser-D-HMmHcM.js} +1 -1
- package/payload/server/public/assets/{calendar-B3zo6V34.js → calendar-tGWvFyFn.js} +1 -1
- package/payload/server/public/assets/chat-DkttVxAz.js +1 -0
- package/payload/server/public/assets/chevron-left-CZ4ez9G5.js +1 -0
- package/payload/server/public/assets/data-P-mcmnNi.js +1 -0
- package/payload/server/public/assets/{graph-labels-GlTEIYJj.js → graph-labels-qnBleOE6.js} +1 -1
- package/payload/server/public/assets/{graph-RaOrnSf2.js → graph-zEw610xK.js} +1 -1
- package/payload/server/public/assets/{maximize-2-CHDa8p5h.js → maximize-2-BlTjXT_Y.js} +1 -1
- package/payload/server/public/assets/{operator-DtqHY4jV.js → operator-C7oIw2PG.js} +1 -1
- package/payload/server/public/assets/{page-DxnWgPtd.js → page-CTP7OFZa.js} +1 -1
- package/payload/server/public/assets/{page-CkhFj8GQ.js → page-DL6Zsdvk.js} +1 -1
- package/payload/server/public/assets/{public-D5TUfQ3I.js → public-r1A9dqR_.js} +1 -1
- package/payload/server/public/assets/{rotate-ccw-BJjVAQQY.js → rotate-ccw-CzkkKx4-.js} +1 -1
- package/payload/server/public/assets/{tasks-CM9pYYG1.js → tasks-fIJwYFWG.js} +1 -1
- package/payload/server/public/assets/{time-entry-format-C5RYEYjM.js → time-entry-format-j669DgXj.js} +1 -1
- package/payload/server/public/assets/{triangle-alert-DLfIsuo_.js → triangle-alert-BPgCgIef.js} +1 -1
- package/payload/server/public/assets/{useCopyFeedback-7yKeiAzV.js → useCopyFeedback-BmLa2aes.js} +1 -1
- package/payload/server/public/assets/{useSelectionMode-CWlh6AEq.js → useSelectionMode-DnnM7A8-.js} +1 -1
- package/payload/server/public/assets/{useSubAccountSwitcher-B2n9QPVI.css → useSubAccountSwitcher-C_E8h07P.css} +1 -1
- package/payload/server/public/assets/{useVoiceRecorder-j3ZArZ2H.js → useVoiceRecorder-u2dwZfen.js} +1 -1
- package/payload/server/public/browser.html +5 -5
- package/payload/server/public/calendar.html +6 -6
- package/payload/server/public/chat.html +12 -12
- package/payload/server/public/data.html +11 -11
- package/payload/server/public/graph.html +11 -11
- package/payload/server/public/index.html +14 -14
- package/payload/server/public/operator.html +14 -14
- package/payload/server/public/public.html +12 -12
- package/payload/server/public/tasks.html +5 -5
- package/payload/server/server.js +358 -189
- package/payload/server/public/assets/activity-Mz1BhgB1.js +0 -1
- package/payload/server/public/assets/chat-5tJdo9i_.js +0 -1
- package/payload/server/public/assets/chevron-left-BJllrPBj.js +0 -1
- package/payload/server/public/assets/data-Du79EgR-.js +0 -1
- /package/payload/server/public/assets/{useSubAccountSwitcher-CBlKgwms.js → useSubAccountSwitcher-CFa6ZAT1.js} +0 -0
|
@@ -1,12 +1,84 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
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
|
|
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
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
11
|
-
|
|
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
|
|
1
|
+
{"version":3,"file":"d1-command.js","sourceRoot":"","sources":["../../src/lib/d1-command.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,iFAAiF;AACjF,MAAM,CAAC,MAAM,aAAa,GAAG,MAAM,CAAC;AAEpC,sFAAsF;AACtF,MAAM,CAAC,MAAM,qBAAqB,GAAG,EAAE,GAAG,IAAI,GAAG,IAAI,CAAC;AAEtD,MAAM,GAAG,GAAG,sCAAsC,CAAC;AAuBnD,MAAM,YAAY,GAAY,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;IAChD,MAAM,GAAG,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE,IAAmB,CAAC,CAAC;IAClD,OAAO,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;AACrE,CAAC,CAAC;AAEF,KAAK,SAAS,CAAC,CAAC,UAAU,CAAC,GAAa;IACtC,+EAA+E;IAC/E,6EAA6E;IAC7E,qCAAqC;IACrC,IAAI,CAAC,GAAG,CAAC,IAAI;QAAE,OAAO;IACtB,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,CAAC,IAA4C;QAAE,MAAM,KAAK,CAAC;AAC1F,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,oBAAoB,CAClC,SAAiB,EACjB,MAAc,EACd,KAAa;IAEb,MAAM,EAAE,GAAG,IAAI,eAAe,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;IACjD,OAAO;QACL,GAAG,EAAE,GAAG,GAAG,aAAa,SAAS,gBAAgB,EAAE,EAAE;QACrD,IAAI,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE,EAAE;KACvE,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,mBAAmB,CACjC,SAAiB,EACjB,UAAkB,EAClB,GAAW,EACX,KAAa,EACb,SAAmB,EAAE;IAErB,OAAO;QACL,GAAG,EAAE,GAAG,GAAG,aAAa,SAAS,gBAAgB,UAAU,QAAQ;QACnE,IAAI,EAAE;YACJ,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,aAAa,EAAE,UAAU,KAAK,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YACjF,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;SACtC;KACF,CAAC;AACJ,CAAC;AAED,2EAA2E;AAC3E,KAAK,UAAU,UAAU,CAAC,MAAiC;IACzD,MAAM,KAAK,GAAiB,EAAE,CAAC;IAC/B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,KAAK,EAAE,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QACjC,KAAK,IAAI,KAAK,CAAC,UAAU,CAAC;QAC1B,IAAI,KAAK,GAAG,qBAAqB,EAAE,CAAC;YAClC,MAAM,IAAI,KAAK,CAAC,yBAAyB,qBAAqB,WAAW,CAAC,CAAC;QAC7E,CAAC;QACD,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACpB,CAAC;IACD,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AAChD,CAAC;AAED;;;;;;GAMG;AACH,KAAK,UAAU,SAAS,CACtB,OAAgB,EAChB,GAAW,EACX,IAAmC,EACnC,OAAe;IAEf,6EAA6E;IAC7E,+EAA+E;IAC/E,4EAA4E;IAC5E,gFAAgF;IAChF,2EAA2E;IAC3E,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,KAAK,GAAG,UAAU,CACtB,GAAG,EAAE,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,eAAe,OAAO,oBAAoB,aAAa,IAAI,CAAC,CAAC,EAC9F,aAAa,CACd,CAAC;IACF,IAAI,CAAC;QACH,IAAI,GAAe,CAAC;QACpB,IAAI,CAAC;YACH,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;QACnE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,2EAA2E;YAC3E,oEAAoE;YACpE,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBAC9B,MAAM,IAAI,KAAK,CAAC,eAAe,OAAO,oBAAoB,aAAa,IAAI,CAAC,CAAC;YAC/E,CAAC;YACD,MAAM,IAAI,KAAK,CAAC,eAAe,OAAO,YAAa,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC9E,CAAC;QACD,IAAI,IAAY,CAAC;QACjB,IAAI,CAAC;YACH,0EAA0E;YAC1E,2EAA2E;YAC3E,gDAAgD;YAChD,IAAI,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACtC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,UAAU,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBAC9B,MAAM,IAAI,KAAK,CAAC,eAAe,OAAO,oBAAoB,aAAa,IAAI,CAAC,CAAC;YAC/E,CAAC;YACD,yEAAyE;YACzE,MAAM,IAAI,KAAK,CAAC,eAAe,OAAO,YAAY,GAAG,CAAC,MAAM,KAAM,GAAa,CAAC,OAAO,EAAE,CAAC,CAAC;QAC7F,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,eAAe,OAAO,YAAY,GAAG,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC;QAC3E,CAAC;QACD,IAAI,MAAoC,CAAC;QACzC,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAiC,CAAC;QAC5D,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,eAAe,OAAO,uBAAuB,IAAI,EAAE,CAAC,CAAC;QACvE,CAAC;QACD,IAAI,MAAM,EAAE,OAAO,KAAK,IAAI,EAAE,CAAC;YAC7B,MAAM,IAAI,KAAK,CAAC,eAAe,OAAO,eAAe,IAAI,EAAE,CAAC,CAAC;QAC/D,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;YAAS,CAAC;QACT,YAAY,CAAC,KAAK,CAAC,CAAC;IACtB,CAAC;AACH,CAAC;AAcD;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,IAI9B;IACC,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,IAAI,YAAY,CAAC;IAC7C,MAAM,GAAG,GAAG,IAAI,GAAG,EAAkB,CAAC;IACtC,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,IAAI,SAAS,GAAG,CAAC,CAAC;IAElB,KAAK,UAAU,SAAS,CAAC,MAAc;QACrC,MAAM,MAAM,GAAG,GAAG,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC/B,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,MAAM,CAAC;QACxC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,oBAAoB,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QAC/E,MAAM,MAAM,GAAG,CAAC,MAAM,SAAS,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,oBAAoB,CAAC,CAExE,CAAC;QACF,0EAA0E;QAC1E,2EAA2E;QAC3E,4EAA4E;QAC5E,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;QACjE,IAAI,CAAC,GAAG,IAAI,OAAO,GAAG,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACzC,MAAM,IAAI,KAAK,CAAC,qCAAqC,MAAM,gBAAgB,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;QAC/F,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1B,OAAO,GAAG,CAAC,IAAI,CAAC;IAClB,CAAC;IAED,OAAO;QACL,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,EAAE;YAClC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAC3B,IAAI,CAAC;gBACH,MAAM,UAAU,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,CAAC;gBAC3C,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,GAAG,mBAAmB,CACvC,IAAI,CAAC,SAAS,EACd,UAAU,EACV,GAAG,EACH,IAAI,CAAC,KAAK,EACV,MAAM,CACP,CAAC;gBACF,wEAAwE;gBACxE,wEAAwE;gBACxE,uEAAuE;gBACvE,mEAAmE;gBACnE,yDAAyD;gBACzD,UAAU,EAAE,CAAC;gBACb,MAAM,MAAM,GAAG,CAAC,MAAM,SAAS,CAAC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,UAAU,CAAC,CAE9D,CAAC;gBACF,yEAAyE;gBACzE,uEAAuE;gBACvE,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;gBACjC,mEAAmE;gBACnE,wEAAwE;gBACxE,gEAAgE;gBAChE,yEAAyE;gBACzE,yEAAyE;gBACzE,+DAA+D;gBAC/D,qDAAqD;gBACrD,IAAI,KAAK,EAAE,OAAO,KAAK,KAAK,EAAE,CAAC;oBAC7B,MAAM,IAAI,KAAK,CACb,0CAA0C,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CACjF,CAAC;gBACJ,CAAC;gBACD,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAA8B,CAAC;YAC3F,CAAC;oBAAS,CAAC;gBACT,SAAS,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC;YACpC,CAAC;QACH,CAAC;QACD,IAAI,UAAU;YACZ,OAAO,UAAU,CAAC;QACpB,CAAC;QACD,IAAI,SAAS;YACX,OAAO,SAAS,CAAC;QACnB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Dispatcher script: reconcile-bookings
|
|
3
3
|
*
|
|
4
|
-
* Self-contained, like check-due-events. Pulls
|
|
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),
|
|
7
|
-
*
|
|
8
|
-
*
|
|
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
|
|
1
|
+
{"version":3,"file":"reconcile-bookings.d.ts","sourceRoot":"","sources":["../../src/scripts/reconcile-bookings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG"}
|