@objectstack/cli 2.0.1 → 2.0.3
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/.turbo/turbo-build.log +6 -6
- package/CHANGELOG.md +33 -0
- package/dist/bin.js +52 -27
- package/dist/index.js +50 -26
- package/package.json +11 -11
- package/src/commands/serve.ts +11 -0
- package/src/commands/test.ts +47 -17
- package/src/utils/studio.ts +26 -8
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @objectstack/cli@2.0.
|
|
2
|
+
> @objectstack/cli@2.0.3 build /home/runner/work/spec/spec/packages/cli
|
|
3
3
|
> tsup
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: src/bin.ts
|
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
[34mESM[39m Build start
|
|
16
16
|
[34mCLI[39m Cleaning output folder
|
|
17
17
|
[34mESM[39m Build start
|
|
18
|
-
[32mESM[39m [1mdist/
|
|
19
|
-
[32mESM[39m ⚡️ Build success in
|
|
20
|
-
[32mESM[39m [1mdist/
|
|
21
|
-
[32mESM[39m ⚡️ Build success in
|
|
18
|
+
[32mESM[39m [1mdist/bin.js [22m[32m61.55 KB[39m
|
|
19
|
+
[32mESM[39m ⚡️ Build success in 148ms
|
|
20
|
+
[32mESM[39m [1mdist/index.js [22m[32m58.94 KB[39m
|
|
21
|
+
[32mESM[39m ⚡️ Build success in 164ms
|
|
22
22
|
[34mDTS[39m Build start
|
|
23
|
-
[32mDTS[39m ⚡️ Build success in
|
|
23
|
+
[32mDTS[39m ⚡️ Build success in 7756ms
|
|
24
24
|
[32mDTS[39m [1mdist/index.d.ts [22m[32m2.93 KB[39m
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
1
|
# @objectstack/cli
|
|
2
2
|
|
|
3
|
+
## 2.0.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Patch release for maintenance and stability improvements
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @objectstack/spec@2.0.3
|
|
10
|
+
- @objectstack/core@2.0.3
|
|
11
|
+
- @objectstack/objectql@2.0.3
|
|
12
|
+
- @objectstack/runtime@2.0.3
|
|
13
|
+
- @objectstack/rest@2.0.3
|
|
14
|
+
- @objectstack/driver-memory@2.0.3
|
|
15
|
+
- @objectstack/plugin-hono-server@2.0.3
|
|
16
|
+
|
|
17
|
+
## 2.0.2
|
|
18
|
+
|
|
19
|
+
### Patch Changes
|
|
20
|
+
|
|
21
|
+
- 1db8559: chore: exclude generated json-schema from git tracking
|
|
22
|
+
|
|
23
|
+
- Add `packages/spec/json-schema/` to `.gitignore` (1277 generated files, 5MB)
|
|
24
|
+
- JSON schema files are still generated during `pnpm build` and included in npm publish via `files` field
|
|
25
|
+
- Fix studio module resolution logic for better compatibility
|
|
26
|
+
|
|
27
|
+
- Updated dependencies [1db8559]
|
|
28
|
+
- @objectstack/spec@2.0.2
|
|
29
|
+
- @objectstack/core@2.0.2
|
|
30
|
+
- @objectstack/objectql@2.0.2
|
|
31
|
+
- @objectstack/driver-memory@2.0.2
|
|
32
|
+
- @objectstack/plugin-hono-server@2.0.2
|
|
33
|
+
- @objectstack/rest@2.0.2
|
|
34
|
+
- @objectstack/runtime@2.0.2
|
|
35
|
+
|
|
3
36
|
## 2.0.1
|
|
4
37
|
|
|
5
38
|
### Patch Changes
|
package/dist/bin.js
CHANGED
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
3
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
4
|
-
}) : x)(function(x) {
|
|
5
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
6
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
7
|
-
});
|
|
8
2
|
|
|
9
3
|
// src/bin.ts
|
|
10
|
-
import { createRequire } from "module";
|
|
4
|
+
import { createRequire as createRequire2 } from "module";
|
|
11
5
|
import { Command as Command12 } from "commander";
|
|
12
6
|
import chalk13 from "chalk";
|
|
13
7
|
|
|
@@ -747,6 +741,8 @@ import { bundleRequire as bundleRequire2 } from "bundle-require";
|
|
|
747
741
|
// src/utils/studio.ts
|
|
748
742
|
import path6 from "path";
|
|
749
743
|
import fs6 from "fs";
|
|
744
|
+
import { createRequire } from "module";
|
|
745
|
+
import { pathToFileURL } from "url";
|
|
750
746
|
var STUDIO_PATH = "/_studio";
|
|
751
747
|
function resolveStudioPath() {
|
|
752
748
|
const cwd = process.cwd();
|
|
@@ -765,14 +761,25 @@ function resolveStudioPath() {
|
|
|
765
761
|
}
|
|
766
762
|
}
|
|
767
763
|
}
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
764
|
+
const resolutionBases = [
|
|
765
|
+
pathToFileURL(path6.join(cwd, "package.json")).href,
|
|
766
|
+
// consumer workspace
|
|
767
|
+
import.meta.url
|
|
768
|
+
// CLI package itself
|
|
769
|
+
];
|
|
770
|
+
for (const base of resolutionBases) {
|
|
771
|
+
try {
|
|
772
|
+
const req = createRequire(base);
|
|
773
|
+
const resolved = req.resolve("@objectstack/studio/package.json");
|
|
774
|
+
return path6.dirname(resolved);
|
|
775
|
+
} catch {
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
const directPath = path6.join(cwd, "node_modules", "@objectstack", "studio");
|
|
779
|
+
if (fs6.existsSync(path6.join(directPath, "package.json"))) {
|
|
780
|
+
return directPath;
|
|
775
781
|
}
|
|
782
|
+
return null;
|
|
776
783
|
}
|
|
777
784
|
function hasStudioDist(studioPath) {
|
|
778
785
|
return fs6.existsSync(path6.join(studioPath, "dist", "index.html"));
|
|
@@ -979,6 +986,13 @@ var serveCommand = new Command5("serve").description("Start ObjectStack server w
|
|
|
979
986
|
throw new Error(`Failed to import plugin '${plugin}': ${importError.message}`);
|
|
980
987
|
}
|
|
981
988
|
}
|
|
989
|
+
if (pluginToLoad && typeof pluginToLoad === "object" && !pluginToLoad.init) {
|
|
990
|
+
try {
|
|
991
|
+
const { AppPlugin } = await import("@objectstack/runtime");
|
|
992
|
+
pluginToLoad = new AppPlugin(pluginToLoad);
|
|
993
|
+
} catch (e) {
|
|
994
|
+
}
|
|
995
|
+
}
|
|
982
996
|
await kernel.use(pluginToLoad);
|
|
983
997
|
const pluginName = plugin.name || plugin.constructor?.name || "unnamed";
|
|
984
998
|
trackPlugin(pluginName);
|
|
@@ -1082,6 +1096,28 @@ import chalk8 from "chalk";
|
|
|
1082
1096
|
import path8 from "path";
|
|
1083
1097
|
import fs8 from "fs";
|
|
1084
1098
|
import { QA as CoreQA } from "@objectstack/core";
|
|
1099
|
+
function resolveGlob(pattern) {
|
|
1100
|
+
if (!pattern.includes("*")) {
|
|
1101
|
+
return fs8.existsSync(pattern) ? [pattern] : [];
|
|
1102
|
+
}
|
|
1103
|
+
const parts = pattern.split(path8.sep.replace("\\", "/"));
|
|
1104
|
+
const segments = pattern.includes("/") ? pattern.split("/") : parts;
|
|
1105
|
+
let baseDir = ".";
|
|
1106
|
+
let globStart = 0;
|
|
1107
|
+
for (let i = 0; i < segments.length; i++) {
|
|
1108
|
+
if (segments[i].includes("*")) {
|
|
1109
|
+
globStart = i;
|
|
1110
|
+
break;
|
|
1111
|
+
}
|
|
1112
|
+
baseDir = i === 0 ? segments[i] : path8.join(baseDir, segments[i]);
|
|
1113
|
+
}
|
|
1114
|
+
if (!fs8.existsSync(baseDir)) return [];
|
|
1115
|
+
const globPortion = segments.slice(globStart).join("/");
|
|
1116
|
+
const regexStr = globPortion.replace(/\./g, "\\.").replace(/\*\*\//g, "(.+/)?").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*");
|
|
1117
|
+
const regex = new RegExp(`^${regexStr}$`);
|
|
1118
|
+
const entries = fs8.readdirSync(baseDir, { recursive: true, encoding: "utf-8" });
|
|
1119
|
+
return entries.filter((entry) => regex.test(entry.replace(/\\/g, "/"))).map((entry) => path8.join(baseDir, entry)).filter((fullPath) => fs8.statSync(fullPath).isFile());
|
|
1120
|
+
}
|
|
1085
1121
|
var testCommand = new Command7("test").description("Run Quality Protocol test scenarios against a running server").argument("[files]", 'Glob pattern for test files (e.g. "qa/*.test.json")', "qa/*.test.json").option("--url <url>", "Target base URL", "http://localhost:3000").option("--token <token>", "Authentication token").action(async (filesPattern, options) => {
|
|
1086
1122
|
console.log(chalk8.bold(`
|
|
1087
1123
|
\u{1F9EA} ObjectStack Quality Protocol Runner`));
|
|
@@ -1089,18 +1125,7 @@ var testCommand = new Command7("test").description("Run Quality Protocol test sc
|
|
|
1089
1125
|
console.log(`Target: ${chalk8.blue(options.url)}`);
|
|
1090
1126
|
const adapter = new CoreQA.HttpTestAdapter(options.url, options.token);
|
|
1091
1127
|
const runner = new CoreQA.TestRunner(adapter);
|
|
1092
|
-
const
|
|
1093
|
-
const testFiles = [];
|
|
1094
|
-
if (fs8.existsSync(filesPattern)) {
|
|
1095
|
-
testFiles.push(filesPattern);
|
|
1096
|
-
} else {
|
|
1097
|
-
const dir = path8.dirname(filesPattern);
|
|
1098
|
-
const ext = path8.extname(filesPattern);
|
|
1099
|
-
if (fs8.existsSync(dir)) {
|
|
1100
|
-
const files = fs8.readdirSync(dir).filter((f) => f.endsWith(ext) || f.endsWith(".json"));
|
|
1101
|
-
files.forEach((f) => testFiles.push(path8.join(dir, f)));
|
|
1102
|
-
}
|
|
1103
|
-
}
|
|
1128
|
+
const testFiles = resolveGlob(filesPattern);
|
|
1104
1129
|
if (testFiles.length === 0) {
|
|
1105
1130
|
console.warn(chalk8.yellow(`No test files found matching: ${filesPattern}`));
|
|
1106
1131
|
return;
|
|
@@ -1850,7 +1875,7 @@ var generateCommand = new Command11("generate").alias("g").description("Generate
|
|
|
1850
1875
|
});
|
|
1851
1876
|
|
|
1852
1877
|
// src/bin.ts
|
|
1853
|
-
var require2 =
|
|
1878
|
+
var require2 = createRequire2(import.meta.url);
|
|
1854
1879
|
var pkg = require2("../package.json");
|
|
1855
1880
|
process.on("unhandledRejection", (err) => {
|
|
1856
1881
|
console.error(chalk13.red(`
|
package/dist/index.js
CHANGED
|
@@ -1,10 +1,3 @@
|
|
|
1
|
-
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
-
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
-
}) : x)(function(x) {
|
|
4
|
-
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
-
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
-
});
|
|
7
|
-
|
|
8
1
|
// src/commands/compile.ts
|
|
9
2
|
import { Command } from "commander";
|
|
10
3
|
import path2 from "path";
|
|
@@ -1300,6 +1293,8 @@ import { bundleRequire as bundleRequire2 } from "bundle-require";
|
|
|
1300
1293
|
// src/utils/studio.ts
|
|
1301
1294
|
import path7 from "path";
|
|
1302
1295
|
import fs7 from "fs";
|
|
1296
|
+
import { createRequire } from "module";
|
|
1297
|
+
import { pathToFileURL } from "url";
|
|
1303
1298
|
var STUDIO_PATH = "/_studio";
|
|
1304
1299
|
function resolveStudioPath() {
|
|
1305
1300
|
const cwd = process.cwd();
|
|
@@ -1318,14 +1313,25 @@ function resolveStudioPath() {
|
|
|
1318
1313
|
}
|
|
1319
1314
|
}
|
|
1320
1315
|
}
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1316
|
+
const resolutionBases = [
|
|
1317
|
+
pathToFileURL(path7.join(cwd, "package.json")).href,
|
|
1318
|
+
// consumer workspace
|
|
1319
|
+
import.meta.url
|
|
1320
|
+
// CLI package itself
|
|
1321
|
+
];
|
|
1322
|
+
for (const base of resolutionBases) {
|
|
1323
|
+
try {
|
|
1324
|
+
const req = createRequire(base);
|
|
1325
|
+
const resolved = req.resolve("@objectstack/studio/package.json");
|
|
1326
|
+
return path7.dirname(resolved);
|
|
1327
|
+
} catch {
|
|
1328
|
+
}
|
|
1329
|
+
}
|
|
1330
|
+
const directPath = path7.join(cwd, "node_modules", "@objectstack", "studio");
|
|
1331
|
+
if (fs7.existsSync(path7.join(directPath, "package.json"))) {
|
|
1332
|
+
return directPath;
|
|
1328
1333
|
}
|
|
1334
|
+
return null;
|
|
1329
1335
|
}
|
|
1330
1336
|
function hasStudioDist(studioPath) {
|
|
1331
1337
|
return fs7.existsSync(path7.join(studioPath, "dist", "index.html"));
|
|
@@ -1532,6 +1538,13 @@ var serveCommand = new Command8("serve").description("Start ObjectStack server w
|
|
|
1532
1538
|
throw new Error(`Failed to import plugin '${plugin}': ${importError.message}`);
|
|
1533
1539
|
}
|
|
1534
1540
|
}
|
|
1541
|
+
if (pluginToLoad && typeof pluginToLoad === "object" && !pluginToLoad.init) {
|
|
1542
|
+
try {
|
|
1543
|
+
const { AppPlugin } = await import("@objectstack/runtime");
|
|
1544
|
+
pluginToLoad = new AppPlugin(pluginToLoad);
|
|
1545
|
+
} catch (e) {
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1535
1548
|
await kernel.use(pluginToLoad);
|
|
1536
1549
|
const pluginName = plugin.name || plugin.constructor?.name || "unnamed";
|
|
1537
1550
|
trackPlugin(pluginName);
|
|
@@ -1610,6 +1623,28 @@ import chalk11 from "chalk";
|
|
|
1610
1623
|
import path9 from "path";
|
|
1611
1624
|
import fs9 from "fs";
|
|
1612
1625
|
import { QA as CoreQA } from "@objectstack/core";
|
|
1626
|
+
function resolveGlob(pattern) {
|
|
1627
|
+
if (!pattern.includes("*")) {
|
|
1628
|
+
return fs9.existsSync(pattern) ? [pattern] : [];
|
|
1629
|
+
}
|
|
1630
|
+
const parts = pattern.split(path9.sep.replace("\\", "/"));
|
|
1631
|
+
const segments = pattern.includes("/") ? pattern.split("/") : parts;
|
|
1632
|
+
let baseDir = ".";
|
|
1633
|
+
let globStart = 0;
|
|
1634
|
+
for (let i = 0; i < segments.length; i++) {
|
|
1635
|
+
if (segments[i].includes("*")) {
|
|
1636
|
+
globStart = i;
|
|
1637
|
+
break;
|
|
1638
|
+
}
|
|
1639
|
+
baseDir = i === 0 ? segments[i] : path9.join(baseDir, segments[i]);
|
|
1640
|
+
}
|
|
1641
|
+
if (!fs9.existsSync(baseDir)) return [];
|
|
1642
|
+
const globPortion = segments.slice(globStart).join("/");
|
|
1643
|
+
const regexStr = globPortion.replace(/\./g, "\\.").replace(/\*\*\//g, "(.+/)?").replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*");
|
|
1644
|
+
const regex = new RegExp(`^${regexStr}$`);
|
|
1645
|
+
const entries = fs9.readdirSync(baseDir, { recursive: true, encoding: "utf-8" });
|
|
1646
|
+
return entries.filter((entry) => regex.test(entry.replace(/\\/g, "/"))).map((entry) => path9.join(baseDir, entry)).filter((fullPath) => fs9.statSync(fullPath).isFile());
|
|
1647
|
+
}
|
|
1613
1648
|
var testCommand = new Command9("test").description("Run Quality Protocol test scenarios against a running server").argument("[files]", 'Glob pattern for test files (e.g. "qa/*.test.json")', "qa/*.test.json").option("--url <url>", "Target base URL", "http://localhost:3000").option("--token <token>", "Authentication token").action(async (filesPattern, options) => {
|
|
1614
1649
|
console.log(chalk11.bold(`
|
|
1615
1650
|
\u{1F9EA} ObjectStack Quality Protocol Runner`));
|
|
@@ -1617,18 +1652,7 @@ var testCommand = new Command9("test").description("Run Quality Protocol test sc
|
|
|
1617
1652
|
console.log(`Target: ${chalk11.blue(options.url)}`);
|
|
1618
1653
|
const adapter = new CoreQA.HttpTestAdapter(options.url, options.token);
|
|
1619
1654
|
const runner = new CoreQA.TestRunner(adapter);
|
|
1620
|
-
const
|
|
1621
|
-
const testFiles = [];
|
|
1622
|
-
if (fs9.existsSync(filesPattern)) {
|
|
1623
|
-
testFiles.push(filesPattern);
|
|
1624
|
-
} else {
|
|
1625
|
-
const dir = path9.dirname(filesPattern);
|
|
1626
|
-
const ext = path9.extname(filesPattern);
|
|
1627
|
-
if (fs9.existsSync(dir)) {
|
|
1628
|
-
const files = fs9.readdirSync(dir).filter((f) => f.endsWith(ext) || f.endsWith(".json"));
|
|
1629
|
-
files.forEach((f) => testFiles.push(path9.join(dir, f)));
|
|
1630
|
-
}
|
|
1631
|
-
}
|
|
1655
|
+
const testFiles = resolveGlob(filesPattern);
|
|
1632
1656
|
if (testFiles.length === 0) {
|
|
1633
1657
|
console.warn(chalk11.yellow(`No test files found matching: ${filesPattern}`));
|
|
1634
1658
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@objectstack/cli",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.3",
|
|
4
4
|
"description": "Command Line Interface for ObjectStack Protocol",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -21,20 +21,20 @@
|
|
|
21
21
|
"chalk": "^5.3.0",
|
|
22
22
|
"commander": "^14.0.3",
|
|
23
23
|
"tsx": "^4.7.1",
|
|
24
|
-
"zod": "^3.
|
|
25
|
-
"@objectstack/core": "2.0.
|
|
26
|
-
"@objectstack/driver-memory": "^2.0.
|
|
27
|
-
"@objectstack/objectql": "^2.0.
|
|
28
|
-
"@objectstack/plugin-hono-server": "2.0.
|
|
29
|
-
"@objectstack/rest": "2.0.
|
|
30
|
-
"@objectstack/runtime": "^2.0.
|
|
31
|
-
"@objectstack/spec": "2.0.
|
|
24
|
+
"zod": "^4.3.6",
|
|
25
|
+
"@objectstack/core": "2.0.3",
|
|
26
|
+
"@objectstack/driver-memory": "^2.0.3",
|
|
27
|
+
"@objectstack/objectql": "^2.0.3",
|
|
28
|
+
"@objectstack/plugin-hono-server": "2.0.3",
|
|
29
|
+
"@objectstack/rest": "2.0.3",
|
|
30
|
+
"@objectstack/runtime": "^2.0.3",
|
|
31
|
+
"@objectstack/spec": "2.0.3"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"@objectstack/core": "2.0.
|
|
34
|
+
"@objectstack/core": "2.0.3"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
|
-
"@types/node": "^25.
|
|
37
|
+
"@types/node": "^25.2.2",
|
|
38
38
|
"tsup": "^8.0.2",
|
|
39
39
|
"typescript": "^5.3.3",
|
|
40
40
|
"vitest": "^4.0.18"
|
package/src/commands/serve.ts
CHANGED
|
@@ -207,6 +207,17 @@ export const serveCommand = new Command('serve')
|
|
|
207
207
|
}
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
+
// Wrap raw config objects (no init/start) into AppPlugin
|
|
211
|
+
// This handles plugins defined as plain { name, objects, ... } bundles
|
|
212
|
+
if (pluginToLoad && typeof pluginToLoad === 'object' && !pluginToLoad.init) {
|
|
213
|
+
try {
|
|
214
|
+
const { AppPlugin } = await import('@objectstack/runtime');
|
|
215
|
+
pluginToLoad = new AppPlugin(pluginToLoad);
|
|
216
|
+
} catch (e: any) {
|
|
217
|
+
// Fall through to kernel.use which will report the error
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
210
221
|
await kernel.use(pluginToLoad);
|
|
211
222
|
const pluginName = plugin.name || plugin.constructor?.name || 'unnamed';
|
|
212
223
|
trackPlugin(pluginName);
|
package/src/commands/test.ts
CHANGED
|
@@ -7,6 +7,51 @@ import fs from 'fs';
|
|
|
7
7
|
import { QA as CoreQA } from '@objectstack/core';
|
|
8
8
|
import { QA } from '@objectstack/spec';
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* Resolve a glob-like pattern to matching file paths.
|
|
12
|
+
* Supports `*` (single segment wildcard) and `**` (recursive wildcard).
|
|
13
|
+
* Falls back to direct file path if no glob characters are present.
|
|
14
|
+
*/
|
|
15
|
+
function resolveGlob(pattern: string): string[] {
|
|
16
|
+
// Direct file path — no wildcards
|
|
17
|
+
if (!pattern.includes('*')) {
|
|
18
|
+
return fs.existsSync(pattern) ? [pattern] : [];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Split pattern into the static base directory and the glob portion
|
|
22
|
+
const parts = pattern.split(path.sep.replace('\\', '/'));
|
|
23
|
+
// Also handle forward-slash on Windows
|
|
24
|
+
const segments = pattern.includes('/') ? pattern.split('/') : parts;
|
|
25
|
+
|
|
26
|
+
let baseDir = '.';
|
|
27
|
+
let globStart = 0;
|
|
28
|
+
for (let i = 0; i < segments.length; i++) {
|
|
29
|
+
if (segments[i].includes('*')) {
|
|
30
|
+
globStart = i;
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
baseDir = i === 0 ? segments[i] : path.join(baseDir, segments[i]);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!fs.existsSync(baseDir)) return [];
|
|
37
|
+
|
|
38
|
+
// Convert the glob portion into a RegExp
|
|
39
|
+
const globPortion = segments.slice(globStart).join('/');
|
|
40
|
+
const regexStr = globPortion
|
|
41
|
+
.replace(/\./g, '\\.') // escape dots
|
|
42
|
+
.replace(/\*\*\//g, '(.+/)?') // ** matches any directory depth
|
|
43
|
+
.replace(/\*\*/g, '.*') // trailing ** without slash
|
|
44
|
+
.replace(/\*/g, '[^/]*'); // * matches within a single segment
|
|
45
|
+
const regex = new RegExp(`^${regexStr}$`);
|
|
46
|
+
|
|
47
|
+
// Recursively read all files under baseDir
|
|
48
|
+
const entries = fs.readdirSync(baseDir, { recursive: true, encoding: 'utf-8' }) as string[];
|
|
49
|
+
return entries
|
|
50
|
+
.filter(entry => regex.test(entry.replace(/\\/g, '/')))
|
|
51
|
+
.map(entry => path.join(baseDir, entry))
|
|
52
|
+
.filter(fullPath => fs.statSync(fullPath).isFile());
|
|
53
|
+
}
|
|
54
|
+
|
|
10
55
|
export const testCommand = new Command('test')
|
|
11
56
|
.description('Run Quality Protocol test scenarios against a running server')
|
|
12
57
|
.argument('[files]', 'Glob pattern for test files (e.g. "qa/*.test.json")', 'qa/*.test.json')
|
|
@@ -21,23 +66,8 @@ export const testCommand = new Command('test')
|
|
|
21
66
|
const adapter = new CoreQA.HttpTestAdapter(options.url, options.token);
|
|
22
67
|
const runner = new CoreQA.TestRunner(adapter);
|
|
23
68
|
|
|
24
|
-
// 2. Find
|
|
25
|
-
|
|
26
|
-
const cwd = process.cwd();
|
|
27
|
-
const testFiles: string[] = [];
|
|
28
|
-
|
|
29
|
-
// Very basic file finding for demo - assume explicit path or check local dir
|
|
30
|
-
if (fs.existsSync(filesPattern)) {
|
|
31
|
-
testFiles.push(filesPattern);
|
|
32
|
-
} else {
|
|
33
|
-
// Simple directory scan
|
|
34
|
-
const dir = path.dirname(filesPattern);
|
|
35
|
-
const ext = path.extname(filesPattern);
|
|
36
|
-
if (fs.existsSync(dir)) {
|
|
37
|
-
const files = fs.readdirSync(dir).filter(f => f.endsWith(ext) || f.endsWith('.json'));
|
|
38
|
-
files.forEach(f => testFiles.push(path.join(dir, f)));
|
|
39
|
-
}
|
|
40
|
-
}
|
|
69
|
+
// 2. Find test files using glob-style pattern matching
|
|
70
|
+
const testFiles: string[] = resolveGlob(filesPattern);
|
|
41
71
|
|
|
42
72
|
if (testFiles.length === 0) {
|
|
43
73
|
console.warn(chalk.yellow(`No test files found matching: ${filesPattern}`));
|
package/src/utils/studio.ts
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
import path from 'path';
|
|
10
10
|
import fs from 'fs';
|
|
11
11
|
import net from 'net';
|
|
12
|
+
import { createRequire } from 'module';
|
|
13
|
+
import { pathToFileURL } from 'url';
|
|
12
14
|
import { spawn, type ChildProcess } from 'child_process';
|
|
13
15
|
import chalk from 'chalk';
|
|
14
16
|
|
|
@@ -48,15 +50,31 @@ export function resolveStudioPath(): string | null {
|
|
|
48
50
|
}
|
|
49
51
|
}
|
|
50
52
|
|
|
51
|
-
// Fallback: resolve from node_modules
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
53
|
+
// Fallback: resolve from node_modules via createRequire.
|
|
54
|
+
// Try the consumer's cwd first (pnpm strict isolation means the CLI's own
|
|
55
|
+
// import.meta.url cannot see the consumer's dependencies), then the CLI itself.
|
|
56
|
+
const resolutionBases = [
|
|
57
|
+
pathToFileURL(path.join(cwd, 'package.json')).href, // consumer workspace
|
|
58
|
+
import.meta.url, // CLI package itself
|
|
59
|
+
];
|
|
60
|
+
|
|
61
|
+
for (const base of resolutionBases) {
|
|
62
|
+
try {
|
|
63
|
+
const req = createRequire(base);
|
|
64
|
+
const resolved = req.resolve('@objectstack/studio/package.json');
|
|
65
|
+
return path.dirname(resolved);
|
|
66
|
+
} catch {
|
|
67
|
+
// Not resolvable from this base — try next
|
|
68
|
+
}
|
|
59
69
|
}
|
|
70
|
+
|
|
71
|
+
// Last resort: direct filesystem check in cwd/node_modules
|
|
72
|
+
const directPath = path.join(cwd, 'node_modules', '@objectstack', 'studio');
|
|
73
|
+
if (fs.existsSync(path.join(directPath, 'package.json'))) {
|
|
74
|
+
return directPath;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return null;
|
|
60
78
|
}
|
|
61
79
|
|
|
62
80
|
/**
|