@geekmidas/cli 1.10.29 → 1.10.31
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/CHANGELOG.md +18 -0
- package/dist/{CachedStateProvider-37pG1HBx.cjs → CachedStateProvider-3couMAQk.cjs} +1 -1
- package/dist/CachedStateProvider-C4QqvlPE.mjs +3 -0
- package/dist/{CachedStateProvider-C8A-cCm5.mjs → CachedStateProvider-CQr31ups.mjs} +1 -1
- package/dist/{CachedStateProvider-C8A-cCm5.mjs.map → CachedStateProvider-CQr31ups.mjs.map} +1 -1
- package/dist/{CachedStateProvider-CxsXOKkg.cjs → CachedStateProvider-Cyn17x3x.cjs} +1 -1
- package/dist/{CachedStateProvider-CxsXOKkg.cjs.map → CachedStateProvider-Cyn17x3x.cjs.map} +1 -1
- package/dist/{HostingerProvider-5KYmwoK2.cjs → HostingerProvider-C5oLa7N7.cjs} +1 -1
- package/dist/{HostingerProvider-5KYmwoK2.cjs.map → HostingerProvider-C5oLa7N7.cjs.map} +1 -1
- package/dist/{HostingerProvider-ANWchdiK.mjs → HostingerProvider-YvCg3Mus.mjs} +1 -1
- package/dist/{HostingerProvider-ANWchdiK.mjs.map → HostingerProvider-YvCg3Mus.mjs.map} +1 -1
- package/dist/{LocalStateProvider-Dp0KkRcw.mjs → LocalStateProvider-BciOhGR0.mjs} +1 -1
- package/dist/{LocalStateProvider-Dp0KkRcw.mjs.map → LocalStateProvider-BciOhGR0.mjs.map} +1 -1
- package/dist/{LocalStateProvider-CLifRC0Y.cjs → LocalStateProvider-DI6n11UM.cjs} +1 -1
- package/dist/{LocalStateProvider-CLifRC0Y.cjs.map → LocalStateProvider-DI6n11UM.cjs.map} +1 -1
- package/dist/{Route53Provider-QoPgcXxn.mjs → Route53Provider-CYlDOrYO.mjs} +1 -1
- package/dist/{Route53Provider-QoPgcXxn.mjs.map → Route53Provider-CYlDOrYO.mjs.map} +1 -1
- package/dist/{Route53Provider-owQQ4pn6.cjs → Route53Provider-DISZ6E8f.cjs} +1 -1
- package/dist/{Route53Provider-owQQ4pn6.cjs.map → Route53Provider-DISZ6E8f.cjs.map} +1 -1
- package/dist/{SSMStateProvider-CT8tjl9o.cjs → SSMStateProvider-BljH5efc.cjs} +1 -1
- package/dist/{SSMStateProvider-CT8tjl9o.cjs.map → SSMStateProvider-BljH5efc.cjs.map} +1 -1
- package/dist/{SSMStateProvider-CksOTB8M.mjs → SSMStateProvider-DXT1dIdI.mjs} +1 -1
- package/dist/{SSMStateProvider-CksOTB8M.mjs.map → SSMStateProvider-DXT1dIdI.mjs.map} +1 -1
- package/dist/{backup-provisioner-BEXoHTuC.mjs → backup-provisioner-CCUcj28C.mjs} +1 -1
- package/dist/{backup-provisioner-BEXoHTuC.mjs.map → backup-provisioner-CCUcj28C.mjs.map} +1 -1
- package/dist/{backup-provisioner-C4noe75O.cjs → backup-provisioner-CpirEvO1.cjs} +1 -1
- package/dist/{backup-provisioner-C4noe75O.cjs.map → backup-provisioner-CpirEvO1.cjs.map} +1 -1
- package/dist/{bundler-BG6P249p.cjs → bundler-CLOenWgi.cjs} +2 -2
- package/dist/{bundler-BG6P249p.cjs.map → bundler-CLOenWgi.cjs.map} +1 -1
- package/dist/{bundler-c6QvGUBM.mjs → bundler-E5VcXsPH.mjs} +2 -2
- package/dist/{bundler-c6QvGUBM.mjs.map → bundler-E5VcXsPH.mjs.map} +1 -1
- package/dist/client-generator-CUVVtAOo.mjs +23 -0
- package/dist/client-generator-CUVVtAOo.mjs.map +1 -0
- package/dist/client-generator-D-T4ypQo.cjs +35 -0
- package/dist/client-generator-D-T4ypQo.cjs.map +1 -0
- package/dist/{config-jsRYHOHU.mjs → config-B62g483e.mjs} +2 -2
- package/dist/{config-jsRYHOHU.mjs.map → config-B62g483e.mjs.map} +1 -1
- package/dist/{config-D3ORuiUs.cjs → config-Cuo8vFsp.cjs} +2 -2
- package/dist/{config-D3ORuiUs.cjs.map → config-Cuo8vFsp.cjs.map} +1 -1
- package/dist/config.cjs +2 -2
- package/dist/config.d.cts +1 -1
- package/dist/config.d.mts +2 -2
- package/dist/config.mjs +2 -2
- package/dist/deploy/sniffer-worker.cjs +2 -0
- package/dist/deploy/sniffer-worker.cjs.map +1 -1
- package/dist/deploy/sniffer-worker.mjs +2 -0
- package/dist/deploy/sniffer-worker.mjs.map +1 -1
- package/dist/dokploy-api-BPYkG-ZZ.cjs +3 -0
- package/dist/dokploy-api-BiSzWM4v.mjs +3 -0
- package/dist/{dokploy-api-Dvyq-LsM.cjs → dokploy-api-CqOIKHme.cjs} +1 -1
- package/dist/{dokploy-api-Dvyq-LsM.cjs.map → dokploy-api-CqOIKHme.cjs.map} +1 -1
- package/dist/{dokploy-api-BIT0683o.mjs → dokploy-api-sfZFlWuE.mjs} +1 -1
- package/dist/{dokploy-api-BIT0683o.mjs.map → dokploy-api-sfZFlWuE.mjs.map} +1 -1
- package/dist/{encryption-CipsS0KW.cjs → encryption-BVS7Vmzs.cjs} +1 -1
- package/dist/encryption-Bd_m4q3g.mjs +3 -0
- package/dist/{encryption-DSkIlEZ6.mjs → encryption-CO5Q4Bhf.mjs} +1 -1
- package/dist/{encryption-DSkIlEZ6.mjs.map → encryption-CO5Q4Bhf.mjs.map} +1 -1
- package/dist/{encryption-DgKS-Dv9.cjs → encryption-MfXAiFTm.cjs} +1 -1
- package/dist/{encryption-DgKS-Dv9.cjs.map → encryption-MfXAiFTm.cjs.map} +1 -1
- package/dist/{index-CY-ieuRp.d.mts → index-C-_rW4KF.d.mts} +3 -33
- package/dist/index-C-_rW4KF.d.mts.map +1 -0
- package/dist/{index-BkibYzso.d.cts → index-C3t5VL4R.d.cts} +2 -32
- package/dist/index-C3t5VL4R.d.cts.map +1 -0
- package/dist/index.cjs +102 -380
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +78 -356
- package/dist/index.mjs.map +1 -1
- package/dist/{openapi-CQCoLuzh.mjs → openapi-DHq5r17U.mjs} +7 -42
- package/dist/openapi-DHq5r17U.mjs.map +1 -0
- package/dist/{openapi-react-query-C4UdILaI.mjs → openapi-react-query-COpH7Mmz.mjs} +1 -1
- package/dist/{openapi-react-query-C4UdILaI.mjs.map → openapi-react-query-COpH7Mmz.mjs.map} +1 -1
- package/dist/{openapi-react-query-DYbBq-WJ.cjs → openapi-react-query-CxyQTTdF.cjs} +1 -1
- package/dist/{openapi-react-query-DYbBq-WJ.cjs.map → openapi-react-query-CxyQTTdF.cjs.map} +1 -1
- package/dist/openapi-react-query.cjs +1 -1
- package/dist/openapi-react-query.mjs +1 -1
- package/dist/{openapi-CBU4TEi-.cjs → openapi-w-_smBTO.cjs} +7 -54
- package/dist/openapi-w-_smBTO.cjs.map +1 -0
- package/dist/openapi.cjs +4 -3
- package/dist/openapi.d.mts +1 -1
- package/dist/openapi.mjs +4 -3
- package/dist/{reconcile-CZtzdrnc.cjs → reconcile-C8KIy4Uy.cjs} +1 -1
- package/dist/{reconcile-CZtzdrnc.cjs.map → reconcile-C8KIy4Uy.cjs.map} +1 -1
- package/dist/{reconcile-DAAKa0YR.mjs → reconcile-D423q2QW.mjs} +1 -1
- package/dist/{reconcile-DAAKa0YR.mjs.map → reconcile-D423q2QW.mjs.map} +1 -1
- package/dist/sniffer-BqSVVrWA.mjs +356 -0
- package/dist/sniffer-BqSVVrWA.mjs.map +1 -0
- package/dist/sniffer-CBhzByiY.cjs +386 -0
- package/dist/sniffer-CBhzByiY.cjs.map +1 -0
- package/dist/sniffer-Cpq_cbyz.mjs +4 -0
- package/dist/sniffer-eZ13G10M.cjs +4 -0
- package/dist/sync-B2CN_KS5.cjs +4 -0
- package/dist/{sync-DGXXSk2v.cjs → sync-BWY6yoUG.cjs} +1 -1
- package/dist/{sync-DGXXSk2v.cjs.map → sync-BWY6yoUG.cjs.map} +1 -1
- package/dist/{sync-D_NowTkZ.mjs → sync-Dc-QA4Mx.mjs} +1 -1
- package/dist/{sync-D_NowTkZ.mjs.map → sync-Dc-QA4Mx.mjs.map} +1 -1
- package/dist/{sync-COnAugP-.mjs → sync-fzT5WX_y.mjs} +1 -1
- package/dist/{types-OszPdw9m.d.mts → types-BqAbYt5a.d.mts} +1 -1
- package/dist/{types-OszPdw9m.d.mts.map → types-BqAbYt5a.d.mts.map} +1 -1
- package/dist/workspace/index.cjs +1 -1
- package/dist/workspace/index.d.cts +2 -2
- package/dist/workspace/index.d.mts +3 -3
- package/dist/workspace/index.mjs +1 -1
- package/dist/{workspace-D4z4A4cq.mjs → workspace-Bi4X7Yzy.mjs} +1 -6
- package/dist/{workspace-D4z4A4cq.mjs.map → workspace-Bi4X7Yzy.mjs.map} +1 -1
- package/dist/{workspace-4SP3Gx4Y.cjs → workspace-CGYykWfn.cjs} +1 -6
- package/dist/{workspace-4SP3Gx4Y.cjs.map → workspace-CGYykWfn.cjs.map} +1 -1
- package/package.json +3 -3
- package/src/__tests__/openapi.spec.ts +0 -315
- package/src/build/__tests__/manifests.spec.ts +66 -0
- package/src/build/index.ts +54 -1
- package/src/deploy/__tests__/__fixtures__/env-parsers/optional-env-parser.ts +21 -0
- package/src/deploy/__tests__/sniffer.spec.ts +123 -0
- package/src/deploy/sniffer-worker.ts +2 -1
- package/src/deploy/sniffer.ts +86 -12
- package/src/docker/compose.ts +2 -0
- package/src/generators/EndpointGenerator.ts +1 -1
- package/src/index.ts +8 -0
- package/src/init/generators/monorepo.ts +15 -1
- package/src/init/generators/package.ts +2 -0
- package/src/init/index.ts +2 -4
- package/src/init/templates/index.ts +5 -0
- package/src/init/versions.ts +1 -1
- package/src/openapi.ts +1 -35
- package/src/types.ts +7 -0
- package/src/workspace/__tests__/client-generator.spec.ts +1 -485
- package/src/workspace/client-generator.ts +1 -145
- package/src/workspace/index.ts +0 -1
- package/src/workspace/schema.ts +0 -9
- package/src/workspace/types.ts +0 -29
- package/dist/CachedStateProvider-CC0DyO09.mjs +0 -3
- package/dist/dokploy-api-BOx71j6F.cjs +0 -3
- package/dist/dokploy-api-BZGk-lkD.mjs +0 -3
- package/dist/encryption-Db2zvGqM.mjs +0 -3
- package/dist/index-BkibYzso.d.cts.map +0 -1
- package/dist/index-CY-ieuRp.d.mts.map +0 -1
- package/dist/openapi-CBU4TEi-.cjs.map +0 -1
- package/dist/openapi-CQCoLuzh.mjs.map +0 -1
- package/dist/sync-D1Pa30oV.cjs +0 -4
|
@@ -1,16 +1,5 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
1
2
|
import {
|
|
2
|
-
existsSync,
|
|
3
|
-
mkdirSync,
|
|
4
|
-
readFileSync,
|
|
5
|
-
rmSync,
|
|
6
|
-
writeFileSync,
|
|
7
|
-
} from 'node:fs';
|
|
8
|
-
import { tmpdir } from 'node:os';
|
|
9
|
-
import { join } from 'node:path';
|
|
10
|
-
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
11
|
-
import {
|
|
12
|
-
copyAllClients,
|
|
13
|
-
copyClientToFrontends,
|
|
14
3
|
getBackendDependencies,
|
|
15
4
|
getBackendOpenApiPath,
|
|
16
5
|
getDependentFrontends,
|
|
@@ -334,477 +323,4 @@ describe('Client Generator', () => {
|
|
|
334
323
|
expect(path).toBeNull();
|
|
335
324
|
});
|
|
336
325
|
});
|
|
337
|
-
|
|
338
|
-
describe('copyClientToFrontends', () => {
|
|
339
|
-
let testDir: string;
|
|
340
|
-
|
|
341
|
-
beforeEach(() => {
|
|
342
|
-
testDir = join(
|
|
343
|
-
tmpdir(),
|
|
344
|
-
`gkm-client-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
345
|
-
);
|
|
346
|
-
mkdirSync(testDir, { recursive: true });
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
afterEach(() => {
|
|
350
|
-
if (existsSync(testDir)) {
|
|
351
|
-
rmSync(testDir, { recursive: true, force: true });
|
|
352
|
-
}
|
|
353
|
-
});
|
|
354
|
-
|
|
355
|
-
function createOpenApiFile(
|
|
356
|
-
appDir: string,
|
|
357
|
-
endpoints: Array<{ method: string; path: string }>,
|
|
358
|
-
): void {
|
|
359
|
-
const endpointAuthEntries = endpoints
|
|
360
|
-
.map((ep) => ` '${ep.method.toUpperCase()} ${ep.path}': null,`)
|
|
361
|
-
.join('\n');
|
|
362
|
-
|
|
363
|
-
const content = `// Auto-generated by @geekmidas/cli - DO NOT EDIT
|
|
364
|
-
|
|
365
|
-
export const endpointAuth = {
|
|
366
|
-
${endpointAuthEntries}
|
|
367
|
-
} as const;
|
|
368
|
-
|
|
369
|
-
export const paths = {};
|
|
370
|
-
export function createApi() { return {}; }
|
|
371
|
-
`;
|
|
372
|
-
|
|
373
|
-
const gkmDir = join(appDir, '.gkm');
|
|
374
|
-
mkdirSync(gkmDir, { recursive: true });
|
|
375
|
-
writeFileSync(join(gkmDir, 'openapi.ts'), content);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
it('should copy client to dependent frontends', async () => {
|
|
379
|
-
const apiDir = join(testDir, 'apps/api');
|
|
380
|
-
const webDir = join(testDir, 'apps/web');
|
|
381
|
-
mkdirSync(webDir, { recursive: true });
|
|
382
|
-
|
|
383
|
-
createOpenApiFile(apiDir, [
|
|
384
|
-
{ method: 'GET', path: '/users' },
|
|
385
|
-
{ method: 'POST', path: '/users' },
|
|
386
|
-
]);
|
|
387
|
-
|
|
388
|
-
const workspace: NormalizedWorkspace = {
|
|
389
|
-
name: 'test',
|
|
390
|
-
root: testDir,
|
|
391
|
-
apps: {
|
|
392
|
-
api: {
|
|
393
|
-
type: 'backend',
|
|
394
|
-
path: 'apps/api',
|
|
395
|
-
port: 3000,
|
|
396
|
-
dependencies: [],
|
|
397
|
-
routes: './src/endpoints/**/*.ts',
|
|
398
|
-
resolvedDeployTarget: 'dokploy',
|
|
399
|
-
},
|
|
400
|
-
web: {
|
|
401
|
-
type: 'frontend',
|
|
402
|
-
path: 'apps/web',
|
|
403
|
-
port: 3001,
|
|
404
|
-
dependencies: ['api'],
|
|
405
|
-
client: { output: 'src/api' },
|
|
406
|
-
resolvedDeployTarget: 'dokploy',
|
|
407
|
-
},
|
|
408
|
-
},
|
|
409
|
-
services: {},
|
|
410
|
-
deploy: { default: 'dokploy' },
|
|
411
|
-
shared: { packages: [] },
|
|
412
|
-
secrets: {},
|
|
413
|
-
};
|
|
414
|
-
|
|
415
|
-
const results = await copyClientToFrontends(workspace, 'api', {
|
|
416
|
-
silent: true,
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
expect(results).toHaveLength(1);
|
|
420
|
-
expect(results[0]?.success).toBe(true);
|
|
421
|
-
expect(results[0]?.frontendApp).toBe('web');
|
|
422
|
-
expect(results[0]?.backendApp).toBe('api');
|
|
423
|
-
expect(results[0]?.endpointCount).toBe(2);
|
|
424
|
-
expect(existsSync(join(webDir, 'src/api/api.ts'))).toBe(true);
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
it('should skip frontends without client.output configured', async () => {
|
|
428
|
-
const apiDir = join(testDir, 'apps/api');
|
|
429
|
-
const webDir = join(testDir, 'apps/web');
|
|
430
|
-
mkdirSync(webDir, { recursive: true });
|
|
431
|
-
|
|
432
|
-
createOpenApiFile(apiDir, [{ method: 'GET', path: '/users' }]);
|
|
433
|
-
|
|
434
|
-
const workspace: NormalizedWorkspace = {
|
|
435
|
-
name: 'test',
|
|
436
|
-
root: testDir,
|
|
437
|
-
apps: {
|
|
438
|
-
api: {
|
|
439
|
-
type: 'backend',
|
|
440
|
-
path: 'apps/api',
|
|
441
|
-
port: 3000,
|
|
442
|
-
dependencies: [],
|
|
443
|
-
routes: './src/endpoints/**/*.ts',
|
|
444
|
-
resolvedDeployTarget: 'dokploy',
|
|
445
|
-
},
|
|
446
|
-
web: {
|
|
447
|
-
type: 'frontend',
|
|
448
|
-
path: 'apps/web',
|
|
449
|
-
port: 3001,
|
|
450
|
-
dependencies: ['api'],
|
|
451
|
-
// No client.output configured
|
|
452
|
-
resolvedDeployTarget: 'dokploy',
|
|
453
|
-
},
|
|
454
|
-
},
|
|
455
|
-
services: {},
|
|
456
|
-
deploy: { default: 'dokploy' },
|
|
457
|
-
shared: { packages: [] },
|
|
458
|
-
secrets: {},
|
|
459
|
-
};
|
|
460
|
-
|
|
461
|
-
const results = await copyClientToFrontends(workspace, 'api', {
|
|
462
|
-
silent: true,
|
|
463
|
-
});
|
|
464
|
-
|
|
465
|
-
expect(results).toHaveLength(0);
|
|
466
|
-
});
|
|
467
|
-
|
|
468
|
-
it('should return empty results for non-backend app', async () => {
|
|
469
|
-
const workspace: NormalizedWorkspace = {
|
|
470
|
-
name: 'test',
|
|
471
|
-
root: testDir,
|
|
472
|
-
apps: {
|
|
473
|
-
web: {
|
|
474
|
-
type: 'frontend',
|
|
475
|
-
path: 'apps/web',
|
|
476
|
-
port: 3001,
|
|
477
|
-
dependencies: [],
|
|
478
|
-
resolvedDeployTarget: 'dokploy',
|
|
479
|
-
},
|
|
480
|
-
},
|
|
481
|
-
services: {},
|
|
482
|
-
deploy: { default: 'dokploy' },
|
|
483
|
-
shared: { packages: [] },
|
|
484
|
-
secrets: {},
|
|
485
|
-
};
|
|
486
|
-
|
|
487
|
-
const results = await copyClientToFrontends(workspace, 'web', {
|
|
488
|
-
silent: true,
|
|
489
|
-
});
|
|
490
|
-
|
|
491
|
-
expect(results).toEqual([]);
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
it('should return empty results when openapi file does not exist', async () => {
|
|
495
|
-
const apiDir = join(testDir, 'apps/api');
|
|
496
|
-
const webDir = join(testDir, 'apps/web');
|
|
497
|
-
mkdirSync(apiDir, { recursive: true });
|
|
498
|
-
mkdirSync(webDir, { recursive: true });
|
|
499
|
-
// Don't create openapi file
|
|
500
|
-
|
|
501
|
-
const workspace: NormalizedWorkspace = {
|
|
502
|
-
name: 'test',
|
|
503
|
-
root: testDir,
|
|
504
|
-
apps: {
|
|
505
|
-
api: {
|
|
506
|
-
type: 'backend',
|
|
507
|
-
path: 'apps/api',
|
|
508
|
-
port: 3000,
|
|
509
|
-
dependencies: [],
|
|
510
|
-
routes: './src/endpoints/**/*.ts',
|
|
511
|
-
resolvedDeployTarget: 'dokploy',
|
|
512
|
-
},
|
|
513
|
-
web: {
|
|
514
|
-
type: 'frontend',
|
|
515
|
-
path: 'apps/web',
|
|
516
|
-
port: 3001,
|
|
517
|
-
dependencies: ['api'],
|
|
518
|
-
client: { output: 'src/api' },
|
|
519
|
-
resolvedDeployTarget: 'dokploy',
|
|
520
|
-
},
|
|
521
|
-
},
|
|
522
|
-
services: {},
|
|
523
|
-
deploy: { default: 'dokploy' },
|
|
524
|
-
shared: { packages: [] },
|
|
525
|
-
secrets: {},
|
|
526
|
-
};
|
|
527
|
-
|
|
528
|
-
const results = await copyClientToFrontends(workspace, 'api', {
|
|
529
|
-
silent: true,
|
|
530
|
-
});
|
|
531
|
-
|
|
532
|
-
expect(results).toEqual([]);
|
|
533
|
-
});
|
|
534
|
-
|
|
535
|
-
it('should copy to multiple frontends', async () => {
|
|
536
|
-
const apiDir = join(testDir, 'apps/api');
|
|
537
|
-
const webDir = join(testDir, 'apps/web');
|
|
538
|
-
const adminDir = join(testDir, 'apps/admin');
|
|
539
|
-
mkdirSync(webDir, { recursive: true });
|
|
540
|
-
mkdirSync(adminDir, { recursive: true });
|
|
541
|
-
|
|
542
|
-
createOpenApiFile(apiDir, [{ method: 'GET', path: '/users' }]);
|
|
543
|
-
|
|
544
|
-
const workspace: NormalizedWorkspace = {
|
|
545
|
-
name: 'test',
|
|
546
|
-
root: testDir,
|
|
547
|
-
apps: {
|
|
548
|
-
api: {
|
|
549
|
-
type: 'backend',
|
|
550
|
-
path: 'apps/api',
|
|
551
|
-
port: 3000,
|
|
552
|
-
dependencies: [],
|
|
553
|
-
routes: './src/endpoints/**/*.ts',
|
|
554
|
-
resolvedDeployTarget: 'dokploy',
|
|
555
|
-
},
|
|
556
|
-
web: {
|
|
557
|
-
type: 'frontend',
|
|
558
|
-
path: 'apps/web',
|
|
559
|
-
port: 3001,
|
|
560
|
-
dependencies: ['api'],
|
|
561
|
-
client: { output: 'src/api' },
|
|
562
|
-
resolvedDeployTarget: 'dokploy',
|
|
563
|
-
},
|
|
564
|
-
admin: {
|
|
565
|
-
type: 'frontend',
|
|
566
|
-
path: 'apps/admin',
|
|
567
|
-
port: 3002,
|
|
568
|
-
dependencies: ['api'],
|
|
569
|
-
client: { output: 'lib/client' },
|
|
570
|
-
resolvedDeployTarget: 'dokploy',
|
|
571
|
-
},
|
|
572
|
-
},
|
|
573
|
-
services: {},
|
|
574
|
-
deploy: { default: 'dokploy' },
|
|
575
|
-
shared: { packages: [] },
|
|
576
|
-
secrets: {},
|
|
577
|
-
};
|
|
578
|
-
|
|
579
|
-
const results = await copyClientToFrontends(workspace, 'api', {
|
|
580
|
-
silent: true,
|
|
581
|
-
});
|
|
582
|
-
|
|
583
|
-
expect(results).toHaveLength(2);
|
|
584
|
-
expect(results.every((r) => r.success)).toBe(true);
|
|
585
|
-
expect(existsSync(join(webDir, 'src/api/api.ts'))).toBe(true);
|
|
586
|
-
expect(existsSync(join(adminDir, 'lib/client/api.ts'))).toBe(true);
|
|
587
|
-
});
|
|
588
|
-
|
|
589
|
-
it('should add header comment to copied file', async () => {
|
|
590
|
-
const apiDir = join(testDir, 'apps/api');
|
|
591
|
-
const webDir = join(testDir, 'apps/web');
|
|
592
|
-
mkdirSync(webDir, { recursive: true });
|
|
593
|
-
|
|
594
|
-
createOpenApiFile(apiDir, [{ method: 'GET', path: '/users' }]);
|
|
595
|
-
|
|
596
|
-
const workspace: NormalizedWorkspace = {
|
|
597
|
-
name: 'test',
|
|
598
|
-
root: testDir,
|
|
599
|
-
apps: {
|
|
600
|
-
api: {
|
|
601
|
-
type: 'backend',
|
|
602
|
-
path: 'apps/api',
|
|
603
|
-
port: 3000,
|
|
604
|
-
dependencies: [],
|
|
605
|
-
routes: './src/endpoints/**/*.ts',
|
|
606
|
-
resolvedDeployTarget: 'dokploy',
|
|
607
|
-
},
|
|
608
|
-
web: {
|
|
609
|
-
type: 'frontend',
|
|
610
|
-
path: 'apps/web',
|
|
611
|
-
port: 3001,
|
|
612
|
-
dependencies: ['api'],
|
|
613
|
-
client: { output: 'src/api' },
|
|
614
|
-
resolvedDeployTarget: 'dokploy',
|
|
615
|
-
},
|
|
616
|
-
},
|
|
617
|
-
services: {},
|
|
618
|
-
deploy: { default: 'dokploy' },
|
|
619
|
-
shared: { packages: [] },
|
|
620
|
-
secrets: {},
|
|
621
|
-
};
|
|
622
|
-
|
|
623
|
-
await copyClientToFrontends(workspace, 'api', { silent: true });
|
|
624
|
-
|
|
625
|
-
const content = readFileSync(join(webDir, 'src/api/api.ts'), 'utf-8');
|
|
626
|
-
expect(content).toContain('Auto-generated API client for api');
|
|
627
|
-
expect(content).toContain('DO NOT EDIT');
|
|
628
|
-
});
|
|
629
|
-
|
|
630
|
-
it('should use backend name in filename when frontend has multiple backends', async () => {
|
|
631
|
-
const apiDir = join(testDir, 'apps/api');
|
|
632
|
-
const authDir = join(testDir, 'apps/auth');
|
|
633
|
-
const webDir = join(testDir, 'apps/web');
|
|
634
|
-
mkdirSync(webDir, { recursive: true });
|
|
635
|
-
|
|
636
|
-
createOpenApiFile(apiDir, [{ method: 'GET', path: '/users' }]);
|
|
637
|
-
createOpenApiFile(authDir, [{ method: 'POST', path: '/login' }]);
|
|
638
|
-
|
|
639
|
-
const workspace: NormalizedWorkspace = {
|
|
640
|
-
name: 'test',
|
|
641
|
-
root: testDir,
|
|
642
|
-
apps: {
|
|
643
|
-
api: {
|
|
644
|
-
type: 'backend',
|
|
645
|
-
path: 'apps/api',
|
|
646
|
-
port: 3000,
|
|
647
|
-
dependencies: [],
|
|
648
|
-
routes: './src/endpoints/**/*.ts',
|
|
649
|
-
resolvedDeployTarget: 'dokploy',
|
|
650
|
-
},
|
|
651
|
-
auth: {
|
|
652
|
-
type: 'backend',
|
|
653
|
-
path: 'apps/auth',
|
|
654
|
-
port: 3001,
|
|
655
|
-
dependencies: [],
|
|
656
|
-
routes: './src/endpoints/**/*.ts',
|
|
657
|
-
resolvedDeployTarget: 'dokploy',
|
|
658
|
-
},
|
|
659
|
-
web: {
|
|
660
|
-
type: 'frontend',
|
|
661
|
-
path: 'apps/web',
|
|
662
|
-
port: 3002,
|
|
663
|
-
dependencies: ['api', 'auth'], // Multiple backends
|
|
664
|
-
client: { output: 'src/api' },
|
|
665
|
-
resolvedDeployTarget: 'dokploy',
|
|
666
|
-
},
|
|
667
|
-
},
|
|
668
|
-
services: {},
|
|
669
|
-
deploy: { default: 'dokploy' },
|
|
670
|
-
shared: { packages: [] },
|
|
671
|
-
secrets: {},
|
|
672
|
-
};
|
|
673
|
-
|
|
674
|
-
await copyClientToFrontends(workspace, 'api', { silent: true });
|
|
675
|
-
await copyClientToFrontends(workspace, 'auth', { silent: true });
|
|
676
|
-
|
|
677
|
-
// Should use {backend}.ts naming
|
|
678
|
-
expect(existsSync(join(webDir, 'src/api/api.ts'))).toBe(true);
|
|
679
|
-
expect(existsSync(join(webDir, 'src/api/auth.ts'))).toBe(true);
|
|
680
|
-
});
|
|
681
|
-
});
|
|
682
|
-
|
|
683
|
-
describe('copyAllClients', () => {
|
|
684
|
-
let testDir: string;
|
|
685
|
-
|
|
686
|
-
beforeEach(() => {
|
|
687
|
-
testDir = join(
|
|
688
|
-
tmpdir(),
|
|
689
|
-
`gkm-client-test-${Date.now()}-${Math.random().toString(36).slice(2)}`,
|
|
690
|
-
);
|
|
691
|
-
mkdirSync(testDir, { recursive: true });
|
|
692
|
-
});
|
|
693
|
-
|
|
694
|
-
afterEach(() => {
|
|
695
|
-
if (existsSync(testDir)) {
|
|
696
|
-
rmSync(testDir, { recursive: true, force: true });
|
|
697
|
-
}
|
|
698
|
-
});
|
|
699
|
-
|
|
700
|
-
function createOpenApiFile(
|
|
701
|
-
appDir: string,
|
|
702
|
-
endpoints: Array<{ method: string; path: string }>,
|
|
703
|
-
): void {
|
|
704
|
-
const endpointAuthEntries = endpoints
|
|
705
|
-
.map((ep) => ` '${ep.method.toUpperCase()} ${ep.path}': null,`)
|
|
706
|
-
.join('\n');
|
|
707
|
-
|
|
708
|
-
const content = `// Auto-generated by @geekmidas/cli - DO NOT EDIT
|
|
709
|
-
|
|
710
|
-
export const endpointAuth = {
|
|
711
|
-
${endpointAuthEntries}
|
|
712
|
-
} as const;
|
|
713
|
-
|
|
714
|
-
export const paths = {};
|
|
715
|
-
export function createApi() { return {}; }
|
|
716
|
-
`;
|
|
717
|
-
|
|
718
|
-
const gkmDir = join(appDir, '.gkm');
|
|
719
|
-
mkdirSync(gkmDir, { recursive: true });
|
|
720
|
-
writeFileSync(join(gkmDir, 'openapi.ts'), content);
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
it('should copy from all backends to their dependent frontends', async () => {
|
|
724
|
-
const apiDir = join(testDir, 'apps/api');
|
|
725
|
-
const authDir = join(testDir, 'apps/auth');
|
|
726
|
-
const webDir = join(testDir, 'apps/web');
|
|
727
|
-
const adminDir = join(testDir, 'apps/admin');
|
|
728
|
-
mkdirSync(webDir, { recursive: true });
|
|
729
|
-
mkdirSync(adminDir, { recursive: true });
|
|
730
|
-
|
|
731
|
-
createOpenApiFile(apiDir, [{ method: 'GET', path: '/users' }]);
|
|
732
|
-
createOpenApiFile(authDir, [{ method: 'POST', path: '/login' }]);
|
|
733
|
-
|
|
734
|
-
const workspace: NormalizedWorkspace = {
|
|
735
|
-
name: 'test',
|
|
736
|
-
root: testDir,
|
|
737
|
-
apps: {
|
|
738
|
-
api: {
|
|
739
|
-
type: 'backend',
|
|
740
|
-
path: 'apps/api',
|
|
741
|
-
port: 3000,
|
|
742
|
-
dependencies: [],
|
|
743
|
-
routes: './src/endpoints/**/*.ts',
|
|
744
|
-
resolvedDeployTarget: 'dokploy',
|
|
745
|
-
},
|
|
746
|
-
auth: {
|
|
747
|
-
type: 'backend',
|
|
748
|
-
path: 'apps/auth',
|
|
749
|
-
port: 3001,
|
|
750
|
-
dependencies: [],
|
|
751
|
-
routes: './src/endpoints/**/*.ts',
|
|
752
|
-
resolvedDeployTarget: 'dokploy',
|
|
753
|
-
},
|
|
754
|
-
web: {
|
|
755
|
-
type: 'frontend',
|
|
756
|
-
path: 'apps/web',
|
|
757
|
-
port: 3002,
|
|
758
|
-
dependencies: ['api'],
|
|
759
|
-
client: { output: 'src/api' },
|
|
760
|
-
resolvedDeployTarget: 'dokploy',
|
|
761
|
-
},
|
|
762
|
-
admin: {
|
|
763
|
-
type: 'frontend',
|
|
764
|
-
path: 'apps/admin',
|
|
765
|
-
port: 3003,
|
|
766
|
-
dependencies: ['auth'],
|
|
767
|
-
client: { output: 'lib/client' },
|
|
768
|
-
resolvedDeployTarget: 'dokploy',
|
|
769
|
-
},
|
|
770
|
-
},
|
|
771
|
-
services: {},
|
|
772
|
-
deploy: { default: 'dokploy' },
|
|
773
|
-
shared: { packages: [] },
|
|
774
|
-
secrets: {},
|
|
775
|
-
};
|
|
776
|
-
|
|
777
|
-
const results = await copyAllClients(workspace, { silent: true });
|
|
778
|
-
|
|
779
|
-
expect(results).toHaveLength(2);
|
|
780
|
-
expect(results.every((r) => r.success)).toBe(true);
|
|
781
|
-
expect(existsSync(join(webDir, 'src/api/api.ts'))).toBe(true);
|
|
782
|
-
expect(existsSync(join(adminDir, 'lib/client/auth.ts'))).toBe(true);
|
|
783
|
-
});
|
|
784
|
-
|
|
785
|
-
it('should return empty array when no backends have routes', async () => {
|
|
786
|
-
const workspace: NormalizedWorkspace = {
|
|
787
|
-
name: 'test',
|
|
788
|
-
root: testDir,
|
|
789
|
-
apps: {
|
|
790
|
-
worker: {
|
|
791
|
-
type: 'backend',
|
|
792
|
-
path: 'apps/worker',
|
|
793
|
-
port: 3000,
|
|
794
|
-
dependencies: [],
|
|
795
|
-
// No routes
|
|
796
|
-
resolvedDeployTarget: 'dokploy',
|
|
797
|
-
},
|
|
798
|
-
},
|
|
799
|
-
services: {},
|
|
800
|
-
deploy: { default: 'dokploy' },
|
|
801
|
-
shared: { packages: [] },
|
|
802
|
-
secrets: {},
|
|
803
|
-
};
|
|
804
|
-
|
|
805
|
-
const results = await copyAllClients(workspace, { silent: true });
|
|
806
|
-
|
|
807
|
-
expect(results).toEqual([]);
|
|
808
|
-
});
|
|
809
|
-
});
|
|
810
326
|
});
|
|
@@ -1,23 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
|
-
import { dirname, join, relative } from 'node:path';
|
|
1
|
+
import { join } from 'node:path';
|
|
4
2
|
import { isPartitionedRoutes, type Routes } from '../types.js';
|
|
5
3
|
import type { NormalizedWorkspace } from './types.js';
|
|
6
4
|
|
|
7
|
-
const logger = console;
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Result of copying a client to a frontend app.
|
|
11
|
-
*/
|
|
12
|
-
export interface ClientCopyResult {
|
|
13
|
-
frontendApp: string;
|
|
14
|
-
backendApp: string;
|
|
15
|
-
outputPath: string;
|
|
16
|
-
endpointCount: number;
|
|
17
|
-
success: boolean;
|
|
18
|
-
error?: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
5
|
/**
|
|
22
6
|
* Normalize routes to an array of patterns.
|
|
23
7
|
* Handles string, string[], and PartitionedRoutes (extracts paths).
|
|
@@ -120,131 +104,3 @@ export function getBackendOpenApiPath(
|
|
|
120
104
|
|
|
121
105
|
return join(workspace.root, app.path, '.gkm', 'openapi.ts');
|
|
122
106
|
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* Count endpoints in an OpenAPI spec content.
|
|
126
|
-
*/
|
|
127
|
-
function countEndpoints(content: string): number {
|
|
128
|
-
const endpointMatches = content.match(
|
|
129
|
-
/'(GET|POST|PUT|PATCH|DELETE)\s+\/[^']+'/g,
|
|
130
|
-
);
|
|
131
|
-
return endpointMatches?.length ?? 0;
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Copy the OpenAPI client from a backend to all dependent frontend apps.
|
|
136
|
-
* Called when the backend's .gkm/openapi.ts file changes.
|
|
137
|
-
*/
|
|
138
|
-
export async function copyClientToFrontends(
|
|
139
|
-
workspace: NormalizedWorkspace,
|
|
140
|
-
backendAppName: string,
|
|
141
|
-
options: { silent?: boolean } = {},
|
|
142
|
-
): Promise<ClientCopyResult[]> {
|
|
143
|
-
const log = options.silent ? () => {} : logger.log.bind(logger);
|
|
144
|
-
const results: ClientCopyResult[] = [];
|
|
145
|
-
|
|
146
|
-
const backendApp = workspace.apps[backendAppName];
|
|
147
|
-
if (!backendApp || backendApp.type !== 'backend') {
|
|
148
|
-
return results;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Get the backend's OpenAPI spec
|
|
152
|
-
const openApiPath = join(
|
|
153
|
-
workspace.root,
|
|
154
|
-
backendApp.path,
|
|
155
|
-
'.gkm',
|
|
156
|
-
'openapi.ts',
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
if (!existsSync(openApiPath)) {
|
|
160
|
-
return results;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const content = await readFile(openApiPath, 'utf-8');
|
|
164
|
-
const endpointCount = countEndpoints(content);
|
|
165
|
-
|
|
166
|
-
// Get all frontends that depend on this backend
|
|
167
|
-
const dependentFrontends = getDependentFrontends(workspace, backendAppName);
|
|
168
|
-
|
|
169
|
-
for (const frontendAppName of dependentFrontends) {
|
|
170
|
-
const frontendApp = workspace.apps[frontendAppName];
|
|
171
|
-
if (!frontendApp || frontendApp.type !== 'frontend') {
|
|
172
|
-
continue;
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// Check if frontend has client output configured
|
|
176
|
-
const clientOutput = frontendApp.client?.output;
|
|
177
|
-
if (!clientOutput) {
|
|
178
|
-
continue;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const result: ClientCopyResult = {
|
|
182
|
-
frontendApp: frontendAppName,
|
|
183
|
-
backendApp: backendAppName,
|
|
184
|
-
outputPath: '',
|
|
185
|
-
endpointCount,
|
|
186
|
-
success: false,
|
|
187
|
-
};
|
|
188
|
-
|
|
189
|
-
try {
|
|
190
|
-
const frontendPath = join(workspace.root, frontendApp.path);
|
|
191
|
-
const outputDir = join(frontendPath, clientOutput);
|
|
192
|
-
await mkdir(outputDir, { recursive: true });
|
|
193
|
-
|
|
194
|
-
// Use backend app name as filename
|
|
195
|
-
const fileName = `${backendAppName}.ts`;
|
|
196
|
-
const outputPath = join(outputDir, fileName);
|
|
197
|
-
|
|
198
|
-
// Add header comment with backend reference
|
|
199
|
-
const backendRelPath = relative(
|
|
200
|
-
dirname(outputPath),
|
|
201
|
-
join(workspace.root, backendApp.path),
|
|
202
|
-
);
|
|
203
|
-
|
|
204
|
-
const clientContent = `/**
|
|
205
|
-
* Auto-generated API client for ${backendAppName}
|
|
206
|
-
* Generated from: ${backendRelPath}
|
|
207
|
-
*
|
|
208
|
-
* DO NOT EDIT - This file is automatically regenerated when backend schemas change.
|
|
209
|
-
*/
|
|
210
|
-
|
|
211
|
-
${content}
|
|
212
|
-
`;
|
|
213
|
-
|
|
214
|
-
await writeFile(outputPath, clientContent);
|
|
215
|
-
|
|
216
|
-
result.outputPath = outputPath;
|
|
217
|
-
result.success = true;
|
|
218
|
-
|
|
219
|
-
log(
|
|
220
|
-
`📦 Copied client to ${frontendAppName} from ${backendAppName} (${endpointCount} endpoints)`,
|
|
221
|
-
);
|
|
222
|
-
} catch (error) {
|
|
223
|
-
result.error = (error as Error).message;
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
results.push(result);
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
return results;
|
|
230
|
-
}
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* Copy clients from all backends to their dependent frontends.
|
|
234
|
-
* Useful for initial setup or force refresh.
|
|
235
|
-
*/
|
|
236
|
-
export async function copyAllClients(
|
|
237
|
-
workspace: NormalizedWorkspace,
|
|
238
|
-
options: { silent?: boolean } = {},
|
|
239
|
-
): Promise<ClientCopyResult[]> {
|
|
240
|
-
const allResults: ClientCopyResult[] = [];
|
|
241
|
-
|
|
242
|
-
for (const [appName, app] of Object.entries(workspace.apps)) {
|
|
243
|
-
if (app.type === 'backend' && app.routes) {
|
|
244
|
-
const results = await copyClientToFrontends(workspace, appName, options);
|
|
245
|
-
allResults.push(...results);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
return allResults;
|
|
250
|
-
}
|
package/src/workspace/index.ts
CHANGED
package/src/workspace/schema.ts
CHANGED
|
@@ -54,13 +54,6 @@ const HooksConfigSchema = z.object({
|
|
|
54
54
|
server: z.string().optional(),
|
|
55
55
|
});
|
|
56
56
|
|
|
57
|
-
/**
|
|
58
|
-
* Client generation configuration schema.
|
|
59
|
-
*/
|
|
60
|
-
const ClientConfigSchema = z.object({
|
|
61
|
-
output: z.string().optional(),
|
|
62
|
-
});
|
|
63
|
-
|
|
64
57
|
/**
|
|
65
58
|
* Auth provider schema.
|
|
66
59
|
* Currently only 'better-auth' is supported.
|
|
@@ -577,8 +570,6 @@ const AppConfigSchema = z
|
|
|
577
570
|
|
|
578
571
|
// Framework (backend or frontend)
|
|
579
572
|
framework: FrameworkSchema.optional(),
|
|
580
|
-
client: ClientConfigSchema.optional(),
|
|
581
|
-
|
|
582
573
|
// Frontend-specific: config file paths for env sniffing (calls .parse() at import)
|
|
583
574
|
config: z
|
|
584
575
|
.object({
|