@apso/cli 0.40.0 → 0.41.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/generate.js +10 -0
- package/dist/commands/migrate/verify.d.ts +1 -0
- package/dist/commands/migrate/verify.js +5 -1
- package/dist/commands/test/smoke.d.ts +1 -0
- package/dist/commands/test/smoke.js +7 -1
- package/dist/lib/utils/crud-deps.d.ts +18 -0
- package/dist/lib/utils/crud-deps.js +47 -0
- package/dist/lib/verify/index.d.ts +5 -2
- package/dist/lib/verify/index.js +8 -5
- package/npm-shrinkwrap.json +2 -2
- package/oclif.manifest.json +13 -1
- package/package.json +1 -1
|
@@ -10,6 +10,7 @@ const interactive_1 = require("../lib/utils/interactive");
|
|
|
10
10
|
const perf_hooks_1 = require("perf_hooks");
|
|
11
11
|
const file_system_1 = require("../lib/utils/file-system");
|
|
12
12
|
const git_hooks_1 = require("../lib/utils/git-hooks");
|
|
13
|
+
const crud_deps_1 = require("../lib/utils/crud-deps");
|
|
13
14
|
class Generate extends base_command_1.default {
|
|
14
15
|
async run() {
|
|
15
16
|
var _a, _b;
|
|
@@ -240,6 +241,15 @@ class Generate extends base_command_1.default {
|
|
|
240
241
|
else if (generatedFileCount === 0) {
|
|
241
242
|
console.log("[apso] Skipping formatting (no files were generated)");
|
|
242
243
|
}
|
|
244
|
+
// Generated TS code imports @apso/crud*. Projects from the v1 template
|
|
245
|
+
// (@nestjsx/crud) don't declare those, and every entity then fails to
|
|
246
|
+
// compile with TS2307. Declare them so the next install picks them up.
|
|
247
|
+
if (language === "typescript" && generatedFileCount > 0) {
|
|
248
|
+
const added = (0, crud_deps_1.ensureCrudDeps)(process.cwd());
|
|
249
|
+
if (added.length > 0) {
|
|
250
|
+
console.log(`[apso] Added ${added.join(", ")} to package.json (the generated code imports them). Run npm install before building.`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
243
253
|
const totalBuildTime = perf_hooks_1.performance.now() - totalBuildStart;
|
|
244
254
|
console.log(`[apso] Finished building all entities in ${totalBuildTime.toFixed(2)} ms`);
|
|
245
255
|
// Best-effort: install the commit co-author hook so commits that include
|
|
@@ -5,6 +5,7 @@ export default class MigrateVerify extends BaseCommand {
|
|
|
5
5
|
static flags: {
|
|
6
6
|
baseline: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
7
7
|
migration: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces/parser").CustomOptions>;
|
|
8
|
+
"skip-build": import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
8
9
|
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
9
10
|
};
|
|
10
11
|
run(): Promise<void>;
|
|
@@ -8,7 +8,7 @@ const verify_1 = require("../../lib/verify");
|
|
|
8
8
|
class MigrateVerify extends base_command_1.default {
|
|
9
9
|
async run() {
|
|
10
10
|
const { flags } = await this.parse(MigrateVerify);
|
|
11
|
-
const result = await (0, verify_1.verifyMigration)(process.cwd(), path.resolve(flags.baseline), flags.migration, (0, verify_1.cliBinPath)(this.config.root));
|
|
11
|
+
const result = await (0, verify_1.verifyMigration)(process.cwd(), path.resolve(flags.baseline), flags.migration, (0, verify_1.cliBinPath)(this.config.root), flags["skip-build"]);
|
|
12
12
|
(0, verify_1.report)((s) => this.log(s), result, flags.json, result.ok ? "Migration verified." : undefined);
|
|
13
13
|
if (!result.ok)
|
|
14
14
|
this.exit(1);
|
|
@@ -27,6 +27,10 @@ MigrateVerify.flags = {
|
|
|
27
27
|
description: "The migration file to run",
|
|
28
28
|
required: true,
|
|
29
29
|
}),
|
|
30
|
+
"skip-build": core_1.Flags.boolean({
|
|
31
|
+
description: "Use the existing dist/ build instead of building first",
|
|
32
|
+
default: false,
|
|
33
|
+
}),
|
|
30
34
|
json: core_1.Flags.boolean({
|
|
31
35
|
description: "Print the result as JSON",
|
|
32
36
|
default: false,
|
|
@@ -3,6 +3,7 @@ export default class TestSmoke extends BaseCommand {
|
|
|
3
3
|
static description: string;
|
|
4
4
|
static examples: string[];
|
|
5
5
|
static flags: {
|
|
6
|
+
"skip-build": import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
6
7
|
json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
|
|
7
8
|
};
|
|
8
9
|
run(): Promise<void>;
|
|
@@ -11,7 +11,9 @@ class TestSmoke extends base_command_1.default {
|
|
|
11
11
|
const { entities, auth } = (0, apsorc_parser_1.parseApsorc)();
|
|
12
12
|
// With auth configured every entity route needs a session, so only /health is checked.
|
|
13
13
|
const names = auth ? [] : entities.map((e) => e.name);
|
|
14
|
-
const result = await (0, verify_1.smokeTest)(process.cwd(), names
|
|
14
|
+
const result = await (0, verify_1.smokeTest)(process.cwd(), names, {
|
|
15
|
+
skipBuild: flags["skip-build"],
|
|
16
|
+
});
|
|
15
17
|
(0, verify_1.report)((s) => this.log(s), result, flags.json, result.ok ? "Smoke test passed." : undefined);
|
|
16
18
|
if (!result.ok)
|
|
17
19
|
this.exit(1);
|
|
@@ -20,6 +22,10 @@ class TestSmoke extends base_command_1.default {
|
|
|
20
22
|
TestSmoke.description = "Start the service on a local PGlite database and call /health and every entity's list endpoint (TypeScript services)";
|
|
21
23
|
TestSmoke.examples = [`$ apso test smoke`];
|
|
22
24
|
TestSmoke.flags = {
|
|
25
|
+
"skip-build": core_1.Flags.boolean({
|
|
26
|
+
description: "Use the existing dist/ build instead of building first",
|
|
27
|
+
default: false,
|
|
28
|
+
}),
|
|
23
29
|
json: core_1.Flags.boolean({
|
|
24
30
|
description: "Print the result as JSON",
|
|
25
31
|
default: false,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime packages the generated TypeScript code imports (entities, services,
|
|
3
|
+
* controllers, guards). Versions match the pinned service-template-ts tag
|
|
4
|
+
* (see TEMPLATE_REFS in ./template.ts); bump both together.
|
|
5
|
+
*/
|
|
6
|
+
export declare const APSO_CRUD_DEPS: Record<string, string>;
|
|
7
|
+
/**
|
|
8
|
+
* Add any missing @apso/crud runtime packages to the project's package.json.
|
|
9
|
+
*
|
|
10
|
+
* Projects created from the v1 template (@nestjsx/crud) don't declare them, so
|
|
11
|
+
* freshly generated code fails `nest build` with TS2307 "Cannot find module
|
|
12
|
+
* '@apso/crud'" on every entity. Existing @nestjsx deps are left alone because
|
|
13
|
+
* hand-written code may still import them.
|
|
14
|
+
*
|
|
15
|
+
* @param {string} projectDir - Directory holding the project's package.json.
|
|
16
|
+
* @returns {string[]} Names added (empty when nothing changed or no package.json).
|
|
17
|
+
*/
|
|
18
|
+
export declare function ensureCrudDeps(projectDir: string): string[];
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ensureCrudDeps = exports.APSO_CRUD_DEPS = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const fs = tslib_1.__importStar(require("fs"));
|
|
6
|
+
const path = tslib_1.__importStar(require("path"));
|
|
7
|
+
/**
|
|
8
|
+
* Runtime packages the generated TypeScript code imports (entities, services,
|
|
9
|
+
* controllers, guards). Versions match the pinned service-template-ts tag
|
|
10
|
+
* (see TEMPLATE_REFS in ./template.ts); bump both together.
|
|
11
|
+
*/
|
|
12
|
+
exports.APSO_CRUD_DEPS = {
|
|
13
|
+
"@apso/crud": "^1.0.1",
|
|
14
|
+
"@apso/crud-core": "^1.0.1",
|
|
15
|
+
"@apso/crud-request": "^1.0.1",
|
|
16
|
+
"@apso/crud-typeorm": "^1.0.1",
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Add any missing @apso/crud runtime packages to the project's package.json.
|
|
20
|
+
*
|
|
21
|
+
* Projects created from the v1 template (@nestjsx/crud) don't declare them, so
|
|
22
|
+
* freshly generated code fails `nest build` with TS2307 "Cannot find module
|
|
23
|
+
* '@apso/crud'" on every entity. Existing @nestjsx deps are left alone because
|
|
24
|
+
* hand-written code may still import them.
|
|
25
|
+
*
|
|
26
|
+
* @param {string} projectDir - Directory holding the project's package.json.
|
|
27
|
+
* @returns {string[]} Names added (empty when nothing changed or no package.json).
|
|
28
|
+
*/
|
|
29
|
+
function ensureCrudDeps(projectDir) {
|
|
30
|
+
var _a;
|
|
31
|
+
const pkgPath = path.join(projectDir, "package.json");
|
|
32
|
+
if (!fs.existsSync(pkgPath))
|
|
33
|
+
return [];
|
|
34
|
+
const raw = fs.readFileSync(pkgPath, "utf8");
|
|
35
|
+
const pkg = JSON.parse(raw);
|
|
36
|
+
const declared = { ...pkg.devDependencies, ...pkg.dependencies };
|
|
37
|
+
const missing = Object.keys(exports.APSO_CRUD_DEPS).filter((name) => !declared[name]);
|
|
38
|
+
if (missing.length === 0)
|
|
39
|
+
return [];
|
|
40
|
+
pkg.dependencies = (_a = pkg.dependencies) !== null && _a !== void 0 ? _a : {};
|
|
41
|
+
for (const name of missing)
|
|
42
|
+
pkg.dependencies[name] = exports.APSO_CRUD_DEPS[name];
|
|
43
|
+
pkg.dependencies = Object.fromEntries(Object.entries(pkg.dependencies).sort(([a], [b]) => a.localeCompare(b)));
|
|
44
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + (raw.endsWith("\n") ? "\n" : ""));
|
|
45
|
+
return missing;
|
|
46
|
+
}
|
|
47
|
+
exports.ensureCrudDeps = ensureCrudDeps;
|
|
@@ -46,14 +46,17 @@ export interface CheckResult {
|
|
|
46
46
|
* Run a migration on a fresh copy of the deployed schema, then check that the
|
|
47
47
|
* database matches the project's entities (a second generate finds nothing).
|
|
48
48
|
*/
|
|
49
|
-
export declare function verifyMigration(projectDir: string, baselineApsorc: string, migrationFile: string, cliBin: string): Promise<CheckResult>;
|
|
49
|
+
export declare function verifyMigration(projectDir: string, baselineApsorc: string, migrationFile: string, cliBin: string, skipBuild?: boolean): Promise<CheckResult>;
|
|
50
50
|
/**
|
|
51
51
|
* Start the built service on a PGlite database synced to its entities and call
|
|
52
52
|
* GET /health and the list endpoint of every entity.
|
|
53
53
|
* ponytail: list only, no create/read/delete; add when payloads can be built
|
|
54
54
|
* from required fields and relationships.
|
|
55
55
|
*/
|
|
56
|
-
export declare function smokeTest(projectDir: string, entityNames: string[], timeoutMs?:
|
|
56
|
+
export declare function smokeTest(projectDir: string, entityNames: string[], { skipBuild, timeoutMs }?: {
|
|
57
|
+
skipBuild?: boolean | undefined;
|
|
58
|
+
timeoutMs?: number | undefined;
|
|
59
|
+
}): Promise<CheckResult>;
|
|
57
60
|
/** The CLI's own entry point, for running `generate` in the baseline copy. */
|
|
58
61
|
export declare const cliBinPath: (root: string) => string;
|
|
59
62
|
/** Print a check's steps (or JSON) through the command's logger. */
|
package/dist/lib/verify/index.js
CHANGED
|
@@ -137,7 +137,7 @@ ds.initialize()
|
|
|
137
137
|
* Run a migration on a fresh copy of the deployed schema, then check that the
|
|
138
138
|
* database matches the project's entities (a second generate finds nothing).
|
|
139
139
|
*/
|
|
140
|
-
async function verifyMigration(projectDir, baselineApsorc, migrationFile, cliBin) {
|
|
140
|
+
async function verifyMigration(projectDir, baselineApsorc, migrationFile, cliBin, skipBuild = false) {
|
|
141
141
|
const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), "apso-pglite-"));
|
|
142
142
|
const steps = [];
|
|
143
143
|
const done = (s) => {
|
|
@@ -147,7 +147,7 @@ async function verifyMigration(projectDir, baselineApsorc, migrationFile, cliBin
|
|
|
147
147
|
try {
|
|
148
148
|
if (!done(await buildBaseline(projectDir, baselineApsorc, dataDir, cliBin)))
|
|
149
149
|
return { ok: false, steps };
|
|
150
|
-
if (!done(step("build", await build(projectDir))))
|
|
150
|
+
if (!skipBuild && !done(step("build", await build(projectDir))))
|
|
151
151
|
return { ok: false, steps };
|
|
152
152
|
const compiled = path
|
|
153
153
|
.join("dist", path.relative("src", migrationFile))
|
|
@@ -173,12 +173,15 @@ const freePort = () => new Promise((resolve, reject) => {
|
|
|
173
173
|
});
|
|
174
174
|
const get = (url) => new Promise((resolve) => {
|
|
175
175
|
http
|
|
176
|
-
.get(url, (res) => {
|
|
176
|
+
.get(url, { timeout: 15000 }, (res) => {
|
|
177
177
|
let body = "";
|
|
178
178
|
res.on("data", (d) => {
|
|
179
179
|
body += d;
|
|
180
180
|
});
|
|
181
181
|
res.on("end", () => { var _a; return resolve({ status: (_a = res.statusCode) !== null && _a !== void 0 ? _a : 0, body }); });
|
|
182
|
+
})
|
|
183
|
+
.on("timeout", function () {
|
|
184
|
+
this.destroy(new Error("No response within 15s"));
|
|
182
185
|
})
|
|
183
186
|
.on("error", (e) => resolve({ status: 0, body: e.message }));
|
|
184
187
|
});
|
|
@@ -188,7 +191,7 @@ const get = (url) => new Promise((resolve) => {
|
|
|
188
191
|
* ponytail: list only, no create/read/delete; add when payloads can be built
|
|
189
192
|
* from required fields and relationships.
|
|
190
193
|
*/
|
|
191
|
-
async function smokeTest(projectDir, entityNames, timeoutMs = 60000) {
|
|
194
|
+
async function smokeTest(projectDir, entityNames, { skipBuild = false, timeoutMs = 60000 } = {}) {
|
|
192
195
|
var _a, _b;
|
|
193
196
|
const dataDir = fs.mkdtempSync(path.join(os.tmpdir(), "apso-pglite-"));
|
|
194
197
|
const steps = [];
|
|
@@ -198,7 +201,7 @@ async function smokeTest(projectDir, entityNames, timeoutMs = 60000) {
|
|
|
198
201
|
};
|
|
199
202
|
let server;
|
|
200
203
|
try {
|
|
201
|
-
if (!done(step("build", await build(projectDir))))
|
|
204
|
+
if (!skipBuild && !done(step("build", await build(projectDir))))
|
|
202
205
|
return { ok: false, steps };
|
|
203
206
|
if (!done(step("schema", await typeorm(projectDir, ["schema:sync"], (0, exports.pgliteEnv)(dataDir)))))
|
|
204
207
|
return { ok: false, steps };
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@apso/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.41.1",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@apso/cli",
|
|
9
|
-
"version": "0.
|
|
9
|
+
"version": "0.41.1",
|
|
10
10
|
"license": "Apache-2.0",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@apso/schema-tools": "^0.2.0",
|
package/oclif.manifest.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "0.
|
|
2
|
+
"version": "0.41.1",
|
|
3
3
|
"commands": {
|
|
4
4
|
"config": {
|
|
5
5
|
"id": "config",
|
|
@@ -832,6 +832,12 @@
|
|
|
832
832
|
"required": true,
|
|
833
833
|
"multiple": false
|
|
834
834
|
},
|
|
835
|
+
"skip-build": {
|
|
836
|
+
"name": "skip-build",
|
|
837
|
+
"type": "boolean",
|
|
838
|
+
"description": "Use the existing dist/ build instead of building first",
|
|
839
|
+
"allowNo": false
|
|
840
|
+
},
|
|
835
841
|
"json": {
|
|
836
842
|
"name": "json",
|
|
837
843
|
"type": "boolean",
|
|
@@ -1002,6 +1008,12 @@
|
|
|
1002
1008
|
"$ apso test smoke"
|
|
1003
1009
|
],
|
|
1004
1010
|
"flags": {
|
|
1011
|
+
"skip-build": {
|
|
1012
|
+
"name": "skip-build",
|
|
1013
|
+
"type": "boolean",
|
|
1014
|
+
"description": "Use the existing dist/ build instead of building first",
|
|
1015
|
+
"allowNo": false
|
|
1016
|
+
},
|
|
1005
1017
|
"json": {
|
|
1006
1018
|
"name": "json",
|
|
1007
1019
|
"type": "boolean",
|