archal 0.9.8 → 0.9.9

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 (39) hide show
  1. package/README.md +163 -93
  2. package/bin/archal.cjs +3 -3
  3. package/dist/index.cjs +82301 -0
  4. package/dist/index.d.cts +1 -0
  5. package/dist/seed/dynamic-generator.cjs +45640 -0
  6. package/dist/seed/dynamic-generator.d.cts +67 -0
  7. package/dist/vitest/chunk-RKYS44AS.js +2216 -0
  8. package/dist/vitest/chunk-YJICENME.js +1230 -0
  9. package/dist/vitest/chunk-YV6BH6DO.js +45974 -0
  10. package/dist/vitest/index.cjs +51963 -0
  11. package/dist/vitest/index.d.ts +398 -0
  12. package/dist/vitest/index.js +2669 -0
  13. package/dist/vitest/runtime/hosted-session-reaper.cjs +29349 -0
  14. package/dist/vitest/runtime/hosted-session-reaper.d.ts +2 -0
  15. package/dist/vitest/runtime/hosted-session-reaper.js +58 -0
  16. package/dist/vitest/runtime/setup-files.d.ts +2 -0
  17. package/dist/vitest/runtime/setup-files.js +27 -0
  18. package/dist/vitest/src-JGHX6UKK.js +94 -0
  19. package/package.json +15 -19
  20. package/twin-assets/discord/fidelity.json +113 -0
  21. package/twin-assets/discord/tools.json +1953 -0
  22. package/twin-assets/github/fidelity.json +13 -0
  23. package/twin-assets/github/tools.json +21818 -0
  24. package/twin-assets/google-workspace/fidelity.json +19 -0
  25. package/twin-assets/google-workspace/tools.json +10191 -0
  26. package/twin-assets/jira/fidelity.json +40 -0
  27. package/twin-assets/jira/tools.json +17387 -0
  28. package/twin-assets/linear/fidelity.json +18 -0
  29. package/twin-assets/linear/tools.json +6496 -0
  30. package/twin-assets/ramp/fidelity.json +22 -0
  31. package/twin-assets/ramp/tools.json +2610 -0
  32. package/twin-assets/slack/fidelity.json +20 -0
  33. package/twin-assets/slack/tools.json +7301 -0
  34. package/twin-assets/stripe/fidelity.json +22 -0
  35. package/twin-assets/stripe/tools.json +15284 -0
  36. package/twin-assets/supabase/fidelity.json +13 -0
  37. package/twin-assets/supabase/tools.json +2973 -0
  38. package/dist/vitest.d.ts +0 -1
  39. package/dist/vitest.js +0 -23
@@ -0,0 +1,398 @@
1
+ import { defineWorkspace } from 'vitest/config';
2
+ import { Reporter } from 'vitest/reporters';
3
+ import { Vitest } from 'vitest/node';
4
+
5
+ interface ServiceConfig {
6
+ mode: 'route';
7
+ seed?: string;
8
+ }
9
+ type SeedFormat = 'json' | 'markdown';
10
+ interface ResolvedRuntime {
11
+ resolvedServices: string[];
12
+ resolvedSeeds: Record<string, string>;
13
+ manifestVersions: Record<string, string>;
14
+ capabilityVersion?: string;
15
+ runtimeVersion?: string;
16
+ }
17
+ type RuntimeEvent = {
18
+ type: 'runtime_started' | 'runtime_reset' | 'runtime_stopped' | 'routing_installed' | 'routing_uninstalled';
19
+ timestamp: string;
20
+ } | {
21
+ type: 'request_routed';
22
+ timestamp: string;
23
+ service: string;
24
+ sourceUrl: string;
25
+ targetUrl: string;
26
+ } | {
27
+ type: 'request_blocked';
28
+ timestamp: string;
29
+ service: string;
30
+ code: 'ARCHAL_UNDECLARED_SERVICE' | 'ARCHAL_DECLARED_SERVICE_ESCAPE';
31
+ url: string;
32
+ message: string;
33
+ } | {
34
+ type: 'request_warning';
35
+ timestamp: string;
36
+ service: string;
37
+ url: string;
38
+ message: string;
39
+ };
40
+ interface ArchalRuntime {
41
+ start(): Promise<void>;
42
+ reset(): Promise<void>;
43
+ stop(): Promise<void>;
44
+ installRouting(): void;
45
+ uninstallRouting(): void;
46
+ getEvents(): RuntimeEvent[];
47
+ }
48
+ /**
49
+ * Public, redacted view of the session intended for userland introspection.
50
+ *
51
+ * The full `StartedSession` contains `routedRequestHeaders()`
52
+ * functions that return the raw Archal account JWT. That token has
53
+ * full account scope (not session-scoped) and a 30-day expiry, so any
54
+ * code that can read it (a test dependency, a snapshot matcher, etc.)
55
+ * can impersonate the developer for the life of the token.
56
+ *
57
+ * Userland should never need the auth header — the route-mode runtime
58
+ * injects it directly into outbound twin requests without routing through
59
+ * userland code. The redacted snapshot exposes just the metadata tests
60
+ * legitimately need: session id, service names, base URLs, resolved seeds,
61
+ * manifest versions.
62
+ */
63
+ interface PublicSessionSnapshot {
64
+ sessionId: string;
65
+ services: {
66
+ name: string;
67
+ mode: 'route';
68
+ baseUrl: string;
69
+ }[];
70
+ resolvedRuntime: ResolvedRuntime;
71
+ }
72
+
73
+ declare function resetArchalTwins(): Promise<void>;
74
+ declare function bootstrapArchalVitestRouting(): Promise<ArchalRuntime>;
75
+ declare function getInstalledArchalVitestRuntime(): ArchalRuntime | undefined;
76
+ declare function getInstalledArchalVitestSession(): PublicSessionSnapshot | undefined;
77
+
78
+ type SeedClassification = {
79
+ type: 'named';
80
+ name: string;
81
+ } | {
82
+ type: 'file';
83
+ path: string;
84
+ format: SeedFormat;
85
+ };
86
+ /**
87
+ * Determine whether a seed value is a prebuilt named seed or a developer-
88
+ * provided file path. File paths start with `./', `../`, `/`, or end with
89
+ * `.json` / `.md`.
90
+ */
91
+ declare function classifySeed(value: string): SeedClassification;
92
+
93
+ /**
94
+ * A webhook delivery that the twin has queued but has NOT POSTed to the
95
+ * developer's endpoint. The hosted twin runs in AWS ECS and can't reach
96
+ * the developer's localhost, so tests pull queued deliveries from the twin
97
+ * and invoke the user's handler directly with the payload.
98
+ */
99
+ interface ArchalWebhookDelivery {
100
+ /** The Archal service that queued the delivery — `'stripe'`, `'github'`, etc. */
101
+ service: string;
102
+ /** The event type as emitted by the twin (e.g. `"customer.subscription.created"`). */
103
+ eventType: string;
104
+ /** The parsed webhook payload. Shape matches the real service (Stripe Event, GitHub hook payload, etc.). */
105
+ payload: unknown;
106
+ /** The serialized JSON body the twin would have POSTed. Use this for signature verification —
107
+ * re-serializing `payload` can produce different byte order and break HMAC checks. */
108
+ body: string;
109
+ /** Headers the twin would have POSTed with — includes the signature header (e.g. `Stripe-Signature`). */
110
+ headers: Record<string, string>;
111
+ /** The endpoint URL as registered by the user in the twin's state. */
112
+ url: string;
113
+ /** Epoch milliseconds when the delivery was queued. */
114
+ queuedAt: number;
115
+ }
116
+ interface WaitForWebhookOptions {
117
+ /** Match on exact event type. Combine with `where` for narrower matches. */
118
+ eventType?: string;
119
+ /** Predicate to filter matching deliveries. Applied after `eventType` if both are set. */
120
+ where?: (delivery: ArchalWebhookDelivery) => boolean;
121
+ /** Max wait time in milliseconds. Defaults to 2000. */
122
+ timeout?: number;
123
+ /**
124
+ * When `true`, clears the service's entire pending queue after finding a
125
+ * match. This prevents the same delivery from being returned by a later
126
+ * `waitForArchalWebhook()` call.
127
+ *
128
+ * **Defaults to `false`** because `consume: true` deletes ALL queued
129
+ * deliveries (the server has no delete-by-ID endpoint), which silently
130
+ * discards unmatched events. Tests that wait for multiple sequential
131
+ * events would lose deliveries if this were `true` by default:
132
+ *
133
+ * ```ts
134
+ * // With consume: true, the first wait deletes customer.created too
135
+ * const sub = await waitForArchalWebhook('stripe', 'customer.subscription.created', { consume: true });
136
+ * const cust = await waitForArchalWebhook('stripe', 'customer.created'); // would time out
137
+ * ```
138
+ *
139
+ * Use `consume: true` only when you know the test produces exactly one
140
+ * webhook event, or call `clearArchalWebhooks()` explicitly after
141
+ * consuming all expected events.
142
+ */
143
+ consume?: boolean;
144
+ }
145
+ /**
146
+ * Return the current pending webhook deliveries queued by the named twin.
147
+ *
148
+ * Each twin's `WebhookDeliveryEngine` appends a delivery whenever an event
149
+ * matches a webhook endpoint the user has registered. The hosted twin
150
+ * never POSTs these out in route-mode (it can't reach the user's
151
+ * localhost), so this helper is the way tests observe them.
152
+ */
153
+ declare function listArchalWebhooks(serviceName: string): Promise<ArchalWebhookDelivery[]>;
154
+ /**
155
+ * Drop all pending webhook deliveries queued by the named twin.
156
+ *
157
+ * Called automatically by `resetArchalTwins()` so each test starts with
158
+ * an empty queue, and invoked internally by `waitForArchalWebhook()` when
159
+ * `consume: true`.
160
+ */
161
+ declare function clearArchalWebhooks(serviceName: string): Promise<void>;
162
+ /**
163
+ * Poll the named twin's pending-webhook queue until a matching delivery
164
+ * appears or the timeout elapses. Returns the first match.
165
+ *
166
+ * ```ts
167
+ * await stripe.subscriptions.create({ customer: c.id, items: [...] });
168
+ * const event = await waitForArchalWebhook('stripe', 'customer.subscription.created');
169
+ * await myWebhookHandler(event); // invoke your handler directly with the payload
170
+ * ```
171
+ *
172
+ * **Parallel-worker caveat**: this helper consumes pending deliveries
173
+ * from a shared twin queue. When vitest runs test files in parallel
174
+ * (the default) across workers, worker A can consume worker B's
175
+ * deliveries. Use `testIsolation: 'serial'` in `withArchal` or
176
+ * `archalVitestProject` if your tests depend on webhook events.
177
+ */
178
+ declare function waitForArchalWebhook(serviceName: string, matcher: string | WaitForWebhookOptions, options?: WaitForWebhookOptions): Promise<ArchalWebhookDelivery>;
179
+
180
+ /**
181
+ * Archal Vitest Reporter — sends test results to the Archal dashboard.
182
+ *
183
+ * `withArchal()` and `archalVitestProject()` already install this reporter
184
+ * automatically; you only need to reference it manually if you're composing
185
+ * a fully custom vitest config outside those helpers.
186
+ *
187
+ * After each test run, results are POSTed to /api/test-results so they
188
+ * appear in the Archal dashboard alongside scenario traces.
189
+ */
190
+
191
+ declare class ArchalReporter implements Reporter {
192
+ private startTime;
193
+ private ctx;
194
+ private lifecycleMarkerPath;
195
+ private workspaceReporterMarkerPath;
196
+ private projects;
197
+ onInit(ctx: Vitest): void;
198
+ private decodeConfig;
199
+ private buildProjectContext;
200
+ private resolveProjects;
201
+ /**
202
+ * vitest v2 calls onFinished with an array of File objects (from @vitest/runner).
203
+ * Each File extends Suite which has `tasks: Task[]`, and each Task has
204
+ * `.type` ('test' | 'suite'), `.name`, `.result?.state`, `.result?.duration`.
205
+ */
206
+ onFinished(files?: unknown[], _errors?: unknown[]): Promise<void>;
207
+ /**
208
+ * vitest v4 calls onTestRunEnd (not onFinished) for custom reporters.
209
+ * The argument is an array of test modules, each with nested children.
210
+ */
211
+ onTestRunEnd(testModules?: unknown[]): Promise<void>;
212
+ private collectAndReport;
213
+ /**
214
+ * vitest v2: File extends Suite { tasks: Task[] }.
215
+ * Task.type is 'test' | 'suite' | 'custom'. Suite tasks recurse.
216
+ */
217
+ private collectV2Files;
218
+ /**
219
+ * vitest v4: TestModule.children() → TestSuite[].children() → TestCase[]
220
+ * All accessors are methods (not properties) in v4.
221
+ */
222
+ private collectV4Modules;
223
+ private resolveProjectForFile;
224
+ private partitionResults;
225
+ private sendResults;
226
+ private readSessionId;
227
+ }
228
+
229
+ interface ArchalVitestProjectOptions {
230
+ name?: string;
231
+ services: Record<string, ServiceConfig>;
232
+ /**
233
+ * Controls how twin state is isolated across vitest workers.
234
+ *
235
+ * - `'shared'` (default): one hosted session is shared across all workers.
236
+ * Cheapest and fastest, but state from one test is visible to tests in
237
+ * parallel test files. Safe if tests use unique IDs (UUID-prefixed
238
+ * emails, etc.) and don't assert on global counts/ordering.
239
+ *
240
+ * - `'serial'`: automatically sets `maxWorkers: 1` and `fileParallelism:
241
+ * false`, running test files one at a time. Guarantees isolation at the
242
+ * cost of parallelism — use this if your tests assert on global state
243
+ * (e.g. `list.length === 1`) and you don't want to rewrite them.
244
+ */
245
+ testIsolation?: 'shared' | 'serial';
246
+ }
247
+ interface ArchalVitestProjectTestOptions {
248
+ include?: string[];
249
+ exclude?: string[];
250
+ env?: Record<string, string>;
251
+ globals?: boolean;
252
+ pool?: string;
253
+ hookTimeout?: number;
254
+ testTimeout?: number;
255
+ maxWorkers?: number | string;
256
+ fileParallelism?: boolean;
257
+ }
258
+ interface ArchalVitestBuiltTestConfig {
259
+ name: string;
260
+ exclude: string[];
261
+ hookTimeout: number;
262
+ testTimeout: number;
263
+ env: Record<string, string>;
264
+ setupFiles: string[];
265
+ reporters?: unknown[];
266
+ include?: string[];
267
+ globals?: boolean;
268
+ pool?: string;
269
+ maxWorkers?: number | string;
270
+ fileParallelism?: boolean;
271
+ }
272
+ interface ArchalVitestWorkspaceProjectConfig {
273
+ test?: ArchalVitestBuiltTestConfig;
274
+ [key: string]: unknown;
275
+ }
276
+ type ArchalVitestWorkspaceProject = string | ArchalVitestWorkspaceProjectConfig;
277
+ /**
278
+ * Create a vitest workspace project that routes requests to Archal hosted twins.
279
+ *
280
+ * Use this from `vitest.workspace.ts`:
281
+ * ```ts
282
+ * import { archalVitestProject } from 'archal/vitest';
283
+ * export default [
284
+ * archalVitestProject({ services: { stripe: { mode: 'route' } } }),
285
+ * ];
286
+ * ```
287
+ *
288
+ * ⚠️ DO NOT wrap the return value in `defineConfig({ test: archalVitestProject(...) })`.
289
+ * The return value is a workspace project, not a test config. If you want a
290
+ * single `vitest.config.ts`, use `withArchal()` instead.
291
+ */
292
+ declare function archalVitestProject(options: ArchalVitestProjectOptions, testOptions?: ArchalVitestProjectTestOptions): ArchalVitestWorkspaceProjectConfig;
293
+ /**
294
+ * @deprecated Use {@link withArchal} instead. `archalVitestConfig({ services })`
295
+ * is equivalent to `withArchal({}, { services })`, and `withArchal` additionally
296
+ * preserves any existing `test` block fields (coverage, alias, globalSetup,
297
+ * poolOptions, custom reporters, etc.) — which `archalVitestConfig` cannot
298
+ * accept. This export will remain for a deprecation window; new code should
299
+ * use `withArchal`.
300
+ *
301
+ * Migration:
302
+ * ```ts
303
+ * // before
304
+ * test: archalVitestConfig({ services: { stripe: { mode: 'route' } } })
305
+ * // after
306
+ * test: withArchal({}, { services: { stripe: { mode: 'route' } } })
307
+ * ```
308
+ *
309
+ * For `vitest.workspace.ts` usage, continue to use `archalVitestProject()`.
310
+ */
311
+ declare function archalVitestConfig(options: ArchalVitestProjectOptions, testOptions?: ArchalVitestProjectTestOptions): ArchalVitestBuiltTestConfig;
312
+ /**
313
+ * Root-level Vitest config for workspace users.
314
+ *
315
+ * Vitest only honors reporters from the root config, not from individual
316
+ * workspace projects. Pair this with `vitest.workspace.ts` when using
317
+ * `archalVitestProject()` / `archalVitestWorkspace()` so dashboard upload
318
+ * remains enabled.
319
+ */
320
+ declare function archalVitestRootConfig(): {
321
+ test: {
322
+ reporters: string[];
323
+ };
324
+ };
325
+ /**
326
+ * Compose Archal's hosted-twin routing into a user's existing vitest `test`
327
+ * config, preserving every field they set (including ones Archal's options
328
+ * surface doesn't cover — `coverage`, `alias`, `globalSetup`, `poolOptions`,
329
+ * custom `reporters`, etc.).
330
+ *
331
+ * Use this when the project already has a `vitest.config.ts` and the user
332
+ * wants every test in it to route to twins:
333
+ *
334
+ * ```ts
335
+ * import { defineConfig } from 'vitest/config';
336
+ * import { withArchal } from 'archal/vitest';
337
+ *
338
+ * export default defineConfig({
339
+ * test: withArchal(
340
+ * {
341
+ * globals: true,
342
+ * setupFiles: ['./test/my-setup.ts'],
343
+ * coverage: { provider: 'v8' },
344
+ * },
345
+ * { services: { stripe: { mode: 'route' } } },
346
+ * ),
347
+ * });
348
+ * ```
349
+ *
350
+ * Merge behavior:
351
+ * - `setupFiles` and `reporters`: existing entries are preserved and Archal's
352
+ * entries are appended. ArchalReporter is appended only when the user's
353
+ * reporter list doesn't already contain an ArchalReporter instance — so a
354
+ * config carried over from an older helper or hand-rolled setup doesn't
355
+ * end up with two reporters double-uploading to `/dashboard/tests`.
356
+ * Vitest's `'default'` reporter is only inserted when the user didn't
357
+ * specify any reporters of their own.
358
+ * - `env`: user keys preserved; Archal's required session-config keys added.
359
+ * - `exclude`, `hookTimeout`, `testTimeout`, `maxWorkers`, `fileParallelism`:
360
+ * user values win if set; Archal defaults otherwise.
361
+ * - All other fields: passed through untouched.
362
+ *
363
+ * For a workspace (`vitest.workspace.ts`) layout, use `archalVitestProject`.
364
+ */
365
+ declare function withArchal<T extends Record<string, unknown>>(existingTest: T, options: ArchalVitestProjectOptions): T & ArchalVitestBuiltTestConfig;
366
+ /**
367
+ * Wrap an array of workspace projects for `vitest.workspace.ts`.
368
+ *
369
+ * This is a thin identity helper over the array export that vitest already
370
+ * accepts in `vitest.workspace.ts`. It exists so the workspace pattern has a
371
+ * clearly named, discoverable entry point that pairs with
372
+ * `archalVitestProject()` — making it harder to accidentally reach for
373
+ * `defineConfig({ test: archalVitestProject(...) })`, which silently drops
374
+ * setup files (see `installMisuseDetector`).
375
+ *
376
+ * ```ts
377
+ * import { archalVitestProject, archalVitestWorkspace } from 'archal/vitest';
378
+ *
379
+ * export default archalVitestWorkspace([
380
+ * archalVitestProject(
381
+ * { name: 'github-hosted-live', services: { github: { mode: 'route' } } },
382
+ * { include: ['__tests__/github-live.test.ts'] },
383
+ * ),
384
+ * archalVitestProject(
385
+ * { name: 'stripe-hosted-live', services: { stripe: { mode: 'route' } } },
386
+ * { include: ['__tests__/stripe-live.test.ts'] },
387
+ * ),
388
+ * ]);
389
+ * ```
390
+ *
391
+ * Accepts anything `defineWorkspace` accepts — archal-project results, plain
392
+ * `defineProject({...})` configs, glob-pattern strings, or function-based
393
+ * project factories.
394
+ */
395
+ type ArchalVitestWorkspaceInput = Parameters<typeof defineWorkspace>[0];
396
+ declare function archalVitestWorkspace<T extends ArchalVitestWorkspaceInput>(projects: T): T;
397
+
398
+ export { ArchalReporter, type ArchalVitestBuiltTestConfig, type ArchalVitestProjectOptions, type ArchalVitestProjectTestOptions, type PublicSessionSnapshot as ArchalVitestPublicSessionSnapshot, type ArchalVitestWorkspaceProject, type ArchalVitestWorkspaceProjectConfig, type ArchalWebhookDelivery, type WaitForWebhookOptions, archalVitestConfig, archalVitestProject, archalVitestRootConfig, archalVitestWorkspace, bootstrapArchalVitestRouting, classifySeed, clearArchalWebhooks, getInstalledArchalVitestRuntime, getInstalledArchalVitestSession, listArchalWebhooks, resetArchalTwins, waitForArchalWebhook, withArchal };