@volter/twin-calcom 0.1.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 (40) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +90 -0
  3. package/client/calcom-mirror.css +29 -0
  4. package/client/calcom-mirror.tsx +125 -0
  5. package/dist/client/calcom-mirror.bundle.js +321 -0
  6. package/dist/client/calcom-mirror.css +29 -0
  7. package/dist/client/calcom-mirror.d.ts +13 -0
  8. package/dist/client/calcom-mirror.js +61 -0
  9. package/dist/client/calcom-mirror.tsx +125 -0
  10. package/dist/src/calcom-budget.d.ts +52 -0
  11. package/dist/src/calcom-budget.js +145 -0
  12. package/dist/src/calcom-capabilities.d.ts +3 -0
  13. package/dist/src/calcom-capabilities.js +521 -0
  14. package/dist/src/calcom-conformance.d.ts +15 -0
  15. package/dist/src/calcom-conformance.js +86 -0
  16. package/dist/src/calcom-connector.d.ts +90 -0
  17. package/dist/src/calcom-connector.js +229 -0
  18. package/dist/src/calcom-mirror-ui.d.ts +23 -0
  19. package/dist/src/calcom-mirror-ui.js +100 -0
  20. package/dist/src/calcom-perform-harness.d.ts +5 -0
  21. package/dist/src/calcom-perform-harness.js +29 -0
  22. package/dist/src/calcom-server.d.ts +14 -0
  23. package/dist/src/calcom-server.js +31 -0
  24. package/dist/src/calcom-twin.d.ts +16 -0
  25. package/dist/src/calcom-twin.js +718 -0
  26. package/dist/src/cli.d.ts +2 -0
  27. package/dist/src/cli.js +31 -0
  28. package/dist/src/index.d.ts +11 -0
  29. package/dist/src/index.js +69 -0
  30. package/package.json +72 -0
  31. package/src/calcom-budget.ts +171 -0
  32. package/src/calcom-capabilities.ts +529 -0
  33. package/src/calcom-conformance.ts +92 -0
  34. package/src/calcom-connector.ts +258 -0
  35. package/src/calcom-mirror-ui.ts +110 -0
  36. package/src/calcom-perform-harness.ts +24 -0
  37. package/src/calcom-server.ts +39 -0
  38. package/src/calcom-twin.ts +675 -0
  39. package/src/cli.ts +29 -0
  40. package/src/index.ts +101 -0
@@ -0,0 +1,92 @@
1
+ // Cal.com twin CONFORMANCE (dev-only) — an OFFLINE, failable check that the resources the
2
+ // twin projects carry the required v2 fields with the right primitive types. Cal.com has no
3
+ // public per-object JSON Schema we can vendor cleanly, so this encodes the v2 shape we model
4
+ // (required fields + types per resource) as a small hand-authored spec and validates the
5
+ // twin's projection against it. A missing-required field, a wrong type, or a forbidden status
6
+ // value fails the report. Imported LAZILY by the CLI so it never enters the runtime graph.
7
+ import { twinResources } from '@volter/world-core';
8
+ import type { TwinResource } from '@volter/world-core';
9
+
10
+ const SERVICE = 'calcom';
11
+
12
+ type FieldSpec = { type: 'string' | 'number' | 'boolean' | 'array' | 'object'; required?: boolean };
13
+ type ResourceSpec = Record<string, FieldSpec>;
14
+
15
+ // The v2 shape this twin models, per kernel resource type (the projected record's fields).
16
+ const SPEC: Record<string, ResourceSpec> = {
17
+ booking: {
18
+ uid: { type: 'string', required: true },
19
+ bid: { type: 'number', required: true },
20
+ title: { type: 'string', required: true },
21
+ status: { type: 'string', required: true },
22
+ start: { type: 'string', required: true },
23
+ end: { type: 'string', required: true },
24
+ duration: { type: 'number', required: true },
25
+ eventTypeId: { type: 'number', required: true },
26
+ attendees: { type: 'array', required: true },
27
+ hosts: { type: 'array', required: true },
28
+ },
29
+ event_type: {
30
+ evtId: { type: 'number', required: true },
31
+ title: { type: 'string', required: true },
32
+ slug: { type: 'string', required: true },
33
+ lengthInMinutes: { type: 'number', required: true },
34
+ ownerId: { type: 'number', required: true },
35
+ hidden: { type: 'boolean' },
36
+ },
37
+ schedule: {
38
+ schedId: { type: 'number', required: true },
39
+ name: { type: 'string', required: true },
40
+ timeZone: { type: 'string', required: true },
41
+ availability: { type: 'array', required: true },
42
+ isDefault: { type: 'boolean', required: true },
43
+ },
44
+ team: {
45
+ teamId: { type: 'number', required: true },
46
+ name: { type: 'string', required: true },
47
+ slug: { type: 'string', required: true },
48
+ },
49
+ webhook: {
50
+ whId: { type: 'string', required: true },
51
+ subscriberUrl: { type: 'string', required: true },
52
+ triggers: { type: 'array', required: true },
53
+ active: { type: 'boolean' },
54
+ },
55
+ };
56
+
57
+ const VALID_BOOKING_STATUS = new Set(['accepted', 'pending', 'cancelled', 'rejected']);
58
+
59
+ export type CalcomViolation = { type: string; id: string; field: string; reason: string };
60
+ export type CalcomConformanceReport = { ok: boolean; resourcesChecked: number; fieldsChecked: number; violations: CalcomViolation[] };
61
+
62
+ function jsType(v: unknown): string {
63
+ if (Array.isArray(v)) return 'array';
64
+ if (v === null) return 'null';
65
+ return typeof v;
66
+ }
67
+
68
+ export function checkCalcomConformance(opts: { root?: string } = {}): CalcomConformanceReport {
69
+ const resources = twinResources(SERVICE, opts.root) as unknown as (TwinResource & Record<string, unknown>)[];
70
+ const violations: CalcomViolation[] = [];
71
+ let fieldsChecked = 0;
72
+ let checked = 0;
73
+ for (const r of resources) {
74
+ if ((r as Record<string, unknown>).deleted === true) continue;
75
+ const spec = SPEC[r.type];
76
+ if (!spec) continue;
77
+ checked += 1;
78
+ for (const [field, fs] of Object.entries(spec)) {
79
+ const v = (r as Record<string, unknown>)[field];
80
+ if (v === undefined || v === null) {
81
+ if (fs.required) violations.push({ type: r.type, id: r.id, field, reason: 'missing required field' });
82
+ continue;
83
+ }
84
+ fieldsChecked += 1;
85
+ if (jsType(v) !== fs.type) violations.push({ type: r.type, id: r.id, field, reason: `expected ${fs.type}, got ${jsType(v)}` });
86
+ }
87
+ if (r.type === 'booking' && !VALID_BOOKING_STATUS.has(String((r as Record<string, unknown>).status))) {
88
+ violations.push({ type: r.type, id: r.id, field: 'status', reason: `invalid booking status ${(r as Record<string, unknown>).status}` });
89
+ }
90
+ }
91
+ return { ok: violations.length === 0, resourcesChecked: checked, fieldsChecked, violations };
92
+ }
@@ -0,0 +1,258 @@
1
+ // Cal.com CONNECTOR — the live-vendor pull/push path that gives the Cal.com twin the
2
+ // full "git for SaaS" lifecycle.
3
+ //
4
+ // PULL (real → twin): fetch real Cal.com v2 objects (bookings, event types), map the
5
+ // v2 `{ status, data }` envelope → SyncResource[], fold into the
6
+ // tree via the kernel's observe fold (its own diff — a re-pull of identical
7
+ // state appends nothing).
8
+ // PUSH (twin → real): for every PENDING local action, call the real Cal.com v2 REST API
9
+ // and confirmAction on success.
10
+ //
11
+ // The vendor I/O is an INJECTED executor (the auth boundary): the kernel and this pack hold
12
+ // NO Cal.com key and import NO network client. Offline/tests pass a fake executor; live runs
13
+ // pass `liveCalcomExecute(apiKey)`. Same code path either way.
14
+ import { assertBudgetGuardIntact, observeResources } from '@volter/world-core';
15
+ import type { PerformContext, PushOutcome, RemoteExecute, SyncResource, TwinAction } from '@volter/world-core';
16
+ import { CalcomBudget, CalcomBudgetError, calcomCallWeight, type CalcomBudgetOptions } from './calcom-budget.ts';
17
+
18
+ const SERVICE = 'calcom';
19
+
20
+ // Subject types that are twin-internal and never pushed to real Cal.com.
21
+ const INTERNAL_SUBJECT_TYPES = new Set(['slot']);
22
+
23
+ /**
24
+ * The injected real-Cal.com boundary. `request` issues ONE Cal.com v2 REST call and returns
25
+ * the parsed JSON body (the v2 `{ status, data }` envelope). A real client (a raw fetch
26
+ * wrapper) is structurally assignable; tests pass a fake.
27
+ */
28
+ export type CalcomExecute = (
29
+ method: 'GET' | 'POST' | 'PATCH' | 'DELETE',
30
+ path: string,
31
+ body?: Record<string, unknown>,
32
+ ) => Promise<{ status?: string; data?: any; error?: { message?: string; code?: string }; [k: string]: unknown }>;
33
+
34
+ /** Construction options for the live executor. `budget` cannot be null and cannot be loosened. */
35
+ export type LiveCalcomOptions = {
36
+ /** Injected `fetch`, so a test can COUNT the requests the guard did or did not let through. */
37
+ fetchImpl?: typeof fetch;
38
+ /** An existing budget to share across executors. Omit and one is constructed. Cannot be null. */
39
+ budget?: CalcomBudget;
40
+ /** Construction options for the default budget (ledger path, clock). Cannot loosen it. */
41
+ budgetOptions?: CalcomBudgetOptions;
42
+ };
43
+
44
+ /**
45
+ * A live executor against the real Cal.com v2 REST API (the user's own `cal_…` key).
46
+ *
47
+ * THIS IS THE ONE PLACE this pack issues a live `api.cal.com` request, and therefore the one place
48
+ * the rate budget has to be enforced. EVERY call is guarded: the budget is charged BEFORE the
49
+ * request goes out (`checkBudget`, which THROWS `CalcomBudgetError` instead of returning when the
50
+ * ceiling or a cooldown says stop) and the response is fed back (`recordCall`) so a `Retry-After` /
51
+ * 429 / `X-RateLimit-Remaining: 0` signal becomes a persisted cooldown that makes every later call
52
+ * fail fast WITHOUT touching Cal.com. There is deliberately no OPTION to disable the guard, and no
53
+ * value a caller can pass for `budget` that yields an unguarded client. That is NOT immunity from
54
+ * a caller who WANTS one: a fresh `budgetOptions.path` per construction, or an injected clock,
55
+ * restores the allowance, because the seam tests need cannot be denied to a determined caller in
56
+ * the same process. The kernel header states that limit and this does not upgrade it. See
57
+ * `calcom-budget.ts` for why, and for the limits of the guarantee.
58
+ */
59
+ export function liveCalcomExecute(
60
+ apiKey: string,
61
+ apiVersion = '2024-08-13',
62
+ base = 'https://api.cal.com',
63
+ opts: LiveCalcomOptions = {},
64
+ ): CalcomExecute {
65
+ // `null`/`undefined` (or omitting it) build the default budget. Anything else must be an
66
+ // UNMODIFIED CalcomBudget: a duck-typed stand-in, a SUBCLASS that overrides `checkBudget`, and a
67
+ // Proxy that traps it are all refused, because all three are one-liners that would otherwise
68
+ // hand back a client with no ceiling at all (§9 finding, 2026-07-26 — `instanceof` alone was
69
+ // not a check). What this cannot stop is deliberate sabotage from inside the process (an
70
+ // injected clock, a throwaway ledger path); the kernel's header says so rather than pretending
71
+ // otherwise, and this guards the accident and the one-liner, which are the shapes that happen.
72
+ const doFetch = opts.fetchImpl ?? fetch;
73
+ // The default ledger is keyed by a hash of THIS key — Cal.com limits per API key, so a cwd-scoped
74
+ // ledger would hand the same key a fresh allowance per checkout/worktree/CI leg.
75
+ // ONE expression decides which budget is used, so there is no second, weaker test that could
76
+ // disagree with the first. `null`/`undefined` (or omitting it) build the default; anything else
77
+ // must be an UNMODIFIED CalcomBudget — a duck-typed stand-in, a SUBCLASS overriding
78
+ // `checkBudget`, and a Proxy trapping it are ALL refused, because each is a one-liner that
79
+ // would otherwise hand back a client with no ceiling (§9 finding, 2026-07-26: `instanceof`
80
+ // alone was not a check — a subclass satisfied it). What this cannot stop is deliberate
81
+ // sabotage from inside the process (an injected clock, a throwaway ledger path); the kernel
82
+ // header states that limit rather than pretending otherwise. This closes the accident and the
83
+ // one-liner, which are the shapes that actually happen.
84
+ const budget = opts.budget !== undefined && opts.budget !== null
85
+ ? assertBudgetGuardIntact(opts.budget, CalcomBudget, 'liveCalcomExecute')
86
+ : new CalcomBudget({ token: apiKey, ...(opts.budgetOptions ?? {}) });
87
+ return async (method, path, body) => {
88
+ const headers: Record<string, string> = {
89
+ Authorization: `Bearer ${apiKey}`,
90
+ 'cal-api-version': apiVersion,
91
+ };
92
+ const init: { method: string; headers: Record<string, string>; body?: string } = { method, headers };
93
+ if (method !== 'GET' && body) {
94
+ headers['Content-Type'] = 'application/json';
95
+ init.body = JSON.stringify(body);
96
+ }
97
+ const weight = calcomCallWeight(method, path);
98
+ // THROWS instead of calling. Nothing below this line runs when the budget refuses.
99
+ const reservation = budget.checkBudget(weight);
100
+ const res = await doFetch(`${base}/v2${path}`, init);
101
+ const resHeaders: Record<string, string> = {};
102
+ res.headers.forEach((v: string, k: string) => { resHeaders[k.toLowerCase()] = v; });
103
+ const parsed = (await res.json()) as { status?: string; data?: any };
104
+ // Settles the reservation and, on a back-off signal, arms the cooldown. May itself throw (a
105
+ // `Retry-After` beyond the cap is not something to sleep off) — the cooldown is persisted
106
+ // first either way, so the refusal survives the throw.
107
+ // recordCall may THROW after arming the cooldown (a back-off beyond the cap). On a refused
108
+ // call that louder refusal wins; an answer Cal.com ACCEPTED is kept, so a write that landed is
109
+ // never recorded as failed and performed again on retry.
110
+ try {
111
+ budget.recordCall(weight, resHeaders, { status: res.status, reservation });
112
+ } catch (error) {
113
+ if (!(error instanceof CalcomBudgetError) || !res.ok) throw error;
114
+ }
115
+ return parsed;
116
+ };
117
+ }
118
+
119
+ /** Map a v2 booking object → a SyncResource the kernel can fold. */
120
+ export function mapBooking(b: Record<string, any>): SyncResource {
121
+ return {
122
+ type: 'booking',
123
+ // the uid IS Cal.com's id for a booking — prefixing it again made an observed booking a DIFFERENT
124
+ // subject from the written one, so every pull duplicated the row
125
+ id: String(b.uid),
126
+ // every field the twin's own write stores, so a refresh from this twin's wire observes what was written
127
+ fields: {
128
+ uid: b.uid, bid: b.id, title: b.title, status: b.status, start: b.start, end: b.end,
129
+ duration: b.duration, eventTypeId: b.eventTypeId, eventType: b.eventType,
130
+ attendees: b.attendees ?? [], hosts: b.hosts ?? [], guests: b.guests ?? [],
131
+ location: b.location ?? null, meetingUrl: b.meetingUrl ?? null, metadata: b.metadata ?? {},
132
+ },
133
+ };
134
+ }
135
+
136
+ /** Map a v2 event type object → a SyncResource. */
137
+ export function mapEventType(e: Record<string, any>): SyncResource {
138
+ return {
139
+ type: 'event_type',
140
+ id: `evt_${e.id}`,
141
+ // every field the twin's own write stores, so a refresh from this twin's wire observes exactly what was
142
+ // written (protocol 2 shape parity) — the vendor answers all of them on GET /v2/event-types
143
+ fields: {
144
+ evtId: e.id, title: e.title, slug: e.slug, lengthInMinutes: e.lengthInMinutes,
145
+ description: e.description ?? null, ownerId: e.ownerId, hidden: e.hidden ?? false,
146
+ scheduleId: e.scheduleId ?? null, requiresConfirmation: e.requiresConfirmation ?? false,
147
+ bookingUrl: e.bookingUrl, locations: e.locations ?? [], bookingFields: e.bookingFields ?? [],
148
+ seatsPerTimeSlot: e.seatsPerTimeSlot ?? null, seatsShowAttendees: e.seatsShowAttendees ?? false,
149
+ seatsShowAvailabilityCount: e.seatsShowAvailabilityCount ?? true,
150
+ beforeEventBuffer: e.beforeEventBuffer ?? 0, afterEventBuffer: e.afterEventBuffer ?? 0,
151
+ minimumBookingNotice: e.minimumBookingNotice ?? 0,
152
+ },
153
+ };
154
+ }
155
+
156
+ // A pinned occurredAt keeps a pull deterministic/idempotent under test.
157
+ const PULL_AT = '2026-07-01T00:00:00.000Z';
158
+
159
+ /** Fetch + map real bookings into SyncResource[] (no fold) — shared by pull + syncFromReal. */
160
+ async function collectCalcomBookings(execute: CalcomExecute): Promise<SyncResource[]> {
161
+ const res = await execute('GET', '/bookings');
162
+ const rows: any[] = Array.isArray(res.data) ? res.data : (res.data?.bookings ?? []);
163
+ return rows.map(mapBooking);
164
+ }
165
+
166
+ /** Fetch + map real event types into SyncResource[] (no fold) — shared by pull + syncFromReal. */
167
+ async function collectCalcomEventTypes(execute: CalcomExecute): Promise<SyncResource[]> {
168
+ const res = await execute('GET', '/event-types');
169
+ const rows: any[] = Array.isArray(res.data) ? res.data : (res.data?.eventTypes ?? []);
170
+ return rows.map(mapEventType);
171
+ }
172
+
173
+ /** PULL bookings from the real account into the twin's observed log (idempotent). */
174
+ export async function pullCalcomBookings(execute: CalcomExecute, root?: string, occurredAt = PULL_AT): Promise<number> {
175
+ const resources = await collectCalcomBookings(execute);
176
+ observeResources(SERVICE, resources.map((r) => ({ type: r.type, id: r.id, fields: r.fields })), { ...(root !== undefined ? { root } : {}), at: occurredAt, batch: `obs:${SERVICE}:${occurredAt}` });
177
+ return resources.length;
178
+ }
179
+
180
+ /** PULL event types from the real account into the twin's observed log (idempotent). */
181
+ export async function pullCalcomEventTypes(execute: CalcomExecute, root?: string, occurredAt = PULL_AT): Promise<number> {
182
+ const resources = await collectCalcomEventTypes(execute);
183
+ observeResources(SERVICE, resources.map((r) => ({ type: r.type, id: r.id, fields: r.fields })), { ...(root !== undefined ? { root } : {}), at: occurredAt, batch: `obs:${SERVICE}:${occurredAt}` });
184
+ return resources.length;
185
+ }
186
+
187
+ /** Full pull: bookings + event types. */
188
+ export async function pullCalcomAll(execute: CalcomExecute, root?: string, occurredAt = PULL_AT): Promise<{ bookings: number; eventTypes: number }> {
189
+ const bookings = await pullCalcomBookings(execute, root, occurredAt);
190
+ const eventTypes = await pullCalcomEventTypes(execute, root, occurredAt);
191
+ return { bookings, eventTypes };
192
+ }
193
+
194
+ /**
195
+ * D7 consumer-facing pull entry point: pull from real Cal.com (bookings + event types) and fold
196
+ * into the twin in ONE observed batch, returning the standard `{ observed, deltasAppended }`
197
+ * result. Idempotent — a re-pull of identical state appends nothing (deltasAppended drops to 0).
198
+ */
199
+ export async function syncCalcomFromReal(
200
+ execute: CalcomExecute,
201
+ opts: { root?: string; occurredAt?: string } = {},
202
+ ): Promise<{ observed: number; deltasAppended: number }> {
203
+ const occurredAt = opts.occurredAt ?? PULL_AT;
204
+ const bookings = await collectCalcomBookings(execute);
205
+ const eventTypes = await collectCalcomEventTypes(execute);
206
+ const resources = [...bookings, ...eventTypes];
207
+ // protocol 2: the observation lands on the head through the kernel's fold — one batch, one instant
208
+ const report = observeResources(SERVICE, resources.map((r) => ({ type: r.type, id: r.id, fields: r.fields })), { ...(opts.root !== undefined ? { root: opts.root } : {}), at: occurredAt, batch: `obs:${SERVICE}:${occurredAt}` });
209
+ return { observed: report.observed, deltasAppended: report.appended };
210
+ }
211
+
212
+ /** Translate one pending local action into the real Cal.com v2 REST call it represents. */
213
+ export function calcomRequestForAction(action: TwinAction): { method: 'GET' | 'POST' | 'PATCH' | 'DELETE'; path: string; body?: Record<string, unknown> } | null {
214
+ if (INTERNAL_SUBJECT_TYPES.has(action.subject.type)) return null;
215
+ const fields = (action.fields ?? {}) as Record<string, unknown>;
216
+ if (action.subject.type === 'booking') {
217
+ if (fields.status === 'cancelled') return { method: 'POST', path: `/bookings/${String(fields.uid ?? action.subject.id)}/cancel`, body: {} };
218
+ return { method: 'POST', path: '/bookings', body: fields };
219
+ }
220
+ if (action.subject.type === 'event_type') return { method: 'POST', path: '/event-types', body: fields };
221
+ if (action.subject.type === 'schedule') return { method: 'POST', path: '/schedules', body: fields };
222
+ if (action.subject.type === 'webhook') return { method: 'POST', path: '/webhooks', body: fields };
223
+ return null;
224
+ }
225
+
226
+ // ── PROTOCOL 2: the pack's half of the real state system ────────────────────────────────────
227
+ /** The pack's executor over the kernel's: the same Cal.com v2 call, carried by the head. */
228
+ export function calcomExecuteOver(execute: RemoteExecute): CalcomExecute {
229
+ return async (method, path, body) => {
230
+ const res = await execute({ method, path, headers: { accept: 'application/json', ...(body ? { 'content-type': 'application/json' } : {}) }, ...(body === undefined ? {} : { body: JSON.stringify(body) }) });
231
+ let parsed: Record<string, unknown> = {};
232
+ if (res.body) { try { parsed = JSON.parse(res.body) as Record<string, unknown>; } catch { parsed = { status: 'error', error: { message: res.body } }; } }
233
+ return parsed as ReturnType<CalcomExecute> extends Promise<infer R> ? R : never;
234
+ };
235
+ }
236
+ /** The refresh adapter: pull the account's bookings and event types through the executor. */
237
+ export async function syncCalcomFromRemote(execute: RemoteExecute, opts: { root?: string; origin?: string; occurredAt?: string } = {}): Promise<{ observed: number; deltasAppended: number }> {
238
+ return syncCalcomFromReal(calcomExecuteOver(execute), { ...(opts.root !== undefined ? { root: opts.root } : {}), ...(opts.occurredAt !== undefined ? { occurredAt: opts.occurredAt } : {}) });
239
+ }
240
+ /** The perform adapter: one entry crosses to Cal.com, or settles with the reason it never could.
241
+ * Cal.com v2 answers success with `status: 'success'`; an error envelope carries `status: 'error'`,
242
+ * and the head must hear that as a refusal rather than mark an unwritten booking across. */
243
+ export async function performCalcomAction(execute: RemoteExecute, action: TwinAction, ctx: PerformContext): Promise<PushOutcome> {
244
+ return performCalcomEntry(calcomExecuteOver(execute), action, ctx);
245
+ }
246
+ /** The same perform, one level down: over the pack's OWN client, so a suite holding a Cal.com fake can drive
247
+ * the identical path the head drives over the kernel's executor. */
248
+ export async function performCalcomEntry(execute: CalcomExecute, action: TwinAction, _ctx?: PerformContext): Promise<PushOutcome> {
249
+ const req = calcomRequestForAction(action);
250
+ if (!req) return { externalId: action.subject.id, data: { performed: false, reason: `${action.subject.type} is the twin's own record — nothing at Cal.com to write` } };
251
+ const res = await execute(req.method, req.path, req.body);
252
+ if (res.status !== 'success' || res.error !== undefined) {
253
+ throw new Error(`calcom perform ${action.operation ?? action.subject.type} refused: ${JSON.stringify(res.error ?? res).slice(0, 200)}`);
254
+ }
255
+ const data = (res.data ?? {}) as Record<string, unknown>;
256
+ const vendorId = data.uid ?? data.id;
257
+ return { externalId: vendorId === undefined ? action.subject.id : String(vendorId) };
258
+ }
@@ -0,0 +1,110 @@
1
+ // Cal.com MIRROR UI — a Cal.com-bookings-dashboard-like view served as a React/TSX app
2
+ // (bundled by Bun). It renders by consuming the twin's OWN v2 REST API on the same origin
3
+ // (/v2/bookings, /v2/event-types) — the same endpoints a real client uses — so the screen is
4
+ // data-coupled to real twin state (API↔UI parity), not a hardcoded shell.
5
+ // PURE FRONTEND (R3): the mirror imports no handler and no twin internals — it MOUNTS the pack's
6
+ // own fetch adapter as its API backend and reads every byte of state back over the wire.
7
+ import { readFile } from 'node:fs/promises';
8
+ import { bundleClient, fileResponse } from '@volter/world-core';
9
+ import { serveHttp } from '@volter/world-core';
10
+ import { createCalcomTwinFetch } from './calcom-server.ts';
11
+
12
+ const CLIENT_ENTRY = () => new URL('../client/calcom-mirror.tsx', import.meta.url).pathname; // lazy: workerd rejects top-level relative URL from import.meta.url (bundled via pack index)
13
+ const CLIENT_CSS = () => new URL('../client/calcom-mirror.css', import.meta.url).pathname; // lazy: workerd rejects top-level relative URL from import.meta.url (bundled via pack index)
14
+
15
+ // ---------------------------------------------------------------------------
16
+ // Pure, dependency-free render/format helpers (importable by the React client; Bun
17
+ // tree-shakes the server-only exports out of the browser bundle). Keep free of any
18
+ // @volter/world-core / Bun / handler usage.
19
+ // ---------------------------------------------------------------------------
20
+
21
+ export type CalcomRow = Record<string, any>;
22
+
23
+ /** Status pill tone for a booking status. */
24
+ export type PillTone = 'ok' | 'warn' | 'bad' | '';
25
+ export function bookingTone(status: unknown): PillTone {
26
+ const s = String(status ?? '').toLowerCase();
27
+ if (s === 'accepted') return 'ok';
28
+ if (s === 'pending') return 'warn';
29
+ if (s === 'cancelled' || s === 'rejected') return 'bad';
30
+ return '';
31
+ }
32
+
33
+ /** Format a booking's time window as a short "Jul 1, 16:00 → 16:30 UTC" label. */
34
+ export function formatWindow(start: unknown, end: unknown): string {
35
+ const s = typeof start === 'string' ? new Date(start) : null;
36
+ const e = typeof end === 'string' ? new Date(end) : null;
37
+ if (!s || Number.isNaN(s.getTime())) return '—';
38
+ const day = s.toISOString().slice(0, 10);
39
+ const hm = (d: Date) => d.toISOString().slice(11, 16);
40
+ return e && !Number.isNaN(e.getTime()) ? `${day} ${hm(s)} → ${hm(e)} UTC` : `${day} ${hm(s)} UTC`;
41
+ }
42
+
43
+ /** The single attendee label a bookings row shows ("Name <email>"). */
44
+ export function attendeeLabel(attendees: unknown): string {
45
+ if (!Array.isArray(attendees) || attendees.length === 0) return '—';
46
+ const a = attendees[0] as Record<string, unknown>;
47
+ return a?.email ? `${a.name ?? ''} <${a.email}>`.trim() : String(a?.name ?? '—');
48
+ }
49
+
50
+ const APP_SHELL = `<!doctype html>
51
+ <html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
52
+ <base href="/"><title>Cal.com UI mirror (twin)</title><link rel="stylesheet" href="assets/styles.css"></head>
53
+ <body><div id="root"></div><script type="module" src="assets/app.js"></script></body></html>`;
54
+
55
+ let clientBundle: Promise<string> | null = null;
56
+ /** Build the React/TSX dashboard client to browser JS (Bun bundles TSX); cached. */
57
+ export function buildCalcomMirrorClient(): Promise<string> {
58
+ if (!clientBundle) {
59
+ clientBundle = bundleClient(CLIENT_ENTRY())
60
+ .catch((error) => { clientBundle = null; throw error; });
61
+ }
62
+ return clientBundle;
63
+ }
64
+
65
+ /** Serve the Cal.com dashboard mirror UI (React app) + its backing v2 REST API. */
66
+ export async function createCalcomMirrorServer(options: { root?: string; port?: number; readOnly?: boolean }): Promise<{ port: number; stop: () => void }> {
67
+ const twin = createCalcomTwinFetch(options);
68
+ const server = await serveHttp({
69
+ // LOOPBACK-SPECIFIC bind (2026-08-20, the roving ui-verify flake): with the default
70
+ // wildcard hostname, `port: 0` can be handed a port some long-running app already LISTENS
71
+ // on at 127.0.0.1 (SO_REUSEADDR allows the overlapping non-identical bind), and the more
72
+ // specific loopback listener then shadows this server for every 127.0.0.1 fetch — the
73
+ // verify talks to a STRANGER (captured: a desktop app's asset server answering 404s on the
74
+ // mirror's port). Binding 127.0.0.1 makes the kernel allocate a port that is actually free
75
+ // on loopback, so the verify's fetches deterministically reach THIS server.
76
+ hostname: '127.0.0.1',
77
+ port: options.port ?? 0,
78
+ idleTimeout: 60,
79
+ async fetch(request) {
80
+ const url = new URL(request.url);
81
+ if (request.method === 'GET' && url.pathname === '/assets/app.js') {
82
+ try { return new Response(await buildCalcomMirrorClient(), { headers: { 'content-type': 'text/javascript; charset=utf-8' } }); }
83
+ catch (error) { return new Response(String(error), { status: 500 }); }
84
+ }
85
+ if (request.method === 'GET' && url.pathname === '/assets/styles.css') {
86
+ return fileResponse(CLIENT_CSS(), { headers: { 'content-type': 'text/css; charset=utf-8' } });
87
+ }
88
+ if (request.method === 'GET' && (url.pathname === '/' || url.pathname === '')) {
89
+ return new Response(APP_SHELL, { headers: { 'content-type': 'text/html; charset=utf-8' } });
90
+ }
91
+ // everything else → the twin's OWN FETCH ADAPTER (composition, R2/R3): the React client
92
+ // fetches the vendor's real v2 paths, and the adapter is the same closure
93
+ // `createCalcomTwinServer` serves, so there is exactly ONE serving code path — the keyless
94
+ // `GET /twin` door, the world clock, `cal-api-version` threading and an honourable
95
+ // `readOnly` all come from it, and the mirror port cannot drift from the API port.
96
+ return twin(request);
97
+ },
98
+ });
99
+ return { port: server.port ?? options.port ?? 0, stop: () => server.stop(true) };
100
+ }
101
+
102
+ /** The app-shell HTML (pure, for tests). The dashboard itself is the React client. */
103
+ export function calcomMirrorHtml(): string {
104
+ return APP_SHELL;
105
+ }
106
+
107
+ /** The mirror's stylesheet, for a host serving the shell's `assets/styles.css` itself (the hosted mirror mount). */
108
+ export function calcomMirrorStyles(): Promise<string> {
109
+ return readFile(CLIENT_CSS(), 'utf8');
110
+ }
@@ -0,0 +1,24 @@
1
+ // A MINIATURE OF THE HEAD, for this pack's own claims and suites (protocol 2). The kernel's own loop
2
+ // (`performEntries`) needs a bound root and a sealed credential a unit test does not have; this is the same
3
+ // shape without one — the same `deployableEntries`, the pack's adapter, the same `confirmAction`. Not on the
4
+ // serve path, not exported from the pack.
5
+ import { confirmAction, deployableEntries, worldNow } from '@volter/world-core';
6
+ import { performCalcomEntry, type CalcomExecute } from './calcom-connector.ts';
7
+
8
+ export async function performPending(execute: CalcomExecute, opts: { root?: string; occurredAt?: string } = {}): Promise<number> {
9
+ let pushed = 0;
10
+ for (const entry of deployableEntries('calcom', opts.root)) {
11
+ // the head stops the batch on a vendor refusal and marks nothing across; the harness reports the same
12
+ // by simply stopping here — an entry the vendor refused stays the head's to deploy
13
+ let outcome;
14
+ try { outcome = await performCalcomEntry(execute, entry); } catch { return pushed; }
15
+ if (outcome.data && (outcome.data as { performed?: boolean }).performed === false) continue;
16
+ confirmAction({
17
+ service: 'calcom', actionId: entry.id, subject: entry.subject, fields: entry.fields ?? {},
18
+ occurredAt: opts.occurredAt ?? worldNow(), vendorSubjectId: outcome.externalId, receipt: { status: 'deployed' },
19
+ ...(opts.root !== undefined ? { root: opts.root } : {}),
20
+ });
21
+ pushed += 1;
22
+ }
23
+ return pushed;
24
+ }
@@ -0,0 +1,39 @@
1
+ // Cal.com twin HTTP server — serve the full Cal.com v2 twin handler over HTTP so an
2
+ // unmodified scheduling client (or the QA backend sandbox's api.cal.com interception)
3
+ // works against it. JSON bodies pass through to the handler. Writable by default; pass
4
+ // readOnly to reject writes (R4). Auth is a faked-locally Bearer key — accepted, never
5
+ // verified against a real Cal.com (a twin fakes auth locally).
6
+ //
7
+ // FETCH-FIRST (runtime contract R12b): the serve path is the plain fetch below, built from the
8
+ // kernel's ONE adaptation (`createTwinFetchFromHandler`) with the `cal-api-version` threading as
9
+ // its per-request `extras`; the server is one line of Bun.serve around that same closure.
10
+ import { serveHttp } from '@volter/world-core';
11
+ import { handleCalcomTwinRequest } from './calcom-twin.ts';
12
+ import { createTwinFetchFromHandler, statefulTwinManifest } from '@volter/world-core';
13
+
14
+ /** Options every Cal.com-twin HTTP surface needs, independent of who owns the socket. */
15
+ export interface CalcomTwinFetchOptions {
16
+ root?: string;
17
+ readOnly?: boolean;
18
+ }
19
+
20
+ export function createCalcomTwinFetch(options: CalcomTwinFetchOptions = {}): (request: Request) => Promise<Response> {
21
+ return createTwinFetchFromHandler(handleCalcomTwinRequest, {
22
+ ...options,
23
+ manifest: statefulTwinManifest({ vendor: 'calcom', twinOf: 'the Cal.com v2 API', stores: 'event types, bookings, schedules and availability' }),
24
+ // Cal.com v2 pins endpoint behavior with the `cal-api-version` header; thread it through.
25
+ extras: (request) => {
26
+ const apiVersion = request.headers.get('cal-api-version');
27
+ return apiVersion ? { apiVersion } : {};
28
+ },
29
+ });
30
+ }
31
+
32
+ export async function createCalcomTwinServer(options: { root?: string; port?: number; readOnly?: boolean }): Promise<{ port: number; stop: () => void }> {
33
+ const server = await serveHttp({
34
+ port: options.port ?? 0,
35
+ idleTimeout: 60,
36
+ fetch: createCalcomTwinFetch(options),
37
+ });
38
+ return { port: server.port ?? options.port ?? 0, stop: () => server.stop(true) };
39
+ }