workos 0.16.0 → 0.17.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.
@@ -1 +1 @@
1
- {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/lib/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AA2FtC,MAAM,OAAO,qBAAsB,SAAQ,YAAY;IACrD,IAAI,CAA+B,KAAQ,EAAE,OAA2B;QACtE,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACpC,CAAC;IAED,EAAE,CAA+B,KAAQ,EAAE,QAA+C;QACxF,OAAO,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACnC,CAAC;IAED,GAAG,CAA+B,KAAQ,EAAE,QAA+C;QACzF,OAAO,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACpC,CAAC;IAED,IAAI,CAA+B,KAAQ,EAAE,QAA+C;QAC1F,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACrC,CAAC;CACF;AAED,MAAM,UAAU,2BAA2B;IACzC,OAAO,IAAI,qBAAqB,EAAE,CAAC;AACrC,CAAC","sourcesContent":["import { EventEmitter } from 'events';\n\nexport interface InstallerEvents {\n status: { message: string };\n output: { text: string; isError?: boolean };\n 'file:write': { path: string; content: string };\n 'file:edit': { path: string; oldContent: string; newContent: string };\n 'prompt:request': { id: string; message: string; options?: string[] };\n 'prompt:response': { id: string; value: string };\n 'confirm:request': { id: string; message: string; warning?: string; files?: string[] };\n 'confirm:response': { id: string; confirmed: boolean };\n 'credentials:request': { requiresApiKey: boolean };\n 'credentials:response': { apiKey: string; clientId: string };\n complete: { success: boolean; summary?: string };\n error: { message: string; stack?: string };\n\n 'state:enter': { state: string };\n 'state:exit': { state: string };\n 'auth:checking': Record<string, never>;\n 'auth:required': Record<string, never>;\n 'auth:success': Record<string, never>;\n 'auth:failure': { message: string };\n 'detection:start': Record<string, never>;\n 'detection:complete': { integration: string };\n 'detection:none': Record<string, never>;\n 'git:checking': Record<string, never>;\n 'git:clean': Record<string, never>;\n 'git:dirty': { files: string[] };\n 'git:dirty:confirmed': Record<string, never>;\n 'git:dirty:cancelled': Record<string, never>;\n 'credentials:gathering': { requiresApiKey: boolean };\n 'credentials:found': Record<string, never>;\n // Credential discovery events\n 'credentials:env:detected': { files: string[] };\n 'credentials:env:prompt': { files: string[] };\n 'credentials:env:scanning': Record<string, never>;\n 'credentials:env:found': { sourcePath: string };\n 'credentials:env:notfound': Record<string, never>;\n // Device auth events\n 'device:started': { verificationUri: string; verificationUriComplete: string; userCode: string };\n 'device:polling': Record<string, never>;\n 'device:success': { email?: string };\n 'device:timeout': Record<string, never>;\n 'device:error': { message: string };\n // Staging API events\n 'staging:fetching': Record<string, never>;\n 'staging:success': Record<string, never>;\n 'staging:error': { message: string; statusCode?: number };\n 'config:start': Record<string, never>;\n 'config:complete': Record<string, never>;\n 'agent:start': Record<string, never>;\n 'agent:progress': { step: string; detail?: string };\n 'agent:success': { summary?: string };\n 'agent:failure': { message: string; stack?: string };\n 'agent:retry': { attempt: number; maxRetries: number };\n\n 'validation:retry:start': { attempt: number };\n 'validation:retry:complete': { attempt: number; passed: boolean };\n\n 'validation:start': { framework: string };\n 'validation:issues': { issues: import('./validation/types.js').ValidationIssue[] };\n 'validation:complete': { passed: boolean; issueCount: number; durationMs: number };\n\n // Branch check events\n 'branch:checking': Record<string, never>;\n 'branch:protected': { branch: string };\n 'branch:prompt': { branch: string };\n 'branch:created': { branch: string };\n 'branch:create:failed': { error: string };\n 'branch:skipped': Record<string, never>;\n\n // Post-install events\n 'postinstall:changes': { files: string[] };\n 'postinstall:nochanges': Record<string, never>;\n 'postinstall:commit:prompt': Record<string, never>;\n 'postinstall:commit:generating': Record<string, never>;\n 'postinstall:commit:committing': { message: string };\n 'postinstall:commit:success': { message: string };\n 'postinstall:commit:failed': { error: string };\n 'postinstall:pr:prompt': Record<string, never>;\n 'postinstall:pr:generating': Record<string, never>;\n 'postinstall:pr:pushing': Record<string, never>;\n 'postinstall:pr:creating': Record<string, never>;\n 'postinstall:pr:success': { url: string };\n 'postinstall:pr:failed': { error: string };\n 'postinstall:push:failed': { error: string };\n 'postinstall:manual': { instructions: string };\n}\n\nexport type InstallerEventName = keyof InstallerEvents;\n\nexport class InstallerEventEmitter extends EventEmitter {\n emit<K extends InstallerEventName>(event: K, payload: InstallerEvents[K]): boolean {\n return super.emit(event, payload);\n }\n\n on<K extends InstallerEventName>(event: K, listener: (payload: InstallerEvents[K]) => void): this {\n return super.on(event, listener);\n }\n\n off<K extends InstallerEventName>(event: K, listener: (payload: InstallerEvents[K]) => void): this {\n return super.off(event, listener);\n }\n\n once<K extends InstallerEventName>(event: K, listener: (payload: InstallerEvents[K]) => void): this {\n return super.once(event, listener);\n }\n}\n\nexport function createInstallerEventEmitter(): InstallerEventEmitter {\n return new InstallerEventEmitter();\n}\n"]}
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/lib/events.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAoGtC,MAAM,OAAO,qBAAsB,SAAQ,YAAY;IACrD,IAAI,CAA+B,KAAQ,EAAE,OAA2B;QACtE,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;IACpC,CAAC;IAED,EAAE,CAA+B,KAAQ,EAAE,QAA+C;QACxF,OAAO,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACnC,CAAC;IAED,GAAG,CAA+B,KAAQ,EAAE,QAA+C;QACzF,OAAO,KAAK,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACpC,CAAC;IAED,IAAI,CAA+B,KAAQ,EAAE,QAA+C;QAC1F,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACrC,CAAC;CACF;AAED,MAAM,UAAU,2BAA2B;IACzC,OAAO,IAAI,qBAAqB,EAAE,CAAC;AACrC,CAAC","sourcesContent":["import { EventEmitter } from 'events';\n\nexport interface InstallerEvents {\n status: { message: string };\n output: { text: string; isError?: boolean };\n 'file:write': { path: string; content: string };\n 'file:edit': { path: string; oldContent: string; newContent: string };\n 'prompt:request': { id: string; message: string; options?: string[] };\n 'prompt:response': { id: string; value: string };\n 'confirm:request': { id: string; message: string; warning?: string; files?: string[] };\n 'confirm:response': { id: string; confirmed: boolean };\n 'credentials:request': { requiresApiKey: boolean };\n 'credentials:response': { apiKey: string; clientId: string };\n complete: { success: boolean; summary?: string };\n error: { message: string; stack?: string };\n\n 'state:enter': { state: string };\n 'state:exit': { state: string };\n 'auth:checking': Record<string, never>;\n 'auth:required': Record<string, never>;\n 'auth:success': Record<string, never>;\n 'auth:failure': { message: string };\n 'detection:start': Record<string, never>;\n 'detection:complete': { integration: string };\n 'detection:none': Record<string, never>;\n 'git:checking': Record<string, never>;\n 'git:clean': Record<string, never>;\n 'git:dirty': { files: string[] };\n 'git:dirty:confirmed': Record<string, never>;\n 'git:dirty:cancelled': Record<string, never>;\n 'credentials:gathering': { requiresApiKey: boolean };\n 'credentials:found': Record<string, never>;\n // Credential discovery events\n 'credentials:env:detected': { files: string[] };\n 'credentials:env:prompt': { files: string[] };\n 'credentials:env:scanning': Record<string, never>;\n 'credentials:env:found': { sourcePath: string };\n 'credentials:env:notfound': Record<string, never>;\n // Device auth events\n 'device:started': { verificationUri: string; verificationUriComplete: string; userCode: string };\n 'device:polling': Record<string, never>;\n 'device:success': { email?: string };\n 'device:timeout': Record<string, never>;\n 'device:error': { message: string };\n // Staging API events\n 'staging:fetching': Record<string, never>;\n 'staging:success': Record<string, never>;\n 'staging:error': { message: string; statusCode?: number };\n 'config:start': Record<string, never>;\n 'config:complete': Record<string, never>;\n 'agent:start': Record<string, never>;\n 'agent:progress': { step: string; detail?: string };\n 'agent:success': { summary?: string };\n 'agent:failure': { message: string; stack?: string };\n 'agent:retry': { attempt: number; maxRetries: number };\n\n 'validation:retry:start': { attempt: number };\n 'validation:retry:complete': { attempt: number; passed: boolean };\n\n 'validation:start': { framework: string };\n 'validation:issues': { issues: import('./validation/types.js').ValidationIssue[] };\n 'validation:complete': { passed: boolean; issueCount: number; durationMs: number };\n\n // Scaffold events (empty-directory app scaffolding)\n 'scaffold:checking': Record<string, never>;\n 'scaffold:prompt': { packageManager: string };\n 'scaffold:start': { packageManager: string };\n 'scaffold:progress': { text: string };\n 'scaffold:complete': Record<string, never>;\n 'scaffold:failed': { error: string };\n 'scaffold:skipped': Record<string, never>;\n\n // Branch check events\n 'branch:checking': Record<string, never>;\n 'branch:protected': { branch: string };\n 'branch:prompt': { branch: string };\n 'branch:created': { branch: string };\n 'branch:create:failed': { error: string };\n 'branch:skipped': Record<string, never>;\n\n // Post-install events\n 'postinstall:changes': { files: string[] };\n 'postinstall:nochanges': Record<string, never>;\n 'postinstall:commit:prompt': Record<string, never>;\n 'postinstall:commit:generating': Record<string, never>;\n 'postinstall:commit:committing': { message: string };\n 'postinstall:commit:success': { message: string };\n 'postinstall:commit:failed': { error: string };\n 'postinstall:pr:prompt': Record<string, never>;\n 'postinstall:pr:generating': Record<string, never>;\n 'postinstall:pr:pushing': Record<string, never>;\n 'postinstall:pr:creating': Record<string, never>;\n 'postinstall:pr:success': { url: string };\n 'postinstall:pr:failed': { error: string };\n 'postinstall:push:failed': { error: string };\n 'postinstall:manual': { instructions: string };\n}\n\nexport type InstallerEventName = keyof InstallerEvents;\n\nexport class InstallerEventEmitter extends EventEmitter {\n emit<K extends InstallerEventName>(event: K, payload: InstallerEvents[K]): boolean {\n return super.emit(event, payload);\n }\n\n on<K extends InstallerEventName>(event: K, listener: (payload: InstallerEvents[K]) => void): this {\n return super.on(event, listener);\n }\n\n off<K extends InstallerEventName>(event: K, listener: (payload: InstallerEvents[K]) => void): this {\n return super.off(event, listener);\n }\n\n once<K extends InstallerEventName>(event: K, listener: (payload: InstallerEvents[K]) => void): this {\n return super.once(event, listener);\n }\n}\n\nexport function createInstallerEventEmitter(): InstallerEventEmitter {\n return new InstallerEventEmitter();\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import { type ActorRefFrom } from 'xstate';
2
- import type { InstallerMachineContext, InstallerMachineInput, DetectionOutput, GitCheckOutput, AgentOutput, EnvFileInfo, DiscoveryResult, BranchCheckOutput } from './installer-core.types.js';
2
+ import type { InstallerMachineContext, InstallerMachineInput, DetectionOutput, GitCheckOutput, AgentOutput, EnvFileInfo, DiscoveryResult, BranchCheckOutput, WorkspaceCheckOutput } from './installer-core.types.js';
3
3
  import type { InstallerOptions } from '../utils/types.js';
4
4
  import type { DeviceAuthResult, DeviceAuthResponse } from './device-auth.js';
5
5
  import type { StagingCredentials } from './staging-api.js';
@@ -11,6 +11,10 @@ export declare const installerMachine: import("xstate").StateMachine<InstallerMa
11
11
  type: "GIT_CONFIRMED";
12
12
  } | {
13
13
  type: "GIT_CANCELLED";
14
+ } | {
15
+ type: "SCAFFOLD_CONFIRMED";
16
+ } | {
17
+ type: "SCAFFOLD_CANCELLED";
14
18
  } | {
15
19
  type: "CREDENTIALS_SUBMITTED";
16
20
  apiKey: string;
@@ -44,6 +48,10 @@ export declare const installerMachine: import("xstate").StateMachine<InstallerMa
44
48
  cwd: string;
45
49
  }, import("xstate").EventObject>> | import("xstate").ActorRefFromLogic<import("xstate").PromiseActorLogic<boolean, {
46
50
  options: InstallerOptions;
51
+ }, import("xstate").EventObject>> | import("xstate").ActorRefFromLogic<import("xstate").PromiseActorLogic<WorkspaceCheckOutput, {
52
+ options: InstallerOptions;
53
+ }, import("xstate").EventObject>> | import("xstate").ActorRefFromLogic<import("xstate").PromiseActorLogic<void, {
54
+ context: InstallerMachineContext;
47
55
  }, import("xstate").EventObject>> | import("xstate").ActorRefFromLogic<import("xstate").PromiseActorLogic<DetectionOutput, {
48
56
  options: InstallerOptions;
49
57
  }, import("xstate").EventObject>> | import("xstate").ActorRefFromLogic<import("xstate").PromiseActorLogic<GitCheckOutput, {
@@ -98,6 +106,18 @@ export declare const installerMachine: import("xstate").StateMachine<InstallerMa
98
106
  options: InstallerOptions;
99
107
  }, import("xstate").EventObject>;
100
108
  id: string | undefined;
109
+ } | {
110
+ src: "checkWorkspace";
111
+ logic: import("xstate").PromiseActorLogic<WorkspaceCheckOutput, {
112
+ options: InstallerOptions;
113
+ }, import("xstate").EventObject>;
114
+ id: string | undefined;
115
+ } | {
116
+ src: "runScaffold";
117
+ logic: import("xstate").PromiseActorLogic<void, {
118
+ context: InstallerMachineContext;
119
+ }, import("xstate").EventObject>;
120
+ id: string | undefined;
101
121
  } | {
102
122
  src: "detectIntegration";
103
123
  logic: import("xstate").PromiseActorLogic<DetectionOutput, {
@@ -244,6 +264,30 @@ export declare const installerMachine: import("xstate").StateMachine<InstallerMa
244
264
  } | {
245
265
  type: "emitGitCancelled";
246
266
  params: unknown;
267
+ } | {
268
+ type: "emitScaffoldChecking";
269
+ params: unknown;
270
+ } | {
271
+ type: "emitScaffoldPrompt";
272
+ params: unknown;
273
+ } | {
274
+ type: "emitScaffoldStart";
275
+ params: unknown;
276
+ } | {
277
+ type: "emitScaffoldComplete";
278
+ params: unknown;
279
+ } | {
280
+ type: "emitScaffoldFailed";
281
+ params: unknown;
282
+ } | {
283
+ type: "emitScaffoldSkipped";
284
+ params: unknown;
285
+ } | {
286
+ type: "assignWorkspaceResult";
287
+ params: unknown;
288
+ } | {
289
+ type: "assignScaffolded";
290
+ params: unknown;
247
291
  } | {
248
292
  type: "emitBranchChecking";
249
293
  params: unknown;
@@ -416,7 +460,15 @@ export declare const installerMachine: import("xstate").StateMachine<InstallerMa
416
460
  } | {
417
461
  type: "hasGhCli";
418
462
  params: unknown;
463
+ } | {
464
+ type: "notScaffoldable";
465
+ params: unknown;
466
+ } | {
467
+ type: "shouldAutoScaffold";
468
+ params: unknown;
419
469
  }, never, "error" | "cancelled" | "complete" | "idle" | "authenticating" | "configuring" | "runningAgent" | {
470
+ scaffold: "done" | "checking" | "running" | "prompting";
471
+ } | {
420
472
  preparing: {
421
473
  detection: "done" | "running";
422
474
  gitCheck: "done" | "running" | "evaluating" | "awaitingConfirmation";
@@ -431,6 +483,14 @@ export declare const installerMachine: import("xstate").StateMachine<InstallerMa
431
483
  states: {
432
484
  readonly idle: {};
433
485
  readonly authenticating: {};
486
+ readonly scaffold: {
487
+ states: {
488
+ readonly checking: {};
489
+ readonly prompting: {};
490
+ readonly running: {};
491
+ readonly done: {};
492
+ };
493
+ };
434
494
  readonly preparing: {
435
495
  states: {
436
496
  readonly detection: {
@@ -53,6 +53,33 @@ export const installerMachine = setup({
53
53
  emitGitCancelled: ({ context }) => {
54
54
  context.emitter.emit('git:dirty:cancelled', {});
55
55
  },
56
+ emitScaffoldChecking: ({ context }) => {
57
+ context.emitter.emit('scaffold:checking', {});
58
+ },
59
+ emitScaffoldPrompt: ({ context }) => {
60
+ context.emitter.emit('scaffold:prompt', { packageManager: context.packageManager ?? 'npm' });
61
+ },
62
+ emitScaffoldStart: ({ context }) => {
63
+ context.emitter.emit('scaffold:start', { packageManager: context.packageManager ?? 'npm' });
64
+ },
65
+ emitScaffoldComplete: ({ context }) => {
66
+ context.emitter.emit('scaffold:complete', {});
67
+ },
68
+ emitScaffoldFailed: ({ context }) => {
69
+ const message = context.error?.message ?? 'Scaffold failed';
70
+ context.emitter.emit('scaffold:failed', { error: message });
71
+ },
72
+ emitScaffoldSkipped: ({ context }) => {
73
+ context.emitter.emit('scaffold:skipped', {});
74
+ },
75
+ assignWorkspaceResult: assign({
76
+ scaffoldable: ({ event }) => event.output?.scaffoldable ?? false,
77
+ packageManager: ({ event }) => event.output?.packageManager ?? 'npm',
78
+ autoScaffold: ({ event }) => event.output?.autoScaffold ?? false,
79
+ }),
80
+ assignScaffolded: assign({
81
+ scaffolded: () => true,
82
+ }),
56
83
  emitBranchChecking: ({ context }) => {
57
84
  context.emitter.emit('branch:checking', {});
58
85
  },
@@ -274,11 +301,24 @@ export const installerMachine = setup({
274
301
  hasIntegration: ({ context }) => context.integration !== undefined,
275
302
  shouldSkipPostInstall: ({ context }) => context.options.noCommit === true,
276
303
  hasGhCli: () => hasGhCli(),
304
+ // Read from the actor's done event (output), not context: the
305
+ // assignWorkspaceResult action has not run yet when guards are evaluated.
306
+ notScaffoldable: ({ event }) => !event.output?.scaffoldable,
307
+ shouldAutoScaffold: ({ event }) => {
308
+ const output = event.output;
309
+ return !!output?.scaffoldable && !!output?.autoScaffold;
310
+ },
277
311
  },
278
312
  actors: {
279
313
  checkAuthentication: fromPromise(async () => {
280
314
  throw new Error('checkAuthentication not implemented - provide via machine.provide()');
281
315
  }),
316
+ checkWorkspace: fromPromise(async () => {
317
+ throw new Error('checkWorkspace not implemented - provide via machine.provide()');
318
+ }),
319
+ runScaffold: fromPromise(async () => {
320
+ throw new Error('runScaffold not implemented - provide via machine.provide()');
321
+ }),
282
322
  detectIntegration: fromPromise(async () => {
283
323
  throw new Error('detectIntegration not implemented - provide via machine.provide()');
284
324
  }),
@@ -340,7 +380,8 @@ export const installerMachine = setup({
340
380
  context: ({ input }) => ({
341
381
  emitter: input.emitter,
342
382
  options: input.options,
343
- integration: input.options.integration,
383
+ // Set by the detection actor; no pre-seeding (the --integration flag is gone).
384
+ integration: undefined,
344
385
  credentials: input.options.apiKey && input.options.clientId
345
386
  ? { apiKey: input.options.apiKey, clientId: input.options.clientId }
346
387
  : undefined,
@@ -359,9 +400,9 @@ export const installerMachine = setup({
359
400
  on: {
360
401
  START: [
361
402
  {
362
- target: 'preparing',
403
+ target: 'scaffold',
363
404
  guard: 'shouldSkipAuth',
364
- actions: { type: 'emitStateEnter', params: { state: 'preparing' } },
405
+ actions: { type: 'emitStateEnter', params: { state: 'scaffold' } },
365
406
  },
366
407
  {
367
408
  target: 'authenticating',
@@ -377,11 +418,11 @@ export const installerMachine = setup({
377
418
  src: 'checkAuthentication',
378
419
  input: ({ context }) => ({ options: context.options }),
379
420
  onDone: {
380
- target: 'preparing',
421
+ target: 'scaffold',
381
422
  actions: [
382
423
  'emitAuthSuccess',
383
424
  { type: 'emitStateExit', params: { state: 'authenticating' } },
384
- { type: 'emitStateEnter', params: { state: 'preparing' } },
425
+ { type: 'emitStateEnter', params: { state: 'scaffold' } },
385
426
  ],
386
427
  },
387
428
  onError: {
@@ -390,6 +431,81 @@ export const installerMachine = setup({
390
431
  },
391
432
  },
392
433
  },
434
+ scaffold: {
435
+ initial: 'checking',
436
+ states: {
437
+ checking: {
438
+ entry: ['emitScaffoldChecking'],
439
+ invoke: {
440
+ id: 'checkWorkspace',
441
+ src: 'checkWorkspace',
442
+ input: ({ context }) => ({ options: context.options }),
443
+ onDone: [
444
+ {
445
+ // Not an empty dir: fall through to today's behavior.
446
+ target: 'done',
447
+ guard: 'notScaffoldable',
448
+ actions: ['assignWorkspaceResult'],
449
+ },
450
+ {
451
+ // Headless or --scaffold: skip the prompt.
452
+ target: 'running',
453
+ guard: 'shouldAutoScaffold',
454
+ actions: ['assignWorkspaceResult'],
455
+ },
456
+ {
457
+ // Interactive empty dir: ask first.
458
+ target: 'prompting',
459
+ actions: ['assignWorkspaceResult'],
460
+ },
461
+ ],
462
+ onError: {
463
+ // Workspace check failure is non-fatal: proceed to preparing,
464
+ // which errors on no-integration exactly as before this feature.
465
+ target: 'done',
466
+ },
467
+ },
468
+ },
469
+ prompting: {
470
+ entry: ['emitScaffoldPrompt'],
471
+ on: {
472
+ SCAFFOLD_CONFIRMED: {
473
+ target: 'running',
474
+ },
475
+ SCAFFOLD_CANCELLED: {
476
+ target: '#installer.cancelled',
477
+ actions: ['emitScaffoldSkipped', { type: 'emitStateExit', params: { state: 'scaffold' } }],
478
+ },
479
+ },
480
+ },
481
+ running: {
482
+ entry: ['emitScaffoldStart'],
483
+ invoke: {
484
+ id: 'runScaffold',
485
+ src: 'runScaffold',
486
+ input: ({ context }) => ({ context }),
487
+ onDone: {
488
+ target: 'done',
489
+ actions: ['assignScaffolded', 'emitScaffoldComplete'],
490
+ },
491
+ onError: {
492
+ target: '#installer.error',
493
+ actions: ['assignError', 'emitScaffoldFailed', { type: 'emitStateExit', params: { state: 'scaffold' } }],
494
+ },
495
+ },
496
+ },
497
+ done: {
498
+ type: 'final',
499
+ },
500
+ },
501
+ onDone: {
502
+ target: 'preparing',
503
+ actions: [
504
+ { type: 'emitStateExit', params: { state: 'scaffold' } },
505
+ { type: 'emitStateEnter', params: { state: 'preparing' } },
506
+ ],
507
+ },
508
+ },
393
509
  preparing: {
394
510
  type: 'parallel',
395
511
  states: {
@@ -544,7 +660,17 @@ export const installerMachine = setup({
544
660
  {
545
661
  target: 'error',
546
662
  actions: [
547
- assign({ error: () => new Error('Could not detect framework integration') }),
663
+ assign({
664
+ error: ({ context }) => {
665
+ const dir = context.options.installDir;
666
+ return new Error(`No supported framework was detected in ${dir}.\n` +
667
+ `Because this directory isn't empty, a new app wasn't scaffolded, and no recognized ` +
668
+ `framework (such as a package.json with Next.js) was found to install into.\n\n` +
669
+ `Next steps:\n` +
670
+ ` - New app: run \`workos install\` in an empty directory to scaffold Next.js + AuthKit.\n` +
671
+ ` - Existing project: run from the directory that contains your package.json, or pass --install-dir <path>.`);
672
+ },
673
+ }),
548
674
  { type: 'emitStateExit', params: { state: 'preparing' } },
549
675
  ],
550
676
  },