workos 0.11.1 → 0.12.0-beta.1
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/README.md +163 -6
- package/dist/bin.js +22 -2
- package/dist/bin.js.map +1 -1
- package/dist/check-coverage.ts +237 -0
- package/dist/commands/dev.d.ts +23 -0
- package/dist/commands/dev.js +139 -0
- package/dist/commands/dev.js.map +1 -0
- package/dist/commands/emulate.d.ts +6 -0
- package/dist/commands/emulate.js +64 -0
- package/dist/commands/emulate.js.map +1 -0
- package/dist/commands/seed.d.ts +2 -0
- package/dist/commands/seed.js +60 -1
- package/dist/commands/seed.js.map +1 -1
- package/dist/emulate/core/id.d.ts +33 -0
- package/dist/emulate/core/id.js +58 -0
- package/dist/emulate/core/id.js.map +1 -0
- package/dist/emulate/core/index.d.ts +8 -0
- package/dist/emulate/core/index.js +8 -0
- package/dist/emulate/core/index.js.map +1 -0
- package/dist/emulate/core/jwt.d.ts +28 -0
- package/dist/emulate/core/jwt.js +78 -0
- package/dist/emulate/core/jwt.js.map +1 -0
- package/dist/emulate/core/middleware/auth.d.ts +18 -0
- package/dist/emulate/core/middleware/auth.js +28 -0
- package/dist/emulate/core/middleware/auth.js.map +1 -0
- package/dist/emulate/core/middleware/error-handler.d.ts +22 -0
- package/dist/emulate/core/middleware/error-handler.js +72 -0
- package/dist/emulate/core/middleware/error-handler.js.map +1 -0
- package/dist/emulate/core/pagination.d.ts +21 -0
- package/dist/emulate/core/pagination.js +35 -0
- package/dist/emulate/core/pagination.js.map +1 -0
- package/dist/emulate/core/plugin.d.ts +15 -0
- package/dist/emulate/core/plugin.js +2 -0
- package/dist/emulate/core/plugin.js.map +1 -0
- package/dist/emulate/core/server.d.ts +17 -0
- package/dist/emulate/core/server.js +116 -0
- package/dist/emulate/core/server.js.map +1 -0
- package/dist/emulate/core/store.d.ts +42 -0
- package/dist/emulate/core/store.js +148 -0
- package/dist/emulate/core/store.js.map +1 -0
- package/dist/emulate/index.d.ts +25 -0
- package/dist/emulate/index.js +47 -0
- package/dist/emulate/index.js.map +1 -0
- package/dist/emulate/workos/entities.d.ts +360 -0
- package/dist/emulate/workos/entities.js +2 -0
- package/dist/emulate/workos/entities.js.map +1 -0
- package/dist/emulate/workos/event-bus.d.ts +12 -0
- package/dist/emulate/workos/event-bus.js +45 -0
- package/dist/emulate/workos/event-bus.js.map +1 -0
- package/dist/emulate/workos/helpers.d.ts +63 -0
- package/dist/emulate/workos/helpers.js +518 -0
- package/dist/emulate/workos/helpers.js.map +1 -0
- package/dist/emulate/workos/index.d.ts +91 -0
- package/dist/emulate/workos/index.js +319 -0
- package/dist/emulate/workos/index.js.map +1 -0
- package/dist/emulate/workos/routes/api-keys.d.ts +2 -0
- package/dist/emulate/workos/routes/api-keys.js +35 -0
- package/dist/emulate/workos/routes/api-keys.js.map +1 -0
- package/dist/emulate/workos/routes/audit-logs.d.ts +2 -0
- package/dist/emulate/workos/routes/audit-logs.js +107 -0
- package/dist/emulate/workos/routes/audit-logs.js.map +1 -0
- package/dist/emulate/workos/routes/auth-challenges.d.ts +2 -0
- package/dist/emulate/workos/routes/auth-challenges.js +51 -0
- package/dist/emulate/workos/routes/auth-challenges.js.map +1 -0
- package/dist/emulate/workos/routes/auth-factors.d.ts +2 -0
- package/dist/emulate/workos/routes/auth-factors.js +51 -0
- package/dist/emulate/workos/routes/auth-factors.js.map +1 -0
- package/dist/emulate/workos/routes/auth.d.ts +2 -0
- package/dist/emulate/workos/routes/auth.js +349 -0
- package/dist/emulate/workos/routes/auth.js.map +1 -0
- package/dist/emulate/workos/routes/authorization-checks.d.ts +10 -0
- package/dist/emulate/workos/routes/authorization-checks.js +135 -0
- package/dist/emulate/workos/routes/authorization-checks.js.map +1 -0
- package/dist/emulate/workos/routes/authorization-org-roles.d.ts +2 -0
- package/dist/emulate/workos/routes/authorization-org-roles.js +206 -0
- package/dist/emulate/workos/routes/authorization-org-roles.js.map +1 -0
- package/dist/emulate/workos/routes/authorization-permissions.d.ts +2 -0
- package/dist/emulate/workos/routes/authorization-permissions.js +78 -0
- package/dist/emulate/workos/routes/authorization-permissions.js.map +1 -0
- package/dist/emulate/workos/routes/authorization-resources.d.ts +2 -0
- package/dist/emulate/workos/routes/authorization-resources.js +128 -0
- package/dist/emulate/workos/routes/authorization-resources.js.map +1 -0
- package/dist/emulate/workos/routes/authorization-roles.d.ts +2 -0
- package/dist/emulate/workos/routes/authorization-roles.js +136 -0
- package/dist/emulate/workos/routes/authorization-roles.js.map +1 -0
- package/dist/emulate/workos/routes/config.d.ts +2 -0
- package/dist/emulate/workos/routes/config.js +56 -0
- package/dist/emulate/workos/routes/config.js.map +1 -0
- package/dist/emulate/workos/routes/connect.d.ts +2 -0
- package/dist/emulate/workos/routes/connect.js +69 -0
- package/dist/emulate/workos/routes/connect.js.map +1 -0
- package/dist/emulate/workos/routes/connections.d.ts +2 -0
- package/dist/emulate/workos/routes/connections.js +77 -0
- package/dist/emulate/workos/routes/connections.js.map +1 -0
- package/dist/emulate/workos/routes/data-integrations.d.ts +2 -0
- package/dist/emulate/workos/routes/data-integrations.js +55 -0
- package/dist/emulate/workos/routes/data-integrations.js.map +1 -0
- package/dist/emulate/workos/routes/directories.d.ts +2 -0
- package/dist/emulate/workos/routes/directories.js +106 -0
- package/dist/emulate/workos/routes/directories.js.map +1 -0
- package/dist/emulate/workos/routes/email-verification.d.ts +2 -0
- package/dist/emulate/workos/routes/email-verification.js +49 -0
- package/dist/emulate/workos/routes/email-verification.js.map +1 -0
- package/dist/emulate/workos/routes/events.d.ts +2 -0
- package/dist/emulate/workos/routes/events.js +21 -0
- package/dist/emulate/workos/routes/events.js.map +1 -0
- package/dist/emulate/workos/routes/feature-flags.d.ts +2 -0
- package/dist/emulate/workos/routes/feature-flags.js +131 -0
- package/dist/emulate/workos/routes/feature-flags.js.map +1 -0
- package/dist/emulate/workos/routes/invitations.d.ts +2 -0
- package/dist/emulate/workos/routes/invitations.js +125 -0
- package/dist/emulate/workos/routes/invitations.js.map +1 -0
- package/dist/emulate/workos/routes/legacy-mfa.d.ts +2 -0
- package/dist/emulate/workos/routes/legacy-mfa.js +75 -0
- package/dist/emulate/workos/routes/legacy-mfa.js.map +1 -0
- package/dist/emulate/workos/routes/magic-auth.d.ts +2 -0
- package/dist/emulate/workos/routes/magic-auth.js +32 -0
- package/dist/emulate/workos/routes/magic-auth.js.map +1 -0
- package/dist/emulate/workos/routes/memberships.d.ts +2 -0
- package/dist/emulate/workos/routes/memberships.js +118 -0
- package/dist/emulate/workos/routes/memberships.js.map +1 -0
- package/dist/emulate/workos/routes/organization-domains.d.ts +2 -0
- package/dist/emulate/workos/routes/organization-domains.js +58 -0
- package/dist/emulate/workos/routes/organization-domains.js.map +1 -0
- package/dist/emulate/workos/routes/organizations.d.ts +2 -0
- package/dist/emulate/workos/routes/organizations.js +133 -0
- package/dist/emulate/workos/routes/organizations.js.map +1 -0
- package/dist/emulate/workos/routes/password-reset.d.ts +2 -0
- package/dist/emulate/workos/routes/password-reset.js +61 -0
- package/dist/emulate/workos/routes/password-reset.js.map +1 -0
- package/dist/emulate/workos/routes/pipes.d.ts +2 -0
- package/dist/emulate/workos/routes/pipes.js +86 -0
- package/dist/emulate/workos/routes/pipes.js.map +1 -0
- package/dist/emulate/workos/routes/portal.d.ts +2 -0
- package/dist/emulate/workos/routes/portal.js +18 -0
- package/dist/emulate/workos/routes/portal.js.map +1 -0
- package/dist/emulate/workos/routes/radar.d.ts +2 -0
- package/dist/emulate/workos/routes/radar.js +45 -0
- package/dist/emulate/workos/routes/radar.js.map +1 -0
- package/dist/emulate/workos/routes/sessions.d.ts +2 -0
- package/dist/emulate/workos/routes/sessions.js +51 -0
- package/dist/emulate/workos/routes/sessions.js.map +1 -0
- package/dist/emulate/workos/routes/sso.d.ts +2 -0
- package/dist/emulate/workos/routes/sso.js +160 -0
- package/dist/emulate/workos/routes/sso.js.map +1 -0
- package/dist/emulate/workos/routes/user-features.d.ts +2 -0
- package/dist/emulate/workos/routes/user-features.js +50 -0
- package/dist/emulate/workos/routes/user-features.js.map +1 -0
- package/dist/emulate/workos/routes/users.d.ts +2 -0
- package/dist/emulate/workos/routes/users.js +133 -0
- package/dist/emulate/workos/routes/users.js.map +1 -0
- package/dist/emulate/workos/routes/webhook-endpoints.d.ts +2 -0
- package/dist/emulate/workos/routes/webhook-endpoints.js +70 -0
- package/dist/emulate/workos/routes/webhook-endpoints.js.map +1 -0
- package/dist/emulate/workos/routes/widgets.d.ts +2 -0
- package/dist/emulate/workos/routes/widgets.js +27 -0
- package/dist/emulate/workos/routes/widgets.js.map +1 -0
- package/dist/emulate/workos/store.d.ts +48 -0
- package/dist/emulate/workos/store.js +93 -0
- package/dist/emulate/workos/store.js.map +1 -0
- package/dist/emulate/workos/webhook-signer.d.ts +1 -0
- package/dist/emulate/workos/webhook-signer.js +8 -0
- package/dist/emulate/workos/webhook-signer.js.map +1 -0
- package/dist/gen-routes-lib.spec.ts +659 -0
- package/dist/gen-routes-lib.ts +647 -0
- package/dist/gen-routes.ts +96 -0
- package/dist/lib/dev-command.d.ts +26 -0
- package/dist/lib/dev-command.js +122 -0
- package/dist/lib/dev-command.js.map +1 -0
- package/dist/utils/help-json.js +31 -0
- package/dist/utils/help-json.js.map +1 -1
- package/package.json +20 -7
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* Coverage checker: compares the WorkOS OpenAPI spec against the emulator's
|
|
4
|
+
* registered routes to find missing or extra endpoints.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* pnpm check:coverage path/to/openapi.yaml
|
|
8
|
+
* pnpm check:coverage ~/Developer/workos/packages/api/open-api-spec.yaml
|
|
9
|
+
*
|
|
10
|
+
* Reports:
|
|
11
|
+
* - Spec endpoints missing from the emulator
|
|
12
|
+
* - Emulator endpoints not in the spec (custom/internal)
|
|
13
|
+
* - Coverage percentage
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { readFileSync, existsSync, readdirSync } from 'node:fs';
|
|
17
|
+
import { resolve, extname, join } from 'node:path';
|
|
18
|
+
import YAML from 'yaml';
|
|
19
|
+
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Parse OpenAPI spec endpoints
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
interface SpecEndpoint {
|
|
25
|
+
method: string;
|
|
26
|
+
path: string;
|
|
27
|
+
operationId?: string;
|
|
28
|
+
summary?: string;
|
|
29
|
+
tags: string[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parseOpenApiEndpoints(specPath: string): SpecEndpoint[] {
|
|
33
|
+
const raw = readFileSync(specPath, 'utf-8');
|
|
34
|
+
const ext = extname(specPath).toLowerCase();
|
|
35
|
+
const spec = ext === '.yaml' || ext === '.yml' ? YAML.parse(raw) : JSON.parse(raw);
|
|
36
|
+
|
|
37
|
+
const endpoints: SpecEndpoint[] = [];
|
|
38
|
+
const methods = ['get', 'post', 'put', 'patch', 'delete'] as const;
|
|
39
|
+
|
|
40
|
+
for (const [path, item] of Object.entries(spec.paths ?? {}) as [string, any][]) {
|
|
41
|
+
for (const method of methods) {
|
|
42
|
+
const op = item[method];
|
|
43
|
+
if (!op) continue;
|
|
44
|
+
|
|
45
|
+
// Normalize OpenAPI path params {id} → :id
|
|
46
|
+
const normalizedPath = path.replace(/\{([^}]+)\}/g, ':$1');
|
|
47
|
+
|
|
48
|
+
endpoints.push({
|
|
49
|
+
method: method.toUpperCase(),
|
|
50
|
+
path: normalizedPath,
|
|
51
|
+
operationId: op.operationId,
|
|
52
|
+
summary: op.summary,
|
|
53
|
+
tags: op.tags ?? [],
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return endpoints;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Parse emulator registered routes from source files
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
interface EmulatorEndpoint {
|
|
66
|
+
method: string;
|
|
67
|
+
path: string;
|
|
68
|
+
file: string;
|
|
69
|
+
line: number;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function parseEmulatorEndpoints(): EmulatorEndpoint[] {
|
|
73
|
+
const routesDir = resolve('src/emulate/workos/routes');
|
|
74
|
+
const serverFile = resolve('src/emulate/core/server.ts');
|
|
75
|
+
const endpoints: EmulatorEndpoint[] = [];
|
|
76
|
+
|
|
77
|
+
const routePattern = /app\.(get|post|put|patch|delete)\('([^']+)'/g;
|
|
78
|
+
|
|
79
|
+
const filesToScan: string[] = [];
|
|
80
|
+
|
|
81
|
+
// Collect route files
|
|
82
|
+
if (existsSync(routesDir)) {
|
|
83
|
+
for (const file of readdirSync(routesDir)) {
|
|
84
|
+
if (file.endsWith('.ts') && !file.endsWith('.spec.ts')) {
|
|
85
|
+
filesToScan.push(join(routesDir, file));
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Also scan server.ts for JWKS and other direct routes
|
|
91
|
+
if (existsSync(serverFile)) {
|
|
92
|
+
filesToScan.push(serverFile);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
for (const filePath of filesToScan) {
|
|
96
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
97
|
+
const lines = content.split('\n');
|
|
98
|
+
|
|
99
|
+
for (let i = 0; i < lines.length; i++) {
|
|
100
|
+
routePattern.lastIndex = 0;
|
|
101
|
+
let match;
|
|
102
|
+
while ((match = routePattern.exec(lines[i])) !== null) {
|
|
103
|
+
endpoints.push({
|
|
104
|
+
method: match[1].toUpperCase(),
|
|
105
|
+
path: match[2],
|
|
106
|
+
file: filePath.replace(resolve('.') + '/', ''),
|
|
107
|
+
line: i + 1,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return endpoints;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// ---------------------------------------------------------------------------
|
|
117
|
+
// Normalize paths for comparison
|
|
118
|
+
// ---------------------------------------------------------------------------
|
|
119
|
+
|
|
120
|
+
/** Normalize path params to a canonical form for matching.
|
|
121
|
+
* e.g., :id, :orgId, :organization_id all become :param in the same position */
|
|
122
|
+
function normalizePath(path: string): string {
|
|
123
|
+
return path
|
|
124
|
+
.replace(/:[a-zA-Z_]+/g, ':param')
|
|
125
|
+
.replace(/\/+$/, '')
|
|
126
|
+
.toLowerCase();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function routeKey(method: string, path: string): string {
|
|
130
|
+
return `${method} ${normalizePath(path)}`;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// ---------------------------------------------------------------------------
|
|
134
|
+
// Main
|
|
135
|
+
// ---------------------------------------------------------------------------
|
|
136
|
+
|
|
137
|
+
function main(): void {
|
|
138
|
+
const specPath = process.argv[2];
|
|
139
|
+
if (!specPath) {
|
|
140
|
+
console.error('Usage: check-coverage <openapi-spec-path>');
|
|
141
|
+
console.error(' e.g.: pnpm check:coverage ~/Developer/workos/packages/api/open-api-spec.yaml');
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
const resolvedSpec = resolve(specPath);
|
|
146
|
+
if (!existsSync(resolvedSpec)) {
|
|
147
|
+
console.error(`Spec file not found: ${resolvedSpec}`);
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const specEndpoints = parseOpenApiEndpoints(resolvedSpec);
|
|
152
|
+
const emulatorEndpoints = parseEmulatorEndpoints();
|
|
153
|
+
|
|
154
|
+
// Build lookup maps
|
|
155
|
+
const specMap = new Map<string, SpecEndpoint>();
|
|
156
|
+
for (const ep of specEndpoints) {
|
|
157
|
+
specMap.set(routeKey(ep.method, ep.path), ep);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const emulatorMap = new Map<string, EmulatorEndpoint>();
|
|
161
|
+
for (const ep of emulatorEndpoints) {
|
|
162
|
+
emulatorMap.set(routeKey(ep.method, ep.path), ep);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Find gaps
|
|
166
|
+
const missing: SpecEndpoint[] = [];
|
|
167
|
+
const covered: SpecEndpoint[] = [];
|
|
168
|
+
for (const [key, ep] of specMap) {
|
|
169
|
+
if (emulatorMap.has(key)) {
|
|
170
|
+
covered.push(ep);
|
|
171
|
+
} else {
|
|
172
|
+
missing.push(ep);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
const extra: EmulatorEndpoint[] = [];
|
|
177
|
+
for (const [key, ep] of emulatorMap) {
|
|
178
|
+
if (!specMap.has(key)) {
|
|
179
|
+
extra.push(ep);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Group missing by tag
|
|
184
|
+
const missingByTag = new Map<string, SpecEndpoint[]>();
|
|
185
|
+
for (const ep of missing) {
|
|
186
|
+
const tag = ep.tags[0] ?? 'untagged';
|
|
187
|
+
if (!missingByTag.has(tag)) missingByTag.set(tag, []);
|
|
188
|
+
missingByTag.get(tag)!.push(ep);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Report
|
|
192
|
+
const total = specEndpoints.length;
|
|
193
|
+
const coveredCount = covered.length;
|
|
194
|
+
const pct = total > 0 ? ((coveredCount / total) * 100).toFixed(1) : '0';
|
|
195
|
+
|
|
196
|
+
console.log('');
|
|
197
|
+
console.log('=== Emulator API Coverage Report ===');
|
|
198
|
+
console.log('');
|
|
199
|
+
console.log(` Spec endpoints: ${total}`);
|
|
200
|
+
console.log(` Emulator endpoints: ${emulatorEndpoints.length}`);
|
|
201
|
+
console.log(` Covered: ${coveredCount}/${total} (${pct}%)`);
|
|
202
|
+
console.log(` Missing: ${missing.length}`);
|
|
203
|
+
console.log(` Extra (emulator-only): ${extra.length}`);
|
|
204
|
+
console.log('');
|
|
205
|
+
|
|
206
|
+
if (missing.length > 0) {
|
|
207
|
+
console.log('--- Missing from emulator ---');
|
|
208
|
+
console.log('');
|
|
209
|
+
for (const [tag, eps] of [...missingByTag.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
|
|
210
|
+
console.log(` [${tag}]`);
|
|
211
|
+
for (const ep of eps) {
|
|
212
|
+
const desc = ep.summary ? ` — ${ep.summary}` : '';
|
|
213
|
+
console.log(` ${ep.method.padEnd(6)} ${ep.path}${desc}`);
|
|
214
|
+
}
|
|
215
|
+
console.log('');
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (extra.length > 0) {
|
|
220
|
+
console.log('--- Emulator-only (not in spec) ---');
|
|
221
|
+
console.log('');
|
|
222
|
+
for (const ep of extra.sort((a, b) => a.path.localeCompare(b.path))) {
|
|
223
|
+
console.log(` ${ep.method.padEnd(6)} ${ep.path} (${ep.file}:${ep.line})`);
|
|
224
|
+
}
|
|
225
|
+
console.log('');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
if (missing.length === 0) {
|
|
229
|
+
console.log('Full coverage — all spec endpoints are implemented.');
|
|
230
|
+
console.log('');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Exit 1 if there are missing endpoints (useful for CI later)
|
|
234
|
+
process.exit(missing.length > 0 ? 1 : 0);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
main();
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type EmulatorSeedConfig } from '../emulate/index.js';
|
|
2
|
+
export interface DevArgs {
|
|
3
|
+
port: number;
|
|
4
|
+
seed?: string;
|
|
5
|
+
'--'?: string[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Build the env vars object to inject into the child process.
|
|
9
|
+
*
|
|
10
|
+
* Sets both the base URL style (`WORKOS_API_BASE_URL`) and the decomposed
|
|
11
|
+
* style (`WORKOS_API_HOSTNAME` + `WORKOS_API_PORT` + `WORKOS_API_HTTPS`)
|
|
12
|
+
* so the emulator works with authkit SDKs (which read the decomposed vars)
|
|
13
|
+
* and direct SDK consumers (which may use the base URL).
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Default seed data for `workos dev` so the AuthKit login flow works
|
|
17
|
+
* out of the box. Provides a test user, an organization with a verified
|
|
18
|
+
* domain, and a membership linking the two. Skipped when the user
|
|
19
|
+
* provides `--seed` or a `workos-emulate.config.*` file is auto-detected.
|
|
20
|
+
*/
|
|
21
|
+
export declare const DEFAULT_DEV_SEED: EmulatorSeedConfig;
|
|
22
|
+
export declare function buildDevEnv(emulatorUrl: string, apiKey?: string): Record<string, string>;
|
|
23
|
+
export declare function runDev(argv: DevArgs): Promise<void>;
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { createEmulator } from '../emulate/index.js';
|
|
2
|
+
import { resolveDevCommand } from '../lib/dev-command.js';
|
|
3
|
+
import { spawn } from 'node:child_process';
|
|
4
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
5
|
+
import { resolve } from 'node:path';
|
|
6
|
+
import { parse as parseYaml } from 'yaml';
|
|
7
|
+
import chalk from 'chalk';
|
|
8
|
+
function loadSeedFile(filePath) {
|
|
9
|
+
const resolved = resolve(filePath);
|
|
10
|
+
if (!existsSync(resolved)) {
|
|
11
|
+
console.error(`Seed file not found: ${resolved}`);
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
const content = readFileSync(resolved, 'utf-8');
|
|
15
|
+
if (resolved.endsWith('.json')) {
|
|
16
|
+
return JSON.parse(content);
|
|
17
|
+
}
|
|
18
|
+
return parseYaml(content);
|
|
19
|
+
}
|
|
20
|
+
function autoDetectSeedFile() {
|
|
21
|
+
const candidates = ['workos-emulate.config.yaml', 'workos-emulate.config.yml', 'workos-emulate.config.json'];
|
|
22
|
+
for (const name of candidates) {
|
|
23
|
+
const filePath = resolve(name);
|
|
24
|
+
if (existsSync(filePath)) {
|
|
25
|
+
return loadSeedFile(filePath);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Build the env vars object to inject into the child process.
|
|
32
|
+
*
|
|
33
|
+
* Sets both the base URL style (`WORKOS_API_BASE_URL`) and the decomposed
|
|
34
|
+
* style (`WORKOS_API_HOSTNAME` + `WORKOS_API_PORT` + `WORKOS_API_HTTPS`)
|
|
35
|
+
* so the emulator works with authkit SDKs (which read the decomposed vars)
|
|
36
|
+
* and direct SDK consumers (which may use the base URL).
|
|
37
|
+
*/
|
|
38
|
+
/**
|
|
39
|
+
* Default seed data for `workos dev` so the AuthKit login flow works
|
|
40
|
+
* out of the box. Provides a test user, an organization with a verified
|
|
41
|
+
* domain, and a membership linking the two. Skipped when the user
|
|
42
|
+
* provides `--seed` or a `workos-emulate.config.*` file is auto-detected.
|
|
43
|
+
*/
|
|
44
|
+
export const DEFAULT_DEV_SEED = {
|
|
45
|
+
users: [
|
|
46
|
+
{
|
|
47
|
+
email: 'test@example.com',
|
|
48
|
+
first_name: 'Test',
|
|
49
|
+
last_name: 'User',
|
|
50
|
+
password: 'password',
|
|
51
|
+
email_verified: true,
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
organizations: [
|
|
55
|
+
{
|
|
56
|
+
name: 'Test Organization',
|
|
57
|
+
domains: [{ domain: 'example.com', state: 'verified' }],
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
};
|
|
61
|
+
export function buildDevEnv(emulatorUrl, apiKey = 'sk_test_default') {
|
|
62
|
+
const url = new URL(emulatorUrl);
|
|
63
|
+
return {
|
|
64
|
+
WORKOS_API_BASE_URL: emulatorUrl,
|
|
65
|
+
WORKOS_API_HOSTNAME: url.hostname,
|
|
66
|
+
WORKOS_API_PORT: url.port,
|
|
67
|
+
WORKOS_API_HTTPS: 'false',
|
|
68
|
+
WORKOS_API_KEY: apiKey,
|
|
69
|
+
WORKOS_CLIENT_ID: 'client_emulated',
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
export async function runDev(argv) {
|
|
73
|
+
const userSeed = argv.seed ? loadSeedFile(argv.seed) : autoDetectSeedFile();
|
|
74
|
+
const seedConfig = userSeed ?? DEFAULT_DEV_SEED;
|
|
75
|
+
// 1. Start emulator
|
|
76
|
+
const emulator = await createEmulator({
|
|
77
|
+
port: argv.port,
|
|
78
|
+
seed: seedConfig,
|
|
79
|
+
});
|
|
80
|
+
// 2. Resolve dev command
|
|
81
|
+
const explicit = argv['--'];
|
|
82
|
+
const devCmd = explicit && explicit.length > 0
|
|
83
|
+
? { command: explicit[0], args: explicit.slice(1), framework: null }
|
|
84
|
+
: await resolveDevCommand(process.cwd());
|
|
85
|
+
// 3. Print status banner
|
|
86
|
+
console.log();
|
|
87
|
+
console.log(`${chalk.cyan('WorkOS Emulate')} ${chalk.dim(emulator.url)}`);
|
|
88
|
+
if (devCmd.framework) {
|
|
89
|
+
console.log(chalk.dim(`Detected ${devCmd.framework}`));
|
|
90
|
+
}
|
|
91
|
+
console.log(chalk.dim(`Running: ${devCmd.command} ${devCmd.args.join(' ')}`));
|
|
92
|
+
if (!userSeed) {
|
|
93
|
+
console.log();
|
|
94
|
+
console.log(` ${chalk.dim('Email:')} test@example.com`);
|
|
95
|
+
console.log(` ${chalk.dim('Password:')} password`);
|
|
96
|
+
}
|
|
97
|
+
console.log();
|
|
98
|
+
// 4. Spawn child process with env vars
|
|
99
|
+
let child;
|
|
100
|
+
try {
|
|
101
|
+
child = spawn(devCmd.command, devCmd.args, {
|
|
102
|
+
stdio: 'inherit',
|
|
103
|
+
env: {
|
|
104
|
+
...process.env,
|
|
105
|
+
...buildDevEnv(emulator.url, emulator.apiKey),
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
catch {
|
|
110
|
+
console.error(chalk.red(`Failed to start: ${devCmd.command} ${devCmd.args.join(' ')}`));
|
|
111
|
+
console.error(chalk.dim('Try specifying the command explicitly: workos dev -- <your-command>'));
|
|
112
|
+
await emulator.close();
|
|
113
|
+
process.exit(1);
|
|
114
|
+
}
|
|
115
|
+
child.on('error', async (err) => {
|
|
116
|
+
console.error(chalk.red(`Failed to start: ${devCmd.command}`));
|
|
117
|
+
if (err.code === 'ENOENT') {
|
|
118
|
+
console.error(chalk.dim(`Command not found: ${devCmd.command}`));
|
|
119
|
+
console.error(chalk.dim('Try specifying the command explicitly: workos dev -- <your-command>'));
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
console.error(chalk.dim(err.message));
|
|
123
|
+
}
|
|
124
|
+
await emulator.close();
|
|
125
|
+
process.exit(1);
|
|
126
|
+
});
|
|
127
|
+
// 5. Signal handling — forward to child, then close emulator
|
|
128
|
+
const shutdown = (signal) => {
|
|
129
|
+
child.kill(signal);
|
|
130
|
+
emulator.close().then(() => process.exit(0));
|
|
131
|
+
};
|
|
132
|
+
process.on('SIGINT', () => shutdown('SIGINT'));
|
|
133
|
+
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
|
134
|
+
// 6. If child exits, close emulator and exit with same code
|
|
135
|
+
child.on('exit', (code) => {
|
|
136
|
+
emulator.close().then(() => process.exit(code ?? 0));
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=dev.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev.js","sourceRoot":"","sources":["../../src/commands/dev.ts"],"names":[],"mappings":"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","sourcesContent":["import { createEmulator, type EmulatorSeedConfig } from '../emulate/index.js';\nimport { resolveDevCommand } from '../lib/dev-command.js';\nimport { spawn, type ChildProcess } from 'node:child_process';\nimport { readFileSync, existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport chalk from 'chalk';\n\nexport interface DevArgs {\n port: number;\n seed?: string;\n '--'?: string[];\n}\n\nfunction loadSeedFile(filePath: string): EmulatorSeedConfig {\n const resolved = resolve(filePath);\n if (!existsSync(resolved)) {\n console.error(`Seed file not found: ${resolved}`);\n process.exit(1);\n }\n\n const content = readFileSync(resolved, 'utf-8');\n if (resolved.endsWith('.json')) {\n return JSON.parse(content) as EmulatorSeedConfig;\n }\n return parseYaml(content) as EmulatorSeedConfig;\n}\n\nfunction autoDetectSeedFile(): EmulatorSeedConfig | null {\n const candidates = ['workos-emulate.config.yaml', 'workos-emulate.config.yml', 'workos-emulate.config.json'];\n\n for (const name of candidates) {\n const filePath = resolve(name);\n if (existsSync(filePath)) {\n return loadSeedFile(filePath);\n }\n }\n return null;\n}\n\n/**\n * Build the env vars object to inject into the child process.\n *\n * Sets both the base URL style (`WORKOS_API_BASE_URL`) and the decomposed\n * style (`WORKOS_API_HOSTNAME` + `WORKOS_API_PORT` + `WORKOS_API_HTTPS`)\n * so the emulator works with authkit SDKs (which read the decomposed vars)\n * and direct SDK consumers (which may use the base URL).\n */\n/**\n * Default seed data for `workos dev` so the AuthKit login flow works\n * out of the box. Provides a test user, an organization with a verified\n * domain, and a membership linking the two. Skipped when the user\n * provides `--seed` or a `workos-emulate.config.*` file is auto-detected.\n */\nexport const DEFAULT_DEV_SEED: EmulatorSeedConfig = {\n users: [\n {\n email: 'test@example.com',\n first_name: 'Test',\n last_name: 'User',\n password: 'password',\n email_verified: true,\n },\n ],\n organizations: [\n {\n name: 'Test Organization',\n domains: [{ domain: 'example.com', state: 'verified' }],\n },\n ],\n};\n\nexport function buildDevEnv(emulatorUrl: string, apiKey = 'sk_test_default'): Record<string, string> {\n const url = new URL(emulatorUrl);\n return {\n WORKOS_API_BASE_URL: emulatorUrl,\n WORKOS_API_HOSTNAME: url.hostname,\n WORKOS_API_PORT: url.port,\n WORKOS_API_HTTPS: 'false',\n WORKOS_API_KEY: apiKey,\n WORKOS_CLIENT_ID: 'client_emulated',\n };\n}\n\nexport async function runDev(argv: DevArgs): Promise<void> {\n const userSeed = argv.seed ? loadSeedFile(argv.seed) : autoDetectSeedFile();\n const seedConfig = userSeed ?? DEFAULT_DEV_SEED;\n\n // 1. Start emulator\n const emulator = await createEmulator({\n port: argv.port,\n seed: seedConfig,\n });\n\n // 2. Resolve dev command\n const explicit = argv['--'];\n const devCmd =\n explicit && explicit.length > 0\n ? { command: explicit[0], args: explicit.slice(1), framework: null as string | null }\n : await resolveDevCommand(process.cwd());\n\n // 3. Print status banner\n console.log();\n console.log(`${chalk.cyan('WorkOS Emulate')} ${chalk.dim(emulator.url)}`);\n if (devCmd.framework) {\n console.log(chalk.dim(`Detected ${devCmd.framework}`));\n }\n console.log(chalk.dim(`Running: ${devCmd.command} ${devCmd.args.join(' ')}`));\n if (!userSeed) {\n console.log();\n console.log(` ${chalk.dim('Email:')} test@example.com`);\n console.log(` ${chalk.dim('Password:')} password`);\n }\n console.log();\n\n // 4. Spawn child process with env vars\n let child: ChildProcess;\n try {\n child = spawn(devCmd.command, devCmd.args, {\n stdio: 'inherit',\n env: {\n ...process.env,\n ...buildDevEnv(emulator.url, emulator.apiKey),\n },\n });\n } catch {\n console.error(chalk.red(`Failed to start: ${devCmd.command} ${devCmd.args.join(' ')}`));\n console.error(chalk.dim('Try specifying the command explicitly: workos dev -- <your-command>'));\n await emulator.close();\n process.exit(1);\n }\n\n child.on('error', async (err) => {\n console.error(chalk.red(`Failed to start: ${devCmd.command}`));\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n console.error(chalk.dim(`Command not found: ${devCmd.command}`));\n console.error(chalk.dim('Try specifying the command explicitly: workos dev -- <your-command>'));\n } else {\n console.error(chalk.dim(err.message));\n }\n await emulator.close();\n process.exit(1);\n });\n\n // 5. Signal handling — forward to child, then close emulator\n const shutdown = (signal: NodeJS.Signals) => {\n child.kill(signal);\n emulator.close().then(() => process.exit(0));\n };\n process.on('SIGINT', () => shutdown('SIGINT'));\n process.on('SIGTERM', () => shutdown('SIGTERM'));\n\n // 6. If child exits, close emulator and exit with same code\n child.on('exit', (code) => {\n emulator.close().then(() => process.exit(code ?? 0));\n });\n}\n"]}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { createEmulator } from '../emulate/index.js';
|
|
2
|
+
import { readFileSync, existsSync } from 'node:fs';
|
|
3
|
+
import { resolve } from 'node:path';
|
|
4
|
+
import { parse as parseYaml } from 'yaml';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
function loadSeedFile(filePath) {
|
|
7
|
+
const resolved = resolve(filePath);
|
|
8
|
+
if (!existsSync(resolved)) {
|
|
9
|
+
console.error(`Seed file not found: ${resolved}`);
|
|
10
|
+
process.exit(1);
|
|
11
|
+
}
|
|
12
|
+
const content = readFileSync(resolved, 'utf-8');
|
|
13
|
+
if (resolved.endsWith('.json')) {
|
|
14
|
+
return JSON.parse(content);
|
|
15
|
+
}
|
|
16
|
+
return parseYaml(content);
|
|
17
|
+
}
|
|
18
|
+
function autoDetectSeedFile() {
|
|
19
|
+
const candidates = ['workos-emulate.config.yaml', 'workos-emulate.config.yml', 'workos-emulate.config.json'];
|
|
20
|
+
for (const name of candidates) {
|
|
21
|
+
const filePath = resolve(name);
|
|
22
|
+
if (existsSync(filePath)) {
|
|
23
|
+
return loadSeedFile(filePath);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
function printBanner(emulator) {
|
|
29
|
+
console.log();
|
|
30
|
+
console.log(chalk.bold(' WorkOS Emulator'));
|
|
31
|
+
console.log();
|
|
32
|
+
console.log(` ${chalk.dim('URL:')} ${emulator.url}`);
|
|
33
|
+
console.log(` ${chalk.dim('API Key:')} ${emulator.apiKey}`);
|
|
34
|
+
console.log(` ${chalk.dim('Health:')} ${emulator.url}/health`);
|
|
35
|
+
console.log();
|
|
36
|
+
console.log(chalk.dim(' Press Ctrl+C to stop'));
|
|
37
|
+
console.log();
|
|
38
|
+
}
|
|
39
|
+
export async function runEmulate(argv) {
|
|
40
|
+
const seedConfig = argv.seed ? loadSeedFile(argv.seed) : autoDetectSeedFile();
|
|
41
|
+
const emulator = await createEmulator({
|
|
42
|
+
port: argv.port,
|
|
43
|
+
seed: seedConfig ?? undefined,
|
|
44
|
+
});
|
|
45
|
+
if (argv.json) {
|
|
46
|
+
console.log(JSON.stringify({
|
|
47
|
+
url: emulator.url,
|
|
48
|
+
port: emulator.port,
|
|
49
|
+
apiKey: emulator.apiKey,
|
|
50
|
+
health: `${emulator.url}/health`,
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
printBanner(emulator);
|
|
55
|
+
}
|
|
56
|
+
const shutdown = () => {
|
|
57
|
+
if (!argv.json)
|
|
58
|
+
console.log(`\n${chalk.dim('Shutting down...')}`);
|
|
59
|
+
emulator.close().then(() => process.exit(0));
|
|
60
|
+
};
|
|
61
|
+
process.once('SIGINT', shutdown);
|
|
62
|
+
process.once('SIGTERM', shutdown);
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=emulate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emulate.js","sourceRoot":"","sources":["../../src/commands/emulate.ts"],"names":[],"mappings":"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","sourcesContent":["import { createEmulator, type EmulatorSeedConfig } from '../emulate/index.js';\nimport { readFileSync, existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { parse as parseYaml } from 'yaml';\nimport chalk from 'chalk';\n\nexport interface EmulateArgs {\n port: number;\n seed?: string;\n json?: boolean;\n}\n\nfunction loadSeedFile(filePath: string): EmulatorSeedConfig {\n const resolved = resolve(filePath);\n if (!existsSync(resolved)) {\n console.error(`Seed file not found: ${resolved}`);\n process.exit(1);\n }\n\n const content = readFileSync(resolved, 'utf-8');\n if (resolved.endsWith('.json')) {\n return JSON.parse(content) as EmulatorSeedConfig;\n }\n return parseYaml(content) as EmulatorSeedConfig;\n}\n\nfunction autoDetectSeedFile(): EmulatorSeedConfig | null {\n const candidates = ['workos-emulate.config.yaml', 'workos-emulate.config.yml', 'workos-emulate.config.json'];\n\n for (const name of candidates) {\n const filePath = resolve(name);\n if (existsSync(filePath)) {\n return loadSeedFile(filePath);\n }\n }\n return null;\n}\n\nfunction printBanner(emulator: { url: string; port: number; apiKey: string }): void {\n console.log();\n console.log(chalk.bold(' WorkOS Emulator'));\n console.log();\n console.log(` ${chalk.dim('URL:')} ${emulator.url}`);\n console.log(` ${chalk.dim('API Key:')} ${emulator.apiKey}`);\n console.log(` ${chalk.dim('Health:')} ${emulator.url}/health`);\n console.log();\n console.log(chalk.dim(' Press Ctrl+C to stop'));\n console.log();\n}\n\nexport async function runEmulate(argv: EmulateArgs): Promise<void> {\n const seedConfig = argv.seed ? loadSeedFile(argv.seed) : autoDetectSeedFile();\n\n const emulator = await createEmulator({\n port: argv.port,\n seed: seedConfig ?? undefined,\n });\n\n if (argv.json) {\n console.log(\n JSON.stringify({\n url: emulator.url,\n port: emulator.port,\n apiKey: emulator.apiKey,\n health: `${emulator.url}/health`,\n }),\n );\n } else {\n printBanner(emulator);\n }\n\n const shutdown = () => {\n if (!argv.json) console.log(`\\n${chalk.dim('Shutting down...')}`);\n emulator.close().then(() => process.exit(0));\n };\n process.once('SIGINT', shutdown);\n process.once('SIGTERM', shutdown);\n}\n"]}
|
package/dist/commands/seed.d.ts
CHANGED
package/dist/commands/seed.js
CHANGED
|
@@ -17,7 +17,66 @@ function loadState() {
|
|
|
17
17
|
function saveState(state) {
|
|
18
18
|
writeFileSync(STATE_FILE, JSON.stringify(state, null, 2));
|
|
19
19
|
}
|
|
20
|
+
const DEFAULT_SEED_FILE = 'workos-seed.yml';
|
|
21
|
+
const SEED_TEMPLATE = `# WorkOS seed file — provision resources with \`workos seed --file=${DEFAULT_SEED_FILE}\`
|
|
22
|
+
# Resources are created in dependency order: permissions → roles → organizations → config.
|
|
23
|
+
# Existing resources are skipped (idempotent). Run \`workos seed --clean\` to tear down.
|
|
24
|
+
|
|
25
|
+
permissions:
|
|
26
|
+
- name: Read Posts
|
|
27
|
+
slug: posts:read
|
|
28
|
+
- name: Write Posts
|
|
29
|
+
slug: posts:write
|
|
30
|
+
description: Create and edit posts
|
|
31
|
+
|
|
32
|
+
roles:
|
|
33
|
+
- name: Admin
|
|
34
|
+
slug: admin
|
|
35
|
+
description: Full access
|
|
36
|
+
permissions:
|
|
37
|
+
- posts:read
|
|
38
|
+
- posts:write
|
|
39
|
+
- name: Viewer
|
|
40
|
+
slug: viewer
|
|
41
|
+
permissions:
|
|
42
|
+
- posts:read
|
|
43
|
+
|
|
44
|
+
organizations:
|
|
45
|
+
- name: Acme Corp
|
|
46
|
+
domains:
|
|
47
|
+
- acme.com
|
|
48
|
+
|
|
49
|
+
config:
|
|
50
|
+
redirect_uris:
|
|
51
|
+
- http://localhost:3000/auth/callback
|
|
52
|
+
cors_origins:
|
|
53
|
+
- http://localhost:3000
|
|
54
|
+
homepage_url: http://localhost:3000
|
|
55
|
+
`;
|
|
56
|
+
export function runSeedInit() {
|
|
57
|
+
if (existsSync(DEFAULT_SEED_FILE)) {
|
|
58
|
+
if (isJsonMode()) {
|
|
59
|
+
outputJson({ status: 'exists', message: `${DEFAULT_SEED_FILE} already exists`, file: DEFAULT_SEED_FILE });
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
console.log(chalk.yellow(`${DEFAULT_SEED_FILE} already exists — not overwriting.`));
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
writeFileSync(DEFAULT_SEED_FILE, SEED_TEMPLATE);
|
|
67
|
+
if (isJsonMode()) {
|
|
68
|
+
outputJson({ status: 'ok', message: `Created ${DEFAULT_SEED_FILE}`, file: DEFAULT_SEED_FILE });
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
console.log(chalk.green(`Created ${DEFAULT_SEED_FILE}`));
|
|
72
|
+
console.log(chalk.dim('Edit the file, then run: workos seed --file=workos-seed.yml'));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
20
75
|
export async function runSeed(options, apiKey, baseUrl) {
|
|
76
|
+
if (options.init) {
|
|
77
|
+
runSeedInit();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
21
80
|
if (options.clean) {
|
|
22
81
|
await runSeedClean(apiKey, baseUrl);
|
|
23
82
|
return;
|
|
@@ -25,7 +84,7 @@ export async function runSeed(options, apiKey, baseUrl) {
|
|
|
25
84
|
if (!options.file) {
|
|
26
85
|
return exitWithError({
|
|
27
86
|
code: 'missing_args',
|
|
28
|
-
message: 'Provide a seed file: workos seed --file=workos-seed.yml',
|
|
87
|
+
message: 'Provide a seed file: workos seed --file=workos-seed.yml\nRun workos seed --init to create an example seed file.',
|
|
29
88
|
});
|
|
30
89
|
}
|
|
31
90
|
if (!existsSync(options.file)) {
|