nymbal 0.0.0 → 0.2.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.
- package/bin/nymbal.mjs +2 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +815 -0
- package/dist/index.js.map +1 -0
- package/package.json +50 -7
- package/README.md +0 -4
package/bin/nymbal.mjs
ADDED
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,815 @@
|
|
|
1
|
+
// src/index.ts
|
|
2
|
+
import { defineCommand as defineCommand8, runMain } from "citty";
|
|
3
|
+
|
|
4
|
+
// src/utils/version.ts
|
|
5
|
+
import { readFileSync } from "fs";
|
|
6
|
+
import { dirname, join } from "path";
|
|
7
|
+
import { fileURLToPath } from "url";
|
|
8
|
+
var CLI_PACKAGE_NAME = "nymbal";
|
|
9
|
+
var cached = null;
|
|
10
|
+
function readOwnVersion() {
|
|
11
|
+
let dir = dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
for (; ; ) {
|
|
13
|
+
try {
|
|
14
|
+
const pkg = JSON.parse(readFileSync(join(dir, "package.json"), "utf8"));
|
|
15
|
+
if (pkg.name === CLI_PACKAGE_NAME) return pkg.version;
|
|
16
|
+
} catch {
|
|
17
|
+
}
|
|
18
|
+
const parent = dirname(dir);
|
|
19
|
+
if (parent === dir) return void 0;
|
|
20
|
+
dir = parent;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function getCliVersion() {
|
|
24
|
+
cached ??= readOwnVersion() ?? "0.0.0";
|
|
25
|
+
return cached;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// src/commands/dev.ts
|
|
29
|
+
import { defineCommand as defineCommand2 } from "citty";
|
|
30
|
+
import pc from "picocolors";
|
|
31
|
+
|
|
32
|
+
// src/utils/project.ts
|
|
33
|
+
import { existsSync } from "fs";
|
|
34
|
+
import { dirname as dirname2, resolve } from "path";
|
|
35
|
+
import { findConfigFile, loadConfig } from "@nymbalhq/config";
|
|
36
|
+
async function loadProject(startDir = process.cwd()) {
|
|
37
|
+
const configPath = findConfigFile(startDir);
|
|
38
|
+
if (configPath) {
|
|
39
|
+
const envFile = resolve(dirname2(configPath), ".env");
|
|
40
|
+
if (existsSync(envFile)) process.loadEnvFile(envFile);
|
|
41
|
+
}
|
|
42
|
+
return loadConfig(startDir);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// src/commands/dev.ts
|
|
46
|
+
import { createApp, runSeed } from "@nymbalhq/platform";
|
|
47
|
+
import { createHttpServer } from "@nymbalhq/http";
|
|
48
|
+
|
|
49
|
+
// src/utils/spawn.ts
|
|
50
|
+
import { spawn } from "child_process";
|
|
51
|
+
function run(command, args, options = {}) {
|
|
52
|
+
const child = spawn(command, args, {
|
|
53
|
+
stdio: "inherit",
|
|
54
|
+
shell: false,
|
|
55
|
+
...options
|
|
56
|
+
});
|
|
57
|
+
const done = new Promise((resolve4, reject) => {
|
|
58
|
+
child.on("error", reject);
|
|
59
|
+
child.on("exit", (code) => resolve4(code ?? 0));
|
|
60
|
+
});
|
|
61
|
+
return { child, done };
|
|
62
|
+
}
|
|
63
|
+
async function runOrFail(command, args, options = {}) {
|
|
64
|
+
const { done } = run(command, args, options);
|
|
65
|
+
const code = await done;
|
|
66
|
+
if (code !== 0) {
|
|
67
|
+
throw new Error(`${command} ${args.join(" ")} exited with code ${code}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// src/utils/port.ts
|
|
72
|
+
import { createServer } from "net";
|
|
73
|
+
async function isPortFree(port, host) {
|
|
74
|
+
return new Promise((resolve4) => {
|
|
75
|
+
const server = createServer();
|
|
76
|
+
server.once("error", (err) => {
|
|
77
|
+
if (err.code !== "EADDRINUSE" && err.code !== "EACCES") {
|
|
78
|
+
console.error(`[dev] unexpected error probing port ${port}:`, err.message);
|
|
79
|
+
}
|
|
80
|
+
resolve4(false);
|
|
81
|
+
});
|
|
82
|
+
server.once("listening", () => {
|
|
83
|
+
server.close(() => resolve4(true));
|
|
84
|
+
});
|
|
85
|
+
if (host) {
|
|
86
|
+
server.listen(port, host);
|
|
87
|
+
} else {
|
|
88
|
+
server.listen(port);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
async function findFreePort(preferred, maxAttempts = 20, host) {
|
|
93
|
+
if (!Number.isInteger(preferred) || preferred < 1 || preferred > 65535) {
|
|
94
|
+
throw new Error(
|
|
95
|
+
`Invalid storefront port "${preferred}". Set a valid port (1\u201365535) via --storefront-port or NYMBAL_STOREFRONT_PORT.`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
99
|
+
const candidate = preferred + i;
|
|
100
|
+
if (candidate > 65535) break;
|
|
101
|
+
if (await isPortFree(candidate, host)) {
|
|
102
|
+
return candidate;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
throw new Error(
|
|
106
|
+
`No free storefront port found in range ${preferred}\u2013${Math.min(preferred + maxAttempts - 1, 65535)}. Free a port in that range or pass an explicit free port via --storefront-port.`
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// src/utils/template-runner.ts
|
|
111
|
+
import { existsSync as existsSync2 } from "fs";
|
|
112
|
+
import { resolve as resolve2 } from "path";
|
|
113
|
+
function templateFilterFor(template) {
|
|
114
|
+
return template === "astro" ? "@nymbalhq/template-astro" : "@nymbalhq/template-nextjs";
|
|
115
|
+
}
|
|
116
|
+
function isFlatScaffold(projectRoot, template) {
|
|
117
|
+
const marker = template === "astro" ? "astro.config.mjs" : "next.config.mjs";
|
|
118
|
+
const altMarker = template === "astro" ? "astro.config.ts" : "next.config.ts";
|
|
119
|
+
return existsSync2(resolve2(projectRoot, marker)) || existsSync2(resolve2(projectRoot, altMarker));
|
|
120
|
+
}
|
|
121
|
+
function resolveLocalBin(projectRoot, bin) {
|
|
122
|
+
const binPath = resolve2(projectRoot, "node_modules", ".bin", bin);
|
|
123
|
+
return existsSync2(binPath) ? binPath : null;
|
|
124
|
+
}
|
|
125
|
+
function planTemplateLaunch(projectRoot, template, storefrontPort) {
|
|
126
|
+
if (isFlatScaffold(projectRoot, template)) {
|
|
127
|
+
const binName = template === "astro" ? "astro" : "next";
|
|
128
|
+
const localBin = resolveLocalBin(projectRoot, binName);
|
|
129
|
+
if (!localBin) {
|
|
130
|
+
throw new Error(
|
|
131
|
+
`Cannot find the ${binName} dev server in ${projectRoot}/node_modules/.bin. Install dependencies first (e.g. \`pnpm install\`), then re-run \`nymbal dev\`.`
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
cwd: projectRoot,
|
|
136
|
+
command: localBin,
|
|
137
|
+
args: ["dev", "--port", String(storefrontPort), ...foregroundArgs(template)],
|
|
138
|
+
mode: "flat"
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
const filter = templateFilterFor(template);
|
|
142
|
+
return {
|
|
143
|
+
cwd: projectRoot,
|
|
144
|
+
command: "pnpm",
|
|
145
|
+
args: ["--filter", filter, "dev", ...foregroundArgs(template)],
|
|
146
|
+
mode: "filter"
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function foregroundArgs(template) {
|
|
150
|
+
return template === "astro" ? ["--ignore-lock"] : [];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// src/commands/migrate.ts
|
|
154
|
+
import { defineCommand } from "citty";
|
|
155
|
+
import { createCommandStore, runMigrations } from "@nymbalhq/platform";
|
|
156
|
+
import { dirname as dirname3, resolve as resolve3 } from "path";
|
|
157
|
+
import { existsSync as existsSync3 } from "fs";
|
|
158
|
+
var migrateCommand = defineCommand({
|
|
159
|
+
meta: { name: "migrate", description: "Run database migrations (Drizzle)" },
|
|
160
|
+
async run() {
|
|
161
|
+
const { config, projectRoot } = await loadProject();
|
|
162
|
+
const kind = await migrateProject(config, projectRoot);
|
|
163
|
+
console.log(`\u2713 migrations applied (${kind})`);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
async function migrateProject(config, projectRoot) {
|
|
167
|
+
const store = createCommandStore(config);
|
|
168
|
+
try {
|
|
169
|
+
await runMigrations(store, { migrationsRoot: resolveMigrations(projectRoot) });
|
|
170
|
+
return store.kind;
|
|
171
|
+
} finally {
|
|
172
|
+
await store.close();
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function resolveMigrations(projectRoot) {
|
|
176
|
+
const local = resolve3(projectRoot, "migrations");
|
|
177
|
+
if (existsSync3(local)) return local;
|
|
178
|
+
const monorepoDev = resolve3(projectRoot, "packages/platform/migrations");
|
|
179
|
+
if (existsSync3(monorepoDev)) return monorepoDev;
|
|
180
|
+
const pkgDir = resolvePackageDir(projectRoot, "@nymbalhq/platform");
|
|
181
|
+
if (pkgDir) {
|
|
182
|
+
const pkgMigrations = resolve3(pkgDir, "migrations");
|
|
183
|
+
if (existsSync3(pkgMigrations)) return pkgMigrations;
|
|
184
|
+
}
|
|
185
|
+
return local;
|
|
186
|
+
}
|
|
187
|
+
function resolvePackageDir(fromRoot, pkgName) {
|
|
188
|
+
let dir = resolve3(fromRoot);
|
|
189
|
+
for (let i = 0; i < 24; i++) {
|
|
190
|
+
const candidate = resolve3(dir, "node_modules", pkgName);
|
|
191
|
+
if (existsSync3(resolve3(candidate, "package.json"))) {
|
|
192
|
+
return candidate;
|
|
193
|
+
}
|
|
194
|
+
const parent = dirname3(dir);
|
|
195
|
+
if (parent === dir) break;
|
|
196
|
+
dir = parent;
|
|
197
|
+
}
|
|
198
|
+
return null;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// src/admin.ts
|
|
202
|
+
import { existsSync as existsSync4 } from "fs";
|
|
203
|
+
import { createRequire } from "module";
|
|
204
|
+
import { dirname as dirname4, join as join2 } from "path";
|
|
205
|
+
function hasIndex(dir) {
|
|
206
|
+
return existsSync4(join2(dir, "index.html"));
|
|
207
|
+
}
|
|
208
|
+
function resolveAdminRoot() {
|
|
209
|
+
const override = process.env["NYMBAL_ADMIN_DIST"];
|
|
210
|
+
if (override && hasIndex(override)) return override;
|
|
211
|
+
try {
|
|
212
|
+
const manifest = createRequire(import.meta.url).resolve("@nymbalhq/admin/package.json");
|
|
213
|
+
const dist = join2(dirname4(manifest), "dist");
|
|
214
|
+
return hasIndex(dist) ? dist : null;
|
|
215
|
+
} catch {
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// src/commands/dev.ts
|
|
221
|
+
function defaultStorefrontPort(template) {
|
|
222
|
+
return template === "astro" ? 4321 : 3e3;
|
|
223
|
+
}
|
|
224
|
+
function preferredStorefrontPort(template, flagPort, env) {
|
|
225
|
+
if (typeof flagPort === "number" && Number.isFinite(flagPort)) return flagPort;
|
|
226
|
+
const fromEnv = env["NYMBAL_STOREFRONT_PORT"];
|
|
227
|
+
if (fromEnv && fromEnv.trim() !== "") {
|
|
228
|
+
const parsed = Number(fromEnv);
|
|
229
|
+
if (Number.isInteger(parsed) && parsed > 0) return parsed;
|
|
230
|
+
}
|
|
231
|
+
return defaultStorefrontPort(template);
|
|
232
|
+
}
|
|
233
|
+
function storefrontEnv(config, apiUrl, storefrontPort, baseEnv = process.env) {
|
|
234
|
+
const stubPayments = config.commerce.payments.provider === "stripe" ? {} : {
|
|
235
|
+
PUBLIC_NYMBAL_PAYMENTS_PROVIDER: "native-stub",
|
|
236
|
+
// Astro
|
|
237
|
+
NEXT_PUBLIC_NYMBAL_PAYMENTS_PROVIDER: "native-stub"
|
|
238
|
+
// Next.js
|
|
239
|
+
};
|
|
240
|
+
return {
|
|
241
|
+
...baseEnv,
|
|
242
|
+
NYMBAL_API_URL: apiUrl,
|
|
243
|
+
PUBLIC_NYMBAL_API_URL: apiUrl,
|
|
244
|
+
// Astro
|
|
245
|
+
NEXT_PUBLIC_NYMBAL_API_URL: apiUrl,
|
|
246
|
+
// Next.js
|
|
247
|
+
// The framework also reads the port flag; the env var keeps the scaffold
|
|
248
|
+
// config (astro.config.mjs) in sync when it honours NYMBAL_STOREFRONT_PORT.
|
|
249
|
+
NYMBAL_STOREFRONT_PORT: String(storefrontPort),
|
|
250
|
+
...stubPayments
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
var devCommand = defineCommand2({
|
|
254
|
+
meta: {
|
|
255
|
+
name: "dev",
|
|
256
|
+
description: "Start the API server and the configured storefront template with HMR"
|
|
257
|
+
},
|
|
258
|
+
args: {
|
|
259
|
+
seed: {
|
|
260
|
+
type: "boolean",
|
|
261
|
+
description: "Run seed before starting (default: true if empty)",
|
|
262
|
+
default: true
|
|
263
|
+
},
|
|
264
|
+
apiOnly: {
|
|
265
|
+
type: "boolean",
|
|
266
|
+
// `alias` is required: citty 0.1.6 registers the camelCase key `apiOnly`
|
|
267
|
+
// with mri and applies `default: false`. Without this alias a kebab
|
|
268
|
+
// `--api-only` flag lands on `args['api-only']`, but `args.apiOnly`
|
|
269
|
+
// resolves to the `false` default first (`??` does not fall through on
|
|
270
|
+
// `false`), so the flag is silently ignored. The alias makes mri map
|
|
271
|
+
// `--api-only` straight to `apiOnly`. Both forms now work.
|
|
272
|
+
alias: "api-only",
|
|
273
|
+
description: "Start only the API server (skip template dev server)",
|
|
274
|
+
default: false
|
|
275
|
+
},
|
|
276
|
+
storefrontPort: {
|
|
277
|
+
type: "string",
|
|
278
|
+
alias: "storefront-port",
|
|
279
|
+
description: "Port for the storefront dev server (default: 4321 Astro / 3000 Next.js). If busy, the next free port is used. Also settable via NYMBAL_STOREFRONT_PORT."
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
async run({ args }) {
|
|
283
|
+
const { config, projectRoot } = await loadProject();
|
|
284
|
+
const version = getCliVersion();
|
|
285
|
+
await migrateProject(config, projectRoot);
|
|
286
|
+
const app = await createApp(config, { version });
|
|
287
|
+
const { platform } = app;
|
|
288
|
+
if (args.seed) {
|
|
289
|
+
const existing = await platform.documentStore.query("products", {
|
|
290
|
+
partitionKey: { field: "storeId", value: config.store.name },
|
|
291
|
+
limit: 1
|
|
292
|
+
});
|
|
293
|
+
if (existing.items.length === 0) {
|
|
294
|
+
platform.logger.info({}, "Seeding demo content (first run)");
|
|
295
|
+
await runSeed({
|
|
296
|
+
config,
|
|
297
|
+
commandStore: platform.commandStore,
|
|
298
|
+
documentStore: platform.documentStore,
|
|
299
|
+
eventBus: platform.eventBus,
|
|
300
|
+
logger: platform.logger,
|
|
301
|
+
version
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
const http = createHttpServer({
|
|
306
|
+
config,
|
|
307
|
+
logger: platform.logger,
|
|
308
|
+
documentStore: platform.documentStore,
|
|
309
|
+
version
|
|
310
|
+
});
|
|
311
|
+
const adminRoot = resolveAdminRoot();
|
|
312
|
+
if (adminRoot === null) {
|
|
313
|
+
console.log(
|
|
314
|
+
pc.yellow(
|
|
315
|
+
"\u2192 The admin is not built, so /admin/ answers 404: run `pnpm --filter @nymbalhq/admin build`."
|
|
316
|
+
)
|
|
317
|
+
);
|
|
318
|
+
}
|
|
319
|
+
await app.attachHttp(http.adapter, { adminRoot });
|
|
320
|
+
await http.start();
|
|
321
|
+
if (args.apiOnly) {
|
|
322
|
+
const shutdown2 = async (signal) => {
|
|
323
|
+
platform.logger.info({ signal }, "Shutting down API");
|
|
324
|
+
await http.stop().catch(() => {
|
|
325
|
+
});
|
|
326
|
+
await app.stop().catch(() => {
|
|
327
|
+
});
|
|
328
|
+
process.exit(0);
|
|
329
|
+
};
|
|
330
|
+
process.on("SIGINT", () => void shutdown2("SIGINT"));
|
|
331
|
+
process.on("SIGTERM", () => void shutdown2("SIGTERM"));
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
334
|
+
const flagPortRaw = args.storefrontPort;
|
|
335
|
+
const flagPort = typeof flagPortRaw === "string" && flagPortRaw.trim() !== "" ? Number(flagPortRaw) : void 0;
|
|
336
|
+
const preferred = preferredStorefrontPort(config.template, flagPort, process.env);
|
|
337
|
+
const storefrontPort = await findFreePort(preferred);
|
|
338
|
+
if (storefrontPort !== preferred) {
|
|
339
|
+
console.log(
|
|
340
|
+
pc.yellow(
|
|
341
|
+
`\u2192 Storefront port ${preferred} is busy; using free port ${pc.bold(String(storefrontPort))} instead.`
|
|
342
|
+
)
|
|
343
|
+
);
|
|
344
|
+
}
|
|
345
|
+
const launch = planTemplateLaunch(projectRoot, config.template, storefrontPort);
|
|
346
|
+
const apiUrl = `http://localhost:${config.http.port}`;
|
|
347
|
+
const storefrontUrl = `http://localhost:${storefrontPort}`;
|
|
348
|
+
console.log(
|
|
349
|
+
pc.cyan(
|
|
350
|
+
`\u2192 Starting storefront (${pc.bold(config.template)}, ${launch.mode}) on ${pc.bold(storefrontUrl)}`
|
|
351
|
+
)
|
|
352
|
+
);
|
|
353
|
+
console.log(pc.dim(` API: ${apiUrl}`));
|
|
354
|
+
const templateProc = run(launch.command, launch.args, {
|
|
355
|
+
cwd: launch.cwd,
|
|
356
|
+
env: storefrontEnv(config, apiUrl, storefrontPort)
|
|
357
|
+
});
|
|
358
|
+
const shutdown = async (signal) => {
|
|
359
|
+
platform.logger.info({ signal }, "Shutting down");
|
|
360
|
+
try {
|
|
361
|
+
templateProc.child.kill("SIGINT");
|
|
362
|
+
} catch {
|
|
363
|
+
}
|
|
364
|
+
await http.stop().catch(() => {
|
|
365
|
+
});
|
|
366
|
+
await app.stop().catch(() => {
|
|
367
|
+
});
|
|
368
|
+
process.exit(0);
|
|
369
|
+
};
|
|
370
|
+
process.on("SIGINT", () => void shutdown("SIGINT"));
|
|
371
|
+
process.on("SIGTERM", () => void shutdown("SIGTERM"));
|
|
372
|
+
const templateExit = await templateProc.done;
|
|
373
|
+
platform.logger.info({ code: templateExit }, "Template process exited");
|
|
374
|
+
await shutdown("template-exit");
|
|
375
|
+
}
|
|
376
|
+
});
|
|
377
|
+
|
|
378
|
+
// src/commands/build.ts
|
|
379
|
+
import { defineCommand as defineCommand3 } from "citty";
|
|
380
|
+
var buildCommand = defineCommand3({
|
|
381
|
+
meta: { name: "build", description: "Production build of the configured template" },
|
|
382
|
+
async run() {
|
|
383
|
+
const { config, projectRoot } = await loadProject();
|
|
384
|
+
if (!isFlatScaffold(projectRoot, config.template)) {
|
|
385
|
+
await runOrFail("pnpm", ["--filter", templateFilterFor(config.template), "build"], {
|
|
386
|
+
cwd: projectRoot
|
|
387
|
+
});
|
|
388
|
+
return;
|
|
389
|
+
}
|
|
390
|
+
const binName = config.template === "astro" ? "astro" : "next";
|
|
391
|
+
const bin = resolveLocalBin(projectRoot, binName);
|
|
392
|
+
if (!bin) {
|
|
393
|
+
throw new Error(
|
|
394
|
+
`Cannot find the ${binName} binary in ${projectRoot}/node_modules/.bin. Install dependencies first, then re-run \`nymbal build\`.`
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
await runOrFail(bin, ["build"], { cwd: projectRoot });
|
|
398
|
+
}
|
|
399
|
+
});
|
|
400
|
+
|
|
401
|
+
// src/commands/seed.ts
|
|
402
|
+
import { defineCommand as defineCommand4 } from "citty";
|
|
403
|
+
import { createPlatform, runSeed as runSeed2 } from "@nymbalhq/platform";
|
|
404
|
+
var seedCommand = defineCommand4({
|
|
405
|
+
meta: { name: "seed", description: "Re-seed demo content (products, categories)" },
|
|
406
|
+
async run() {
|
|
407
|
+
const { config, projectRoot } = await loadProject();
|
|
408
|
+
await migrateProject(config, projectRoot);
|
|
409
|
+
const platform = createPlatform(config);
|
|
410
|
+
try {
|
|
411
|
+
const result = await runSeed2({
|
|
412
|
+
config,
|
|
413
|
+
commandStore: platform.commandStore,
|
|
414
|
+
documentStore: platform.documentStore,
|
|
415
|
+
eventBus: platform.eventBus,
|
|
416
|
+
logger: platform.logger,
|
|
417
|
+
version: getCliVersion()
|
|
418
|
+
});
|
|
419
|
+
console.log(
|
|
420
|
+
result.alreadySeeded ? "\u2713 demo content already present, nothing to seed" : `\u2713 seeded ${result.products} products across ${result.categories} categories`
|
|
421
|
+
);
|
|
422
|
+
} finally {
|
|
423
|
+
await platform.close();
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
// src/commands/import.ts
|
|
429
|
+
import { defineCommand as defineCommand5 } from "citty";
|
|
430
|
+
import { intro, outro, text, select, spinner, isCancel, cancel } from "@clack/prompts";
|
|
431
|
+
import * as p from "picocolors";
|
|
432
|
+
import { createApp as createApp2 } from "@nymbalhq/platform";
|
|
433
|
+
import { runWooCommerceImport, writeReport } from "@nymbalhq/importers";
|
|
434
|
+
import { join as join3 } from "path";
|
|
435
|
+
var importCommand = defineCommand5({
|
|
436
|
+
meta: {
|
|
437
|
+
name: "import",
|
|
438
|
+
description: "Migrate content from WooCommerce (or other sources) into Nymbal"
|
|
439
|
+
},
|
|
440
|
+
args: {
|
|
441
|
+
source: {
|
|
442
|
+
type: "string",
|
|
443
|
+
description: "Import source (woocommerce)"
|
|
444
|
+
},
|
|
445
|
+
url: {
|
|
446
|
+
type: "string",
|
|
447
|
+
description: "Store URL (e.g. https://mystore.com)"
|
|
448
|
+
},
|
|
449
|
+
key: {
|
|
450
|
+
type: "string",
|
|
451
|
+
description: "WooCommerce Consumer Key (or set WOOCOMMERCE_KEY env var)"
|
|
452
|
+
},
|
|
453
|
+
secret: {
|
|
454
|
+
type: "string",
|
|
455
|
+
description: "WooCommerce Consumer Secret (or set WOOCOMMERCE_SECRET env var)"
|
|
456
|
+
},
|
|
457
|
+
entities: {
|
|
458
|
+
type: "string",
|
|
459
|
+
description: "Comma-separated list: products,customers,orders,reviews (default: products,customers,orders)"
|
|
460
|
+
},
|
|
461
|
+
ai: {
|
|
462
|
+
type: "boolean",
|
|
463
|
+
description: "Enable AI enrichment (default: true)",
|
|
464
|
+
default: true
|
|
465
|
+
},
|
|
466
|
+
gdpr: {
|
|
467
|
+
type: "boolean",
|
|
468
|
+
description: "Anonymise customer PII (default: true)",
|
|
469
|
+
default: true
|
|
470
|
+
},
|
|
471
|
+
fresh: {
|
|
472
|
+
type: "boolean",
|
|
473
|
+
description: "Ignore previous progress and re-import",
|
|
474
|
+
default: false
|
|
475
|
+
},
|
|
476
|
+
concurrency: {
|
|
477
|
+
type: "string",
|
|
478
|
+
description: "Parallel import workers (default: 4)"
|
|
479
|
+
}
|
|
480
|
+
},
|
|
481
|
+
async run({ args }) {
|
|
482
|
+
const { config } = await loadProject();
|
|
483
|
+
const projectRoot = process.cwd();
|
|
484
|
+
const statePath = join3(projectRoot, ".nymbal-migration.json");
|
|
485
|
+
const isTTY = process.stdin.isTTY ?? false;
|
|
486
|
+
intro(p.bold("Nymbal Import"));
|
|
487
|
+
let source = args.source;
|
|
488
|
+
if (!source) {
|
|
489
|
+
if (!isTTY) {
|
|
490
|
+
console.error("--source is required (woocommerce)");
|
|
491
|
+
process.exit(1);
|
|
492
|
+
}
|
|
493
|
+
const picked = await select({
|
|
494
|
+
message: "Source platform?",
|
|
495
|
+
options: [{ value: "woocommerce", label: "WooCommerce" }]
|
|
496
|
+
});
|
|
497
|
+
if (isCancel(picked)) {
|
|
498
|
+
cancel("Import cancelled");
|
|
499
|
+
process.exit(0);
|
|
500
|
+
}
|
|
501
|
+
source = String(picked);
|
|
502
|
+
}
|
|
503
|
+
if (source !== "woocommerce") {
|
|
504
|
+
console.error(`Unsupported source: ${source}. Only 'woocommerce' is supported.`);
|
|
505
|
+
process.exit(1);
|
|
506
|
+
}
|
|
507
|
+
let storeUrl = args.url;
|
|
508
|
+
if (!storeUrl) {
|
|
509
|
+
if (!isTTY) {
|
|
510
|
+
console.error("--url is required");
|
|
511
|
+
process.exit(1);
|
|
512
|
+
}
|
|
513
|
+
const val = await text({ message: "Store URL?", placeholder: "https://mystore.com", validate: (v) => v.startsWith("http") ? void 0 : "Must be a URL" });
|
|
514
|
+
if (isCancel(val)) {
|
|
515
|
+
cancel("Import cancelled");
|
|
516
|
+
process.exit(0);
|
|
517
|
+
}
|
|
518
|
+
storeUrl = String(val);
|
|
519
|
+
}
|
|
520
|
+
let consumerKey = args.key ?? process.env["WOOCOMMERCE_KEY"] ?? "";
|
|
521
|
+
if (!consumerKey) {
|
|
522
|
+
if (!isTTY) {
|
|
523
|
+
console.error("--key or WOOCOMMERCE_KEY is required");
|
|
524
|
+
process.exit(1);
|
|
525
|
+
}
|
|
526
|
+
const val = await text({ message: "Consumer Key?", placeholder: "ck_..." });
|
|
527
|
+
if (isCancel(val)) {
|
|
528
|
+
cancel("Import cancelled");
|
|
529
|
+
process.exit(0);
|
|
530
|
+
}
|
|
531
|
+
consumerKey = String(val);
|
|
532
|
+
}
|
|
533
|
+
let consumerSecret = args.secret ?? process.env["WOOCOMMERCE_SECRET"] ?? "";
|
|
534
|
+
if (!consumerSecret) {
|
|
535
|
+
if (!isTTY) {
|
|
536
|
+
console.error("--secret or WOOCOMMERCE_SECRET is required");
|
|
537
|
+
process.exit(1);
|
|
538
|
+
}
|
|
539
|
+
const val = await text({ message: "Consumer Secret?", placeholder: "cs_..." });
|
|
540
|
+
if (isCancel(val)) {
|
|
541
|
+
cancel("Import cancelled");
|
|
542
|
+
process.exit(0);
|
|
543
|
+
}
|
|
544
|
+
consumerSecret = String(val);
|
|
545
|
+
}
|
|
546
|
+
let entityList;
|
|
547
|
+
if (args.entities) {
|
|
548
|
+
entityList = args.entities.split(",").map((e) => e.trim());
|
|
549
|
+
} else {
|
|
550
|
+
entityList = ["categories", "products", "customers", "orders"];
|
|
551
|
+
}
|
|
552
|
+
const concurrency = parseInt(String(args.concurrency ?? "4"), 10);
|
|
553
|
+
const aiEnabled = args.ai !== false;
|
|
554
|
+
const gdprEnabled = args.gdpr !== false;
|
|
555
|
+
const loadSpin = spinner();
|
|
556
|
+
loadSpin.start("Starting platform\u2026");
|
|
557
|
+
const app = await createApp2(config, { version: getCliVersion() });
|
|
558
|
+
loadSpin.stop("Platform ready");
|
|
559
|
+
try {
|
|
560
|
+
const report = await runWooCommerceImport(
|
|
561
|
+
{
|
|
562
|
+
credentials: { url: storeUrl, consumerKey, consumerSecret },
|
|
563
|
+
entities: entityList,
|
|
564
|
+
ai: aiEnabled,
|
|
565
|
+
gdpr: gdprEnabled,
|
|
566
|
+
fresh: args.fresh,
|
|
567
|
+
concurrency,
|
|
568
|
+
projectRoot,
|
|
569
|
+
currency: config.store.currency,
|
|
570
|
+
statePath
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
services: app.services,
|
|
574
|
+
repos: app.repos,
|
|
575
|
+
adapters: { ai: app.adapters.ai, reviews: app.adapters.reviews },
|
|
576
|
+
onProgress(event) {
|
|
577
|
+
if (event.kind === "phase") {
|
|
578
|
+
console.log(p.cyan(` \u2192 ${event.phase}`));
|
|
579
|
+
} else if (event.kind === "progress") {
|
|
580
|
+
const pct = Math.round(event.done / event.total * 100);
|
|
581
|
+
process.stdout.write(`\r ${event.entity}: ${event.done}/${event.total} (${pct}%) `);
|
|
582
|
+
if (event.done === event.total) process.stdout.write("\n");
|
|
583
|
+
} else if (event.kind === "error") {
|
|
584
|
+
console.log(p.red(` \u2717 ${event.phase} [${event.id}]: ${event.message}`));
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
}
|
|
588
|
+
);
|
|
589
|
+
const reportPath = await writeReport(report, projectRoot, config.store.name);
|
|
590
|
+
outro(
|
|
591
|
+
[
|
|
592
|
+
p.green("\u2713 Import complete"),
|
|
593
|
+
` Products: ${report.imported.products}`,
|
|
594
|
+
` Customers: ${report.imported.customers}`,
|
|
595
|
+
` Orders: ${report.imported.orders}`,
|
|
596
|
+
` Categories: ${report.imported.categories}`,
|
|
597
|
+
...aiEnabled ? [` AI enriched: ${report.enriched.enriched} products`] : [],
|
|
598
|
+
...report.errors.length > 0 ? [p.yellow(` \u26A0 ${report.errors.length} errors \u2014 see report`)] : [],
|
|
599
|
+
``,
|
|
600
|
+
` Report: ${reportPath}`,
|
|
601
|
+
` Next: ${p.bold("nymbal dev")}`
|
|
602
|
+
].join("\n")
|
|
603
|
+
);
|
|
604
|
+
} finally {
|
|
605
|
+
await app.stop();
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
});
|
|
609
|
+
|
|
610
|
+
// src/commands/config-validate.ts
|
|
611
|
+
import { defineCommand as defineCommand6 } from "citty";
|
|
612
|
+
import pc2 from "picocolors";
|
|
613
|
+
import { NymbalError } from "@nymbalhq/types";
|
|
614
|
+
var configValidateCommand = defineCommand6({
|
|
615
|
+
meta: { name: "config:validate", description: "Validate nymbal.config.ts" },
|
|
616
|
+
async run() {
|
|
617
|
+
try {
|
|
618
|
+
const { config, path } = await loadProject();
|
|
619
|
+
console.log(pc2.green(`\u2713 config valid`) + ` (${path})`);
|
|
620
|
+
console.log(
|
|
621
|
+
` template: ${config.template}, cloud: ${config.infrastructure.cloud}, commandStore: ${config.infrastructure.commandStore}`
|
|
622
|
+
);
|
|
623
|
+
process.exit(0);
|
|
624
|
+
} catch (err) {
|
|
625
|
+
if (err instanceof NymbalError) {
|
|
626
|
+
console.error(pc2.red("\u2717 config invalid"));
|
|
627
|
+
console.error(err.message);
|
|
628
|
+
} else {
|
|
629
|
+
console.error(err);
|
|
630
|
+
}
|
|
631
|
+
process.exit(1);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
|
|
636
|
+
// src/commands/test.ts
|
|
637
|
+
import { defineCommand as defineCommand7 } from "citty";
|
|
638
|
+
import { loadConfig as loadConfig2 } from "@nymbalhq/config";
|
|
639
|
+
var testCommand = defineCommand7({
|
|
640
|
+
meta: { name: "test", description: "Run unit + integration tests (vitest)" },
|
|
641
|
+
async run() {
|
|
642
|
+
await runOrFail("pnpm", ["vitest", "run"]);
|
|
643
|
+
}
|
|
644
|
+
});
|
|
645
|
+
var testE2eCommand = defineCommand7({
|
|
646
|
+
meta: { name: "test:e2e", description: "Run Playwright E2E against the configured template" },
|
|
647
|
+
async run() {
|
|
648
|
+
const { config } = await loadConfig2();
|
|
649
|
+
await runOrFail("pnpm", [
|
|
650
|
+
"--filter",
|
|
651
|
+
"@nymbalhq/e2e",
|
|
652
|
+
"exec",
|
|
653
|
+
"playwright",
|
|
654
|
+
"test",
|
|
655
|
+
"--project",
|
|
656
|
+
config.template,
|
|
657
|
+
"--grep-invert",
|
|
658
|
+
"@visual|@a11y"
|
|
659
|
+
]);
|
|
660
|
+
}
|
|
661
|
+
});
|
|
662
|
+
var testVisualCommand = defineCommand7({
|
|
663
|
+
meta: { name: "test:visual", description: "Run visual regression tests (Playwright toHaveScreenshot)" },
|
|
664
|
+
args: {
|
|
665
|
+
"update-baselines": {
|
|
666
|
+
type: "boolean",
|
|
667
|
+
description: "Regenerate screenshot baselines",
|
|
668
|
+
default: false
|
|
669
|
+
}
|
|
670
|
+
},
|
|
671
|
+
async run({ args }) {
|
|
672
|
+
const { config } = await loadConfig2();
|
|
673
|
+
const isCI = !!process.env.CI;
|
|
674
|
+
const updateBaselines = args["update-baselines"];
|
|
675
|
+
if (updateBaselines && !isCI) {
|
|
676
|
+
const dockerImage = "mcr.microsoft.com/playwright:v1.50-jammy";
|
|
677
|
+
const cwd = process.cwd();
|
|
678
|
+
await runOrFail("docker", [
|
|
679
|
+
"run",
|
|
680
|
+
"--rm",
|
|
681
|
+
"-v",
|
|
682
|
+
`${cwd}:${cwd}`,
|
|
683
|
+
"-w",
|
|
684
|
+
cwd,
|
|
685
|
+
dockerImage,
|
|
686
|
+
"sh",
|
|
687
|
+
"-c",
|
|
688
|
+
`pnpm --filter @nymbalhq/e2e exec playwright test --project ${config.template} --grep "@visual" --update-snapshots`
|
|
689
|
+
]);
|
|
690
|
+
} else {
|
|
691
|
+
const pwArgs = [
|
|
692
|
+
"--filter",
|
|
693
|
+
"@nymbalhq/e2e",
|
|
694
|
+
"exec",
|
|
695
|
+
"playwright",
|
|
696
|
+
"test",
|
|
697
|
+
"--project",
|
|
698
|
+
config.template,
|
|
699
|
+
"--grep",
|
|
700
|
+
"@visual"
|
|
701
|
+
];
|
|
702
|
+
if (updateBaselines) pwArgs.push("--update-snapshots");
|
|
703
|
+
await runOrFail("pnpm", pwArgs);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
});
|
|
707
|
+
var testContractsCommand = defineCommand7({
|
|
708
|
+
meta: { name: "test:contracts", description: "Run adapter contract tests" },
|
|
709
|
+
args: {
|
|
710
|
+
adapter: { type: "string", description: "Run only contracts for a specific adapter (e.g. payments, email)" }
|
|
711
|
+
},
|
|
712
|
+
async run({ args }) {
|
|
713
|
+
const vArgs = [
|
|
714
|
+
"vitest",
|
|
715
|
+
"run",
|
|
716
|
+
"--project",
|
|
717
|
+
"contracts-native"
|
|
718
|
+
];
|
|
719
|
+
if (args.adapter) {
|
|
720
|
+
vArgs.push(`tests/contracts/${String(args.adapter)}.test.ts`);
|
|
721
|
+
}
|
|
722
|
+
await runOrFail("pnpm", vArgs);
|
|
723
|
+
}
|
|
724
|
+
});
|
|
725
|
+
var testPerformanceCommand = defineCommand7({
|
|
726
|
+
meta: { name: "test:performance", description: "Run Lighthouse CI against the configured template" },
|
|
727
|
+
async run() {
|
|
728
|
+
const { config } = await loadConfig2();
|
|
729
|
+
const port = config.template === "nextjs" ? 3e3 : 4321;
|
|
730
|
+
await runOrFail("pnpm", ["lhci", "autorun"], {
|
|
731
|
+
env: {
|
|
732
|
+
...process.env,
|
|
733
|
+
NYMBAL_TEMPLATE: config.template,
|
|
734
|
+
NYMBAL_TEMPLATE_PORT: String(port)
|
|
735
|
+
}
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
});
|
|
739
|
+
var testAllCommand = defineCommand7({
|
|
740
|
+
meta: { name: "test:all", description: "Run unit, contract, e2e, visual, and performance tests" },
|
|
741
|
+
async run() {
|
|
742
|
+
const steps = [
|
|
743
|
+
{ name: "unit", args: ["vitest", "run"] },
|
|
744
|
+
{ name: "contracts", args: ["vitest", "run", "--project", "contracts-native"] }
|
|
745
|
+
];
|
|
746
|
+
for (const step of steps) {
|
|
747
|
+
const { done } = run("pnpm", step.args);
|
|
748
|
+
const code = await done;
|
|
749
|
+
if (code !== 0) {
|
|
750
|
+
console.error(`\u2717 ${step.name} failed`);
|
|
751
|
+
process.exit(code);
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
const { config } = await loadConfig2();
|
|
755
|
+
await runOrFail("pnpm", [
|
|
756
|
+
"--filter",
|
|
757
|
+
"@nymbalhq/e2e",
|
|
758
|
+
"exec",
|
|
759
|
+
"playwright",
|
|
760
|
+
"test",
|
|
761
|
+
"--project",
|
|
762
|
+
config.template,
|
|
763
|
+
"--grep-invert",
|
|
764
|
+
"@visual|@a11y"
|
|
765
|
+
]);
|
|
766
|
+
await runOrFail("pnpm", [
|
|
767
|
+
"--filter",
|
|
768
|
+
"@nymbalhq/e2e",
|
|
769
|
+
"exec",
|
|
770
|
+
"playwright",
|
|
771
|
+
"test",
|
|
772
|
+
"--project",
|
|
773
|
+
config.template,
|
|
774
|
+
"--grep",
|
|
775
|
+
"@visual"
|
|
776
|
+
]);
|
|
777
|
+
await runOrFail("pnpm", ["lhci", "autorun"], {
|
|
778
|
+
env: { ...process.env, NYMBAL_TEMPLATE: config.template }
|
|
779
|
+
});
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
// src/index.ts
|
|
784
|
+
var main = defineCommand8({
|
|
785
|
+
meta: {
|
|
786
|
+
name: "nymbal",
|
|
787
|
+
version: getCliVersion(),
|
|
788
|
+
description: "Nymbal developer CLI"
|
|
789
|
+
},
|
|
790
|
+
subCommands: {
|
|
791
|
+
dev: devCommand,
|
|
792
|
+
build: buildCommand,
|
|
793
|
+
migrate: migrateCommand,
|
|
794
|
+
seed: seedCommand,
|
|
795
|
+
import: importCommand,
|
|
796
|
+
"config:validate": configValidateCommand,
|
|
797
|
+
test: testCommand,
|
|
798
|
+
"test:e2e": testE2eCommand,
|
|
799
|
+
"test:visual": testVisualCommand,
|
|
800
|
+
"test:contracts": testContractsCommand,
|
|
801
|
+
"test:performance": testPerformanceCommand,
|
|
802
|
+
"test:all": testAllCommand
|
|
803
|
+
}
|
|
804
|
+
});
|
|
805
|
+
var [flag, ...rest] = process.argv.slice(2);
|
|
806
|
+
if (rest.length === 0 && (flag === "--version" || flag === "-v")) {
|
|
807
|
+
process.stdout.write(`${getCliVersion()}
|
|
808
|
+
`);
|
|
809
|
+
} else {
|
|
810
|
+
runMain(main);
|
|
811
|
+
}
|
|
812
|
+
export {
|
|
813
|
+
resolveAdminRoot
|
|
814
|
+
};
|
|
815
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/utils/version.ts","../src/commands/dev.ts","../src/utils/project.ts","../src/utils/spawn.ts","../src/utils/port.ts","../src/utils/template-runner.ts","../src/commands/migrate.ts","../src/admin.ts","../src/commands/build.ts","../src/commands/seed.ts","../src/commands/import.ts","../src/commands/config-validate.ts","../src/commands/test.ts"],"sourcesContent":["import { defineCommand, runMain } from 'citty'\nimport { getCliVersion } from './utils/version.js'\nimport { devCommand } from './commands/dev.js'\nimport { buildCommand } from './commands/build.js'\nimport { migrateCommand } from './commands/migrate.js'\nimport { seedCommand } from './commands/seed.js'\nimport { importCommand } from './commands/import.js'\nimport { configValidateCommand } from './commands/config-validate.js'\nimport {\n testAllCommand,\n testCommand,\n testContractsCommand,\n testE2eCommand,\n testPerformanceCommand,\n testVisualCommand,\n} from './commands/test.js'\n\nexport { resolveAdminRoot } from './admin.js'\n\nconst main = defineCommand({\n meta: {\n name: 'nymbal',\n version: getCliVersion(),\n description: 'Nymbal developer CLI',\n },\n subCommands: {\n dev: devCommand,\n build: buildCommand,\n migrate: migrateCommand,\n seed: seedCommand,\n import: importCommand,\n 'config:validate': configValidateCommand,\n test: testCommand,\n 'test:e2e': testE2eCommand,\n 'test:visual': testVisualCommand,\n 'test:contracts': testContractsCommand,\n 'test:performance': testPerformanceCommand,\n 'test:all': testAllCommand,\n },\n})\n\n// citty prints --version through consola, which prefixes `[log]` when CI is set; scripts need the bare version.\nconst [flag, ...rest] = process.argv.slice(2)\nif (rest.length === 0 && (flag === '--version' || flag === '-v')) {\n process.stdout.write(`${getCliVersion()}\\n`)\n} else {\n runMain(main)\n}\n","import { readFileSync } from 'node:fs'\nimport { dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst CLI_PACKAGE_NAME = 'nymbal'\n\nlet cached: string | null = null\n\n/**\n * Walks up from this module to the CLI's own package.json (found by name, so a nested or unrelated package.json\n * never answers). That is `dist/` in the workspace and in `node_modules/nymbal`, and `src/utils/` under Vitest.\n */\nfunction readOwnVersion(): string | undefined {\n let dir = dirname(fileURLToPath(import.meta.url))\n for (;;) {\n try {\n const pkg = JSON.parse(readFileSync(join(dir, 'package.json'), 'utf8')) as {\n name?: string\n version?: string\n }\n if (pkg.name === CLI_PACKAGE_NAME) return pkg.version\n } catch {\n // No readable package.json in this directory: keep walking.\n }\n const parent = dirname(dir)\n if (parent === dir) return undefined\n dir = parent\n }\n}\n\nexport function getCliVersion(): string {\n cached ??= readOwnVersion() ?? '0.0.0'\n return cached\n}\n","import { defineCommand } from 'citty'\nimport pc from 'picocolors'\nimport type { NymbalConfig } from '@nymbalhq/config'\nimport { loadProject } from '../utils/project.js'\nimport { createApp, runSeed } from '@nymbalhq/platform'\nimport { createHttpServer } from '@nymbalhq/http'\nimport { run } from '../utils/spawn.js'\nimport { getCliVersion } from '../utils/version.js'\nimport { findFreePort } from '../utils/port.js'\nimport { planTemplateLaunch } from '../utils/template-runner.js'\nimport { migrateProject } from './migrate.js'\nimport { resolveAdminRoot } from '../admin.js'\n\n/** Default storefront dev-server port per template (matches the scaffold scripts). */\nexport function defaultStorefrontPort(template: 'astro' | 'nextjs'): number {\n return template === 'astro' ? 4321 : 3000\n}\n\n/**\n * Resolve the *preferred* storefront port from (in priority order):\n * 1. --storefront-port flag\n * 2. NYMBAL_STOREFRONT_PORT env var\n * 3. the template default (Astro 4321 / Next.js 3000)\n * The value returned here is only the starting point; the dev command then\n * probes upward for a genuinely free port so a busy default never hard-fails.\n */\nexport function preferredStorefrontPort(\n template: 'astro' | 'nextjs',\n flagPort: number | undefined,\n env: NodeJS.ProcessEnv,\n): number {\n if (typeof flagPort === 'number' && Number.isFinite(flagPort)) return flagPort\n const fromEnv = env['NYMBAL_STOREFRONT_PORT']\n if (fromEnv && fromEnv.trim() !== '') {\n const parsed = Number(fromEnv)\n if (Number.isInteger(parsed) && parsed > 0) return parsed\n }\n return defaultStorefrontPort(template)\n}\n\n/**\n * The environment the storefront dev server starts with. The client-side variables must be set before it starts, and\n * both templates skip Stripe when the payments provider variable says `native-stub`, which is every provider but\n * `stripe`.\n */\nexport function storefrontEnv(\n config: NymbalConfig,\n apiUrl: string,\n storefrontPort: number,\n baseEnv: NodeJS.ProcessEnv = process.env,\n): NodeJS.ProcessEnv {\n const stubPayments =\n config.commerce.payments.provider === 'stripe'\n ? {}\n : {\n PUBLIC_NYMBAL_PAYMENTS_PROVIDER: 'native-stub', // Astro\n NEXT_PUBLIC_NYMBAL_PAYMENTS_PROVIDER: 'native-stub', // Next.js\n }\n return {\n ...baseEnv,\n NYMBAL_API_URL: apiUrl,\n PUBLIC_NYMBAL_API_URL: apiUrl, // Astro\n NEXT_PUBLIC_NYMBAL_API_URL: apiUrl, // Next.js\n // The framework also reads the port flag; the env var keeps the scaffold\n // config (astro.config.mjs) in sync when it honours NYMBAL_STOREFRONT_PORT.\n NYMBAL_STOREFRONT_PORT: String(storefrontPort),\n ...stubPayments,\n }\n}\n\nexport const devCommand = defineCommand({\n meta: {\n name: 'dev',\n description: 'Start the API server and the configured storefront template with HMR',\n },\n args: {\n seed: {\n type: 'boolean',\n description: 'Run seed before starting (default: true if empty)',\n default: true,\n },\n apiOnly: {\n type: 'boolean',\n // `alias` is required: citty 0.1.6 registers the camelCase key `apiOnly`\n // with mri and applies `default: false`. Without this alias a kebab\n // `--api-only` flag lands on `args['api-only']`, but `args.apiOnly`\n // resolves to the `false` default first (`??` does not fall through on\n // `false`), so the flag is silently ignored. The alias makes mri map\n // `--api-only` straight to `apiOnly`. Both forms now work.\n alias: 'api-only',\n description: 'Start only the API server (skip template dev server)',\n default: false,\n },\n storefrontPort: {\n type: 'string',\n alias: 'storefront-port',\n description:\n 'Port for the storefront dev server (default: 4321 Astro / 3000 Next.js). ' +\n 'If busy, the next free port is used. Also settable via NYMBAL_STOREFRONT_PORT.',\n },\n },\n async run({ args }) {\n const { config, projectRoot } = await loadProject()\n const version = getCliVersion()\n\n // Auto-migrate on startup so a fresh clone works without running `nymbal migrate` first\n await migrateProject(config, projectRoot)\n\n const app = await createApp(config, { version })\n const { platform } = app\n\n if (args.seed) {\n const existing = await platform.documentStore.query('products', {\n partitionKey: { field: 'storeId', value: config.store.name },\n limit: 1,\n })\n if (existing.items.length === 0) {\n platform.logger.info({}, 'Seeding demo content (first run)')\n await runSeed({\n config,\n commandStore: platform.commandStore,\n documentStore: platform.documentStore,\n eventBus: platform.eventBus,\n logger: platform.logger,\n version,\n })\n }\n }\n\n const http = createHttpServer({\n config,\n logger: platform.logger,\n documentStore: platform.documentStore,\n version,\n })\n const adminRoot = resolveAdminRoot()\n if (adminRoot === null) {\n // eslint-disable-next-line no-console\n console.log(\n pc.yellow(\n '→ The admin is not built, so /admin/ answers 404: run `pnpm --filter @nymbalhq/admin build`.',\n ),\n )\n }\n await app.attachHttp(http.adapter, { adminRoot })\n await http.start()\n\n if (args.apiOnly) {\n const shutdown = async (signal: string) => {\n platform.logger.info({ signal }, 'Shutting down API')\n await http.stop().catch(() => {})\n await app.stop().catch(() => {})\n process.exit(0)\n }\n process.on('SIGINT', () => void shutdown('SIGINT'))\n process.on('SIGTERM', () => void shutdown('SIGTERM'))\n // Keep process alive — Playwright webServer will kill it when done\n return\n }\n\n // Resolve a free storefront port. The preferred port is configurable but may\n // be taken (a developer commonly has another project on 4321/3000), so we\n // probe upward for a free one instead of hard-failing.\n const flagPortRaw = args.storefrontPort\n const flagPort =\n typeof flagPortRaw === 'string' && flagPortRaw.trim() !== '' ? Number(flagPortRaw) : undefined\n const preferred = preferredStorefrontPort(config.template, flagPort, process.env)\n const storefrontPort = await findFreePort(preferred)\n if (storefrontPort !== preferred) {\n // eslint-disable-next-line no-console\n console.log(\n pc.yellow(\n `→ Storefront port ${preferred} is busy; using free port ${pc.bold(String(storefrontPort))} instead.`,\n ),\n )\n }\n\n // Plan how to launch the storefront. A flat scaffold (create-nymbal-app\n // output) runs the framework dev server directly in the project root; a\n // monorepo layout falls back to a pnpm --filter on the template package.\n const launch = planTemplateLaunch(projectRoot, config.template, storefrontPort)\n\n const apiUrl = `http://localhost:${config.http.port}`\n const storefrontUrl = `http://localhost:${storefrontPort}`\n // eslint-disable-next-line no-console\n console.log(\n pc.cyan(\n `→ Starting storefront (${pc.bold(config.template)}, ${launch.mode}) on ${pc.bold(storefrontUrl)}`,\n ),\n )\n // eslint-disable-next-line no-console\n console.log(pc.dim(` API: ${apiUrl}`))\n\n const templateProc = run(launch.command, launch.args, {\n cwd: launch.cwd,\n env: storefrontEnv(config, apiUrl, storefrontPort),\n })\n\n const shutdown = async (signal: string) => {\n platform.logger.info({ signal }, 'Shutting down')\n try {\n templateProc.child.kill('SIGINT')\n } catch {\n // ignore\n }\n await http.stop().catch(() => {})\n await app.stop().catch(() => {})\n process.exit(0)\n }\n process.on('SIGINT', () => void shutdown('SIGINT'))\n process.on('SIGTERM', () => void shutdown('SIGTERM'))\n\n const templateExit = await templateProc.done\n platform.logger.info({ code: templateExit }, 'Template process exited')\n await shutdown('template-exit')\n },\n})\n","import { existsSync } from 'node:fs'\nimport { dirname, resolve } from 'node:path'\nimport { findConfigFile, loadConfig, type LoadedConfig } from '@nymbalhq/config'\n\n/**\n * Loads `<projectRoot>/.env` into the process environment, then the project's config, so `env()` calls in\n * `nymbal.config.ts` see it. A variable already set in the environment is kept: `process.loadEnvFile` never overrides.\n */\nexport async function loadProject(startDir: string = process.cwd()): Promise<LoadedConfig> {\n const configPath = findConfigFile(startDir)\n if (configPath) {\n const envFile = resolve(dirname(configPath), '.env')\n if (existsSync(envFile)) process.loadEnvFile(envFile)\n }\n return loadConfig(startDir)\n}\n","import { spawn, type ChildProcess, type SpawnOptions } from 'node:child_process'\n\nexport interface RunOptions extends SpawnOptions {\n name?: string\n}\n\nexport function run(\n command: string,\n args: string[],\n options: RunOptions = {},\n): { child: ChildProcess; done: Promise<number> } {\n const child = spawn(command, args, {\n stdio: 'inherit',\n shell: false,\n ...options,\n })\n const done = new Promise<number>((resolve, reject) => {\n child.on('error', reject)\n child.on('exit', (code) => resolve(code ?? 0))\n })\n return { child, done }\n}\n\nexport async function runOrFail(\n command: string,\n args: string[],\n options: RunOptions = {},\n): Promise<void> {\n const { done } = run(command, args, options)\n const code = await done\n if (code !== 0) {\n throw new Error(`${command} ${args.join(' ')} exited with code ${code}`)\n }\n}\n","import { createServer } from 'node:net'\n\n/**\n * Resolve a usable storefront port.\n *\n * The headline `nymbal dev` flow must not hard-fail when the preferred port is\n * already taken (a developer commonly has another project on 4321/3000). We\n * probe the preferred port first and, if it is busy, increment until we find a\n * free one — exactly how the underlying dev servers (Astro, Next.js) behave on\n * their own, but here we resolve the port *before* spawning so we can tell the\n * storefront which port to bind and tell the user where to open the browser.\n */\nexport async function isPortFree(port: number, host?: string): Promise<boolean> {\n return new Promise<boolean>((resolve) => {\n const server = createServer()\n server.once('error', (err: NodeJS.ErrnoException) => {\n // EADDRINUSE / EACCES → not usable. Any other error → treat as not free\n // rather than silently swallowing it; the caller will skip this port.\n if (err.code !== 'EADDRINUSE' && err.code !== 'EACCES') {\n // eslint-disable-next-line no-console\n console.error(`[dev] unexpected error probing port ${port}:`, err.message)\n }\n resolve(false)\n })\n server.once('listening', () => {\n server.close(() => resolve(true))\n })\n // Bind across all interfaces (no explicit host) so a port already held on\n // IPv6 (e.g. another dev server on [::1]) is correctly seen as busy. A\n // 127.0.0.1-only probe would miss an IPv6 listener and wrongly report free,\n // then the framework — which binds dual-stack — would fail on that port.\n if (host) {\n server.listen(port, host)\n } else {\n server.listen(port)\n }\n })\n}\n\n/**\n * Find a free port starting at `preferred`, scanning upward up to `maxAttempts`\n * times. Throws an actionable error if no port in the range is free.\n */\nexport async function findFreePort(\n preferred: number,\n maxAttempts = 20,\n host?: string,\n): Promise<number> {\n if (!Number.isInteger(preferred) || preferred < 1 || preferred > 65535) {\n throw new Error(\n `Invalid storefront port \"${preferred}\". Set a valid port (1–65535) via --storefront-port or NYMBAL_STOREFRONT_PORT.`,\n )\n }\n for (let i = 0; i < maxAttempts; i++) {\n const candidate = preferred + i\n if (candidate > 65535) break\n if (await isPortFree(candidate, host)) {\n return candidate\n }\n }\n throw new Error(\n `No free storefront port found in range ${preferred}–${Math.min(preferred + maxAttempts - 1, 65535)}. ` +\n `Free a port in that range or pass an explicit free port via --storefront-port.`,\n )\n}\n","import { existsSync } from 'node:fs'\nimport { resolve } from 'node:path'\nimport type { NymbalConfig } from '@nymbalhq/config'\n\nexport type TemplateName = 'astro' | 'nextjs'\n\nexport interface TemplateLaunch {\n /** Working directory the storefront dev server runs in. */\n cwd: string\n /** Command + args to start the storefront dev server. */\n command: string\n args: string[]\n /**\n * How the storefront was located. `flat` = the project root IS the storefront\n * (create-nymbal-app output). `filter` = a sub-package in a monorepo layout.\n */\n mode: 'flat' | 'filter'\n}\n\nexport function templateFilterFor(template: NymbalConfig['template']): string {\n return template === 'astro' ? '@nymbalhq/template-astro' : '@nymbalhq/template-nextjs'\n}\n\n/**\n * A flat scaffold (create-nymbal-app output) places the storefront AT the\n * project root: the framework config and a `dev` script live in projectRoot.\n * In that layout we must run the dev server in projectRoot directly — NOT via\n * `pnpm --filter @nymbalhq/template-astro`, which would match the workspace\n * template package (the wrong directory) inside a monorepo, or match nothing at\n * all outside one.\n */\nexport function isFlatScaffold(projectRoot: string, template: TemplateName): boolean {\n const marker = template === 'astro' ? 'astro.config.mjs' : 'next.config.mjs'\n const altMarker = template === 'astro' ? 'astro.config.ts' : 'next.config.ts'\n return existsSync(resolve(projectRoot, marker)) || existsSync(resolve(projectRoot, altMarker))\n}\n\n/**\n * Resolve the framework's local CLI bin inside the project's node_modules.\n * We spawn the local bin directly (rather than the package-manager `run dev`\n * script) so we can inject the resolved free port via `--port`, overriding the\n * hardcoded port baked into the scaffold's `dev` script. Returns null when the\n * bin is not present (deps not installed) so the caller can produce an\n * actionable error.\n */\nexport function resolveLocalBin(projectRoot: string, bin: string): string | null {\n const binPath = resolve(projectRoot, 'node_modules', '.bin', bin)\n return existsSync(binPath) ? binPath : null\n}\n\n/**\n * Build the storefront dev launch plan.\n *\n * @param storefrontPort - the resolved (free) port the storefront must bind to.\n * Passed to the framework via flag so we control where it listens, instead of\n * relying on the framework's hardcoded default which may already be taken.\n */\nexport function planTemplateLaunch(\n projectRoot: string,\n template: TemplateName,\n storefrontPort: number,\n): TemplateLaunch {\n if (isFlatScaffold(projectRoot, template)) {\n // Run the framework dev server directly in the project root. We pass the\n // port explicitly so the framework binds where we resolved a free port,\n // overriding any hardcoded port in the scaffold's package.json script.\n const binName = template === 'astro' ? 'astro' : 'next'\n const localBin = resolveLocalBin(projectRoot, binName)\n if (!localBin) {\n throw new Error(\n `Cannot find the ${binName} dev server in ${projectRoot}/node_modules/.bin. ` +\n `Install dependencies first (e.g. \\`pnpm install\\`), then re-run \\`nymbal dev\\`.`,\n )\n }\n return {\n cwd: projectRoot,\n command: localBin,\n args: ['dev', '--port', String(storefrontPort), ...foregroundArgs(template)],\n mode: 'flat',\n }\n }\n\n // Monorepo layout fallback: the storefront lives in a workspace sub-package.\n const filter = templateFilterFor(template)\n return {\n cwd: projectRoot,\n command: 'pnpm',\n args: ['--filter', filter, 'dev', ...foregroundArgs(template)],\n mode: 'filter',\n }\n}\n\n/**\n * Astro 7 backgrounds `astro dev` when it detects an AI agent, so the launcher would see its child exit while the\n * server lives on detached; --ignore-lock keeps it in the foreground. `next dev` rejects unknown options.\n */\nfunction foregroundArgs(template: TemplateName): string[] {\n return template === 'astro' ? ['--ignore-lock'] : []\n}\n","import { defineCommand } from 'citty'\nimport type { NymbalConfig } from '@nymbalhq/config'\nimport { loadProject } from '../utils/project.js'\nimport { createCommandStore, runMigrations } from '@nymbalhq/platform'\nimport { dirname, resolve } from 'node:path'\nimport { existsSync } from 'node:fs'\n\nexport const migrateCommand = defineCommand({\n meta: { name: 'migrate', description: 'Run database migrations (Drizzle)' },\n async run() {\n const { config, projectRoot } = await loadProject()\n const kind = await migrateProject(config, projectRoot)\n // eslint-disable-next-line no-console\n console.log(`✓ migrations applied (${kind})`)\n },\n})\n\n/** Applies the project's migrations to its command store; the store is closed again before returning. */\nexport async function migrateProject(config: NymbalConfig, projectRoot: string): Promise<string> {\n const store = createCommandStore(config)\n try {\n await runMigrations(store, { migrationsRoot: resolveMigrations(projectRoot) })\n return store.kind\n } finally {\n await store.close()\n }\n}\n\nexport function resolveMigrations(projectRoot: string): string {\n // 1. Project-local override (scaffolded projects or custom migrations)\n const local = resolve(projectRoot, 'migrations')\n if (existsSync(local)) return local\n\n // 2. Monorepo workspace: packages/platform/migrations (for CI and fresh clones)\n const monorepoDev = resolve(projectRoot, 'packages/platform/migrations')\n if (existsSync(monorepoDev)) return monorepoDev\n\n // 3. Published package: node_modules/@nymbalhq/platform/migrations\n //\n // We must NOT use `require.resolve('@nymbalhq/platform/package.json')` nor\n // `require.resolve('@nymbalhq/platform')`: the package's `exports` map is\n // ESM-only and does not expose `./package.json`, so CJS resolution throws\n // (ERR_PACKAGE_PATH_NOT_EXPORTED / \"No exports main defined\"). Instead we\n // locate the installed package directory directly under node_modules, which\n // is independent of the exports map and works for symlinked (pnpm workspace)\n // and flat installs alike.\n const pkgDir = resolvePackageDir(projectRoot, '@nymbalhq/platform')\n if (pkgDir) {\n const pkgMigrations = resolve(pkgDir, 'migrations')\n if (existsSync(pkgMigrations)) return pkgMigrations\n }\n\n return local\n}\n\n/**\n * Resolve the on-disk root directory of an installed package from `fromRoot`,\n * by walking up the directory tree looking for `node_modules/<pkgName>`. This\n * does not depend on the package's `exports` map (which may be ESM-only and not\n * expose `./package.json`), and resolves correctly for pnpm symlinked workspace\n * installs as well as flat node_modules. Returns null when not found.\n */\nexport function resolvePackageDir(fromRoot: string, pkgName: string): string | null {\n let dir = resolve(fromRoot)\n for (let i = 0; i < 24; i++) {\n const candidate = resolve(dir, 'node_modules', pkgName)\n if (existsSync(resolve(candidate, 'package.json'))) {\n return candidate\n }\n const parent = dirname(dir)\n if (parent === dir) break\n dir = parent\n }\n return null\n}\n","import { existsSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { dirname, join } from 'node:path'\n\nfunction hasIndex(dir: string): boolean {\n return existsSync(join(dir, 'index.html'))\n}\n\n/**\n * The admin dist to serve: `NYMBAL_ADMIN_DIST` when it holds `index.html` (read per call), else the installed\n * `@nymbalhq/admin` dist when built, else null.\n */\nexport function resolveAdminRoot(): string | null {\n const override = process.env['NYMBAL_ADMIN_DIST']\n if (override && hasIndex(override)) return override\n try {\n const manifest = createRequire(import.meta.url).resolve('@nymbalhq/admin/package.json')\n const dist = join(dirname(manifest), 'dist')\n return hasIndex(dist) ? dist : null\n } catch {\n return null\n }\n}\n","import { defineCommand } from 'citty'\nimport { loadProject } from '../utils/project.js'\nimport { runOrFail } from '../utils/spawn.js'\nimport { isFlatScaffold, resolveLocalBin, templateFilterFor } from '../utils/template-runner.js'\n\nexport const buildCommand = defineCommand({\n meta: { name: 'build', description: 'Production build of the configured template' },\n async run() {\n const { config, projectRoot } = await loadProject()\n if (!isFlatScaffold(projectRoot, config.template)) {\n // Inside this monorepo the storefront is a workspace package.\n await runOrFail('pnpm', ['--filter', templateFilterFor(config.template), 'build'], {\n cwd: projectRoot,\n })\n return\n }\n const binName = config.template === 'astro' ? 'astro' : 'next'\n const bin = resolveLocalBin(projectRoot, binName)\n if (!bin) {\n throw new Error(\n `Cannot find the ${binName} binary in ${projectRoot}/node_modules/.bin. ` +\n 'Install dependencies first, then re-run `nymbal build`.',\n )\n }\n await runOrFail(bin, ['build'], { cwd: projectRoot })\n },\n})\n","import { defineCommand } from 'citty'\nimport { loadProject } from '../utils/project.js'\nimport { createPlatform, runSeed } from '@nymbalhq/platform'\nimport { getCliVersion } from '../utils/version.js'\nimport { migrateProject } from './migrate.js'\n\nexport const seedCommand = defineCommand({\n meta: { name: 'seed', description: 'Re-seed demo content (products, categories)' },\n async run() {\n const { config, projectRoot } = await loadProject()\n // A fresh project has no tables yet; `nymbal dev` migrates before it seeds, and so does this.\n await migrateProject(config, projectRoot)\n const platform = createPlatform(config)\n try {\n const result = await runSeed({\n config,\n commandStore: platform.commandStore,\n documentStore: platform.documentStore,\n eventBus: platform.eventBus,\n logger: platform.logger,\n version: getCliVersion(),\n })\n // eslint-disable-next-line no-console\n console.log(\n result.alreadySeeded\n ? '✓ demo content already present, nothing to seed'\n : `✓ seeded ${result.products} products across ${result.categories} categories`,\n )\n } finally {\n await platform.close()\n }\n },\n})\n","import { defineCommand } from 'citty'\nimport { intro, outro, text, select, spinner, isCancel, cancel } from '@clack/prompts'\nimport * as p from 'picocolors'\nimport { loadProject } from '../utils/project.js'\nimport { createApp } from '@nymbalhq/platform'\nimport { runWooCommerceImport, writeReport, type ImportEntity, type ProgressEvent } from '@nymbalhq/importers'\nimport { join } from 'node:path'\nimport { getCliVersion } from '../utils/version.js'\n\nexport const importCommand = defineCommand({\n meta: {\n name: 'import',\n description: 'Migrate content from WooCommerce (or other sources) into Nymbal',\n },\n args: {\n source: {\n type: 'string',\n description: 'Import source (woocommerce)',\n },\n url: {\n type: 'string',\n description: 'Store URL (e.g. https://mystore.com)',\n },\n key: {\n type: 'string',\n description: 'WooCommerce Consumer Key (or set WOOCOMMERCE_KEY env var)',\n },\n secret: {\n type: 'string',\n description: 'WooCommerce Consumer Secret (or set WOOCOMMERCE_SECRET env var)',\n },\n entities: {\n type: 'string',\n description: 'Comma-separated list: products,customers,orders,reviews (default: products,customers,orders)',\n },\n ai: {\n type: 'boolean',\n description: 'Enable AI enrichment (default: true)',\n default: true,\n },\n gdpr: {\n type: 'boolean',\n description: 'Anonymise customer PII (default: true)',\n default: true,\n },\n fresh: {\n type: 'boolean',\n description: 'Ignore previous progress and re-import',\n default: false,\n },\n concurrency: {\n type: 'string',\n description: 'Parallel import workers (default: 4)',\n },\n },\n async run({ args }) {\n const { config } = await loadProject()\n const projectRoot = process.cwd()\n const statePath = join(projectRoot, '.nymbal-migration.json')\n const isTTY = process.stdin.isTTY ?? false\n\n intro(p.bold('Nymbal Import'))\n\n // ── Resolve source ────────────────────────────────────────────────────────\n let source = args.source\n if (!source) {\n if (!isTTY) {\n console.error('--source is required (woocommerce)')\n process.exit(1)\n }\n const picked = await select({\n message: 'Source platform?',\n options: [{ value: 'woocommerce', label: 'WooCommerce' }],\n })\n if (isCancel(picked)) { cancel('Import cancelled'); process.exit(0) }\n source = String(picked)\n }\n if (source !== 'woocommerce') {\n console.error(`Unsupported source: ${source}. Only 'woocommerce' is supported.`)\n process.exit(1)\n }\n\n // ── Resolve credentials ───────────────────────────────────────────────────\n let storeUrl = args.url\n if (!storeUrl) {\n if (!isTTY) { console.error('--url is required'); process.exit(1) }\n const val = await text({ message: 'Store URL?', placeholder: 'https://mystore.com', validate: (v) => v.startsWith('http') ? undefined : 'Must be a URL' })\n if (isCancel(val)) { cancel('Import cancelled'); process.exit(0) }\n storeUrl = String(val)\n }\n\n let consumerKey = args.key ?? process.env['WOOCOMMERCE_KEY'] ?? ''\n if (!consumerKey) {\n if (!isTTY) { console.error('--key or WOOCOMMERCE_KEY is required'); process.exit(1) }\n const val = await text({ message: 'Consumer Key?', placeholder: 'ck_...' })\n if (isCancel(val)) { cancel('Import cancelled'); process.exit(0) }\n consumerKey = String(val)\n }\n\n let consumerSecret = args.secret ?? process.env['WOOCOMMERCE_SECRET'] ?? ''\n if (!consumerSecret) {\n if (!isTTY) { console.error('--secret or WOOCOMMERCE_SECRET is required'); process.exit(1) }\n const val = await text({ message: 'Consumer Secret?', placeholder: 'cs_...' })\n if (isCancel(val)) { cancel('Import cancelled'); process.exit(0) }\n consumerSecret = String(val)\n }\n\n // ── Resolve entity list ───────────────────────────────────────────────────\n let entityList: ImportEntity[]\n if (args.entities) {\n entityList = args.entities.split(',').map((e) => e.trim()) as ImportEntity[]\n } else {\n entityList = ['categories', 'products', 'customers', 'orders']\n }\n\n const concurrency = parseInt(String(args.concurrency ?? '4'), 10)\n const aiEnabled = args.ai !== false\n const gdprEnabled = args.gdpr !== false\n\n // ── Bootstrap app ─────────────────────────────────────────────────────────\n const loadSpin = spinner()\n loadSpin.start('Starting platform…')\n const app = await createApp(config, { version: getCliVersion() })\n loadSpin.stop('Platform ready')\n\n try {\n const report = await runWooCommerceImport(\n {\n credentials: { url: storeUrl, consumerKey, consumerSecret },\n entities: entityList,\n ai: aiEnabled,\n gdpr: gdprEnabled,\n fresh: args.fresh,\n concurrency,\n projectRoot,\n currency: config.store.currency,\n statePath,\n },\n {\n services: app.services,\n repos: app.repos,\n adapters: { ai: app.adapters.ai, reviews: app.adapters.reviews },\n onProgress(event: ProgressEvent) {\n if (event.kind === 'phase') {\n console.log(p.cyan(` → ${event.phase}`))\n } else if (event.kind === 'progress') {\n const pct = Math.round((event.done / event.total) * 100)\n process.stdout.write(`\\r ${event.entity}: ${event.done}/${event.total} (${pct}%) `)\n if (event.done === event.total) process.stdout.write('\\n')\n } else if (event.kind === 'error') {\n console.log(p.red(` ✗ ${event.phase} [${event.id}]: ${event.message}`))\n }\n },\n },\n )\n\n const reportPath = await writeReport(report, projectRoot, config.store.name)\n\n outro(\n [\n p.green('✓ Import complete'),\n ` Products: ${report.imported.products}`,\n ` Customers: ${report.imported.customers}`,\n ` Orders: ${report.imported.orders}`,\n ` Categories: ${report.imported.categories}`,\n ...(aiEnabled ? [` AI enriched: ${report.enriched.enriched} products`] : []),\n ...(report.errors.length > 0 ? [p.yellow(` ⚠ ${report.errors.length} errors — see report`)] : []),\n ``,\n ` Report: ${reportPath}`,\n ` Next: ${p.bold('nymbal dev')}`,\n ].join('\\n'),\n )\n } finally {\n await app.stop()\n }\n },\n})\n","import { defineCommand } from 'citty'\nimport pc from 'picocolors'\nimport { loadProject } from '../utils/project.js'\nimport { NymbalError } from '@nymbalhq/types'\n\nexport const configValidateCommand = defineCommand({\n meta: { name: 'config:validate', description: 'Validate nymbal.config.ts' },\n async run() {\n try {\n const { config, path } = await loadProject()\n // eslint-disable-next-line no-console\n console.log(pc.green(`✓ config valid`) + ` (${path})`)\n // eslint-disable-next-line no-console\n console.log(\n ` template: ${config.template}, cloud: ${config.infrastructure.cloud}, commandStore: ${config.infrastructure.commandStore}`,\n )\n process.exit(0)\n } catch (err) {\n if (err instanceof NymbalError) {\n // eslint-disable-next-line no-console\n console.error(pc.red('✗ config invalid'))\n // eslint-disable-next-line no-console\n console.error(err.message)\n } else {\n // eslint-disable-next-line no-console\n console.error(err)\n }\n process.exit(1)\n }\n },\n})\n","import { defineCommand } from 'citty'\nimport { loadConfig } from '@nymbalhq/config'\nimport { run, runOrFail } from '../utils/spawn.js'\n\nexport const testCommand = defineCommand({\n meta: { name: 'test', description: 'Run unit + integration tests (vitest)' },\n async run() {\n await runOrFail('pnpm', ['vitest', 'run'])\n },\n})\n\nexport const testE2eCommand = defineCommand({\n meta: { name: 'test:e2e', description: 'Run Playwright E2E against the configured template' },\n async run() {\n const { config } = await loadConfig()\n await runOrFail('pnpm', [\n '--filter', '@nymbalhq/e2e', 'exec',\n 'playwright', 'test', '--project', config.template, '--grep-invert', '@visual|@a11y',\n ])\n },\n})\n\nexport const testVisualCommand = defineCommand({\n meta: { name: 'test:visual', description: 'Run visual regression tests (Playwright toHaveScreenshot)' },\n args: {\n 'update-baselines': {\n type: 'boolean',\n description: 'Regenerate screenshot baselines',\n default: false,\n },\n },\n async run({ args }) {\n const { config } = await loadConfig()\n const isCI = !!process.env.CI\n const updateBaselines = args['update-baselines']\n\n if (updateBaselines && !isCI) {\n // Run in Docker for Linux-consistent baselines\n const dockerImage = 'mcr.microsoft.com/playwright:v1.50-jammy'\n const cwd = process.cwd()\n await runOrFail('docker', [\n 'run', '--rm', '-v', `${cwd}:${cwd}`, '-w', cwd,\n dockerImage, 'sh', '-c',\n `pnpm --filter @nymbalhq/e2e exec playwright test --project ${config.template} --grep \"@visual\" --update-snapshots`,\n ])\n } else {\n const pwArgs = [\n '--filter', '@nymbalhq/e2e', 'exec',\n 'playwright', 'test', '--project', config.template, '--grep', '@visual',\n ]\n if (updateBaselines) pwArgs.push('--update-snapshots')\n await runOrFail('pnpm', pwArgs)\n }\n },\n})\n\nexport const testContractsCommand = defineCommand({\n meta: { name: 'test:contracts', description: 'Run adapter contract tests' },\n args: {\n adapter: { type: 'string', description: 'Run only contracts for a specific adapter (e.g. payments, email)' },\n },\n async run({ args }) {\n const vArgs = [\n 'vitest', 'run',\n '--project', 'contracts-native',\n ]\n if (args.adapter) {\n // Pass the specific file path as additional argument\n vArgs.push(`tests/contracts/${String(args.adapter)}.test.ts`)\n }\n await runOrFail('pnpm', vArgs)\n },\n})\n\nexport const testPerformanceCommand = defineCommand({\n meta: { name: 'test:performance', description: 'Run Lighthouse CI against the configured template' },\n async run() {\n const { config } = await loadConfig()\n const port = config.template === 'nextjs' ? 3000 : 4321\n await runOrFail('pnpm', ['lhci', 'autorun'], {\n env: {\n ...process.env,\n NYMBAL_TEMPLATE: config.template,\n NYMBAL_TEMPLATE_PORT: String(port),\n },\n })\n },\n})\n\nexport const testAllCommand = defineCommand({\n meta: { name: 'test:all', description: 'Run unit, contract, e2e, visual, and performance tests' },\n async run() {\n const steps: Array<{ name: string; args: string[] }> = [\n { name: 'unit', args: ['vitest', 'run'] },\n { name: 'contracts', args: ['vitest', 'run', '--project', 'contracts-native'] },\n ]\n\n for (const step of steps) {\n const { done } = run('pnpm', step.args)\n const code = await done\n if (code !== 0) {\n // eslint-disable-next-line no-console\n console.error(`✗ ${step.name} failed`)\n process.exit(code)\n }\n }\n\n const { config } = await loadConfig()\n await runOrFail('pnpm', [\n '--filter', '@nymbalhq/e2e', 'exec',\n 'playwright', 'test', '--project', config.template, '--grep-invert', '@visual|@a11y',\n ])\n await runOrFail('pnpm', [\n '--filter', '@nymbalhq/e2e', 'exec',\n 'playwright', 'test', '--project', config.template, '--grep', '@visual',\n ])\n await runOrFail('pnpm', ['lhci', 'autorun'], {\n env: { ...process.env, NYMBAL_TEMPLATE: config.template },\n })\n },\n})\n"],"mappings":"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","names":["defineCommand","defineCommand","dirname","resolve","resolve","existsSync","resolve","dirname","resolve","existsSync","existsSync","dirname","join","defineCommand","shutdown","defineCommand","defineCommand","defineCommand","runSeed","defineCommand","runSeed","defineCommand","createApp","join","defineCommand","join","createApp","defineCommand","pc","defineCommand","pc","defineCommand","loadConfig","defineCommand","loadConfig","defineCommand"]}
|
package/package.json
CHANGED
|
@@ -1,10 +1,53 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nymbal",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "nymbal — developer CLI for the Nymbal platform",
|
|
5
5
|
"license": "MIT",
|
|
6
|
-
"repository": {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/nymbalhq/nymbal.git",
|
|
9
|
+
"directory": "packages/cli"
|
|
10
|
+
},
|
|
11
|
+
"publishConfig": {
|
|
12
|
+
"access": "public"
|
|
13
|
+
},
|
|
14
|
+
"type": "module",
|
|
15
|
+
"engines": {
|
|
16
|
+
"node": ">=22"
|
|
17
|
+
},
|
|
18
|
+
"bin": {
|
|
19
|
+
"nymbal": "./bin/nymbal.mjs"
|
|
20
|
+
},
|
|
21
|
+
"main": "./dist/index.js",
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"exports": {
|
|
24
|
+
".": {
|
|
25
|
+
"types": "./dist/index.d.ts",
|
|
26
|
+
"import": "./dist/index.js"
|
|
27
|
+
}
|
|
28
|
+
},
|
|
29
|
+
"files": [
|
|
30
|
+
"bin",
|
|
31
|
+
"dist"
|
|
32
|
+
],
|
|
33
|
+
"dependencies": {
|
|
34
|
+
"@clack/prompts": "^0.7.0",
|
|
35
|
+
"citty": "^0.1.6",
|
|
36
|
+
"picocolors": "^1.1.1",
|
|
37
|
+
"@nymbalhq/admin": "0.2.0",
|
|
38
|
+
"@nymbalhq/config": "0.2.0",
|
|
39
|
+
"@nymbalhq/http": "0.2.0",
|
|
40
|
+
"@nymbalhq/importers": "0.2.0",
|
|
41
|
+
"@nymbalhq/platform": "0.2.0",
|
|
42
|
+
"@nymbalhq/types": "0.2.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"tsup": "^8.3.5",
|
|
46
|
+
"typescript": "^5.6.3"
|
|
47
|
+
},
|
|
48
|
+
"scripts": {
|
|
49
|
+
"build": "tsup",
|
|
50
|
+
"dev": "tsup --watch",
|
|
51
|
+
"typecheck": "tsc -p tsconfig.json --noEmit"
|
|
52
|
+
}
|
|
53
|
+
}
|
package/README.md
DELETED