@speedkit/cli 4.24.0 → 4.24.2

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 CHANGED
@@ -1,3 +1,18 @@
1
+ ## [4.24.2](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v4.24.1...v4.24.2) (2026-09-03)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * **onboarding:** match the document handler's own dependency ranges ([414bd14](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/414bd14584778c0e7b1a3f556c311cd997380df7))
7
+ * **onboarding:** resolve document handler modules at runtime ([44aa76e](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/44aa76ef1b7bf846e47b0be7f6e11b850d7dd0e6))
8
+
9
+ ## [4.24.1](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v4.24.0...v4.24.1) (2026-09-02)
10
+
11
+
12
+ ### Bug Fixes
13
+
14
+ * **onboarding:** install config dependencies into the workspace ([20a091e](https://gitlab.orestes.info/baqend/speed-kit-cli/commit/20a091e4b5deff21559363ec4baa960f0c350ec3))
15
+
1
16
  # [4.24.0](https://gitlab.orestes.info/baqend/speed-kit-cli/compare/v4.23.3...v4.24.0) (2026-08-28)
2
17
 
3
18
 
package/README.md CHANGED
@@ -21,7 +21,7 @@ $ npm install -g @speedkit/cli
21
21
  $ sk COMMAND
22
22
  running command...
23
23
  $ sk (--version)
24
- @speedkit/cli/4.24.0 linux-x64 node-v22.23.2
24
+ @speedkit/cli/4.24.2 linux-x64 node-v22.23.2
25
25
  $ sk --help [COMMAND]
26
26
  USAGE
27
27
  $ sk COMMAND
@@ -1,7 +1,6 @@
1
1
  import { BundleService } from "./bundle-service.js";
2
2
  export declare class BundleServiceFactory {
3
3
  private customerFolder?;
4
- private additionalModulePaths;
5
- constructor(customerFolder?: string, additionalModulePaths?: string[]);
4
+ constructor(customerFolder?: string);
6
5
  buildService(): BundleService;
7
6
  }
@@ -1,12 +1,10 @@
1
1
  import { BundleService } from "./bundle-service.js";
2
2
  export class BundleServiceFactory {
3
3
  customerFolder;
4
- additionalModulePaths;
5
- constructor(customerFolder, additionalModulePaths = []) {
4
+ constructor(customerFolder) {
6
5
  this.customerFolder = customerFolder;
7
- this.additionalModulePaths = additionalModulePaths;
8
6
  }
9
7
  buildService() {
10
- return new BundleService(this.customerFolder, this.additionalModulePaths);
8
+ return new BundleService(this.customerFolder);
11
9
  }
12
10
  }
@@ -8,15 +8,11 @@ export type BundleBasePaths = Record<string, string>;
8
8
  export type BundleResolveFallback = (specifier: string) => Promise<string | null>;
9
9
  export declare class BundleService {
10
10
  private moduleDiscoveryDir;
11
- private additionalModulePaths;
12
11
  /**
13
12
  * @param moduleDiscoveryDir the directory bundled code resolves its own imports from
14
- * @param additionalModulePaths extra `node_modules` directories to search, like `NODE_PATH`.
15
- * `sk` installs the packages a customer config declares under `dependencies` into its cache
16
- * rather than into the customer folder, so esbuild would otherwise never find them.
17
13
  */
18
- constructor(moduleDiscoveryDir: string, additionalModulePaths?: string[]);
19
- bundle({ entryPoints, basePaths, define, target, minify, logLevel, alias, platform, resolveFallback, }: {
14
+ constructor(moduleDiscoveryDir: string);
15
+ bundle({ entryPoints, basePaths, define, target, minify, logLevel, alias, platform, resolveFallback, packages, }: {
20
16
  entryPoints: BundleEntryPoints;
21
17
  basePaths?: BundleBasePaths;
22
18
  target?: string | string[];
@@ -26,25 +22,8 @@ export declare class BundleService {
26
22
  logLevel?: LogLevel;
27
23
  platform?: "browser" | "node" | "neutral";
28
24
  resolveFallback?: BundleResolveFallback;
25
+ packages?: "bundle" | "external";
29
26
  }): Promise<string>;
30
- /**
31
- * Turns each alias target into an absolute path, resolved from the same directory the bundled
32
- * code resolves its own imports from.
33
- *
34
- * esbuild resolves an alias target from its working directory, not from the entry point's
35
- * resolveDir. `{ "node-fetch": "node-fetch-native" }` therefore only resolved while `sk` ran
36
- * inside the customer checkout, and failed from anywhere else — even though the bundled code
37
- * could import the very same module by name.
38
- *
39
- * A target that is not installed stays a bare name, so esbuild reports it as before.
40
- */
41
- private resolveAliases;
42
- /**
43
- * Looks the target up the way the bundled code would: the customer folder first, then the
44
- * additional module paths. Returns `null` when it is installed nowhere, so the bare name reaches
45
- * esbuild and is reported as before.
46
- */
47
- private resolveAliasTarget;
48
27
  private entryPointsPlugin;
49
28
  baqendPlugin(basePaths: BundleBasePaths): {
50
29
  name: string;
@@ -4,24 +4,18 @@ import { URL } from "node:url";
4
4
  import { BundleResolveError } from "./error/bundle-resolve-error.js";
5
5
  import { BundleFetchError } from "./error/bundle-fetch-error.js";
6
6
  import { resolve } from "node:path";
7
- import { createRequire } from "node:module";
8
7
  const FALLBACK_NAMESPACE = "bundle-resolve-fallback";
9
8
  const RELATIVE_SPECIFIER = /^\.{1,2}\//;
10
9
  const importCache = {};
11
10
  export class BundleService {
12
11
  moduleDiscoveryDir;
13
- additionalModulePaths;
14
12
  /**
15
13
  * @param moduleDiscoveryDir the directory bundled code resolves its own imports from
16
- * @param additionalModulePaths extra `node_modules` directories to search, like `NODE_PATH`.
17
- * `sk` installs the packages a customer config declares under `dependencies` into its cache
18
- * rather than into the customer folder, so esbuild would otherwise never find them.
19
14
  */
20
- constructor(moduleDiscoveryDir, additionalModulePaths = []) {
15
+ constructor(moduleDiscoveryDir) {
21
16
  this.moduleDiscoveryDir = moduleDiscoveryDir;
22
- this.additionalModulePaths = additionalModulePaths;
23
17
  }
24
- async bundle({ entryPoints, basePaths, define, target = "es5", minify = true, logLevel = "silent", alias = {}, platform, resolveFallback, }) {
18
+ async bundle({ entryPoints, basePaths, define, target = "es5", minify = true, logLevel = "silent", alias = {}, platform, resolveFallback, packages = "bundle", }) {
25
19
  const plugins = [];
26
20
  if (basePaths && Object.keys(basePaths).length > 0) {
27
21
  plugins.push(this.baqendPlugin(basePaths));
@@ -43,58 +37,14 @@ export class BundleService {
43
37
  entryPoints: Object.keys(entryPoints),
44
38
  platform: platform || "neutral",
45
39
  logLevel,
46
- nodePaths: this.additionalModulePaths,
47
- alias: this.resolveAliases(alias),
40
+ packages,
41
+ alias,
48
42
  });
49
43
  return result.outputFiles
50
44
  .map((outputFile) => outputFile.text)
51
45
  .join("")
52
46
  .trim();
53
47
  }
54
- /**
55
- * Turns each alias target into an absolute path, resolved from the same directory the bundled
56
- * code resolves its own imports from.
57
- *
58
- * esbuild resolves an alias target from its working directory, not from the entry point's
59
- * resolveDir. `{ "node-fetch": "node-fetch-native" }` therefore only resolved while `sk` ran
60
- * inside the customer checkout, and failed from anywhere else — even though the bundled code
61
- * could import the very same module by name.
62
- *
63
- * A target that is not installed stays a bare name, so esbuild reports it as before.
64
- */
65
- resolveAliases(alias) {
66
- if (Object.keys(alias).length === 0) {
67
- return alias;
68
- }
69
- // The filename never has to exist; only its directory decides where the lookup starts.
70
- const requireFrom = createRequire(resolve(this.moduleDiscoveryDir, "noop.js"));
71
- return Object.fromEntries(Object.entries(alias).map(([from, to]) => [
72
- from,
73
- this.resolveAliasTarget(requireFrom, to) ?? to,
74
- ]));
75
- }
76
- /**
77
- * Looks the target up the way the bundled code would: the customer folder first, then the
78
- * additional module paths. Returns `null` when it is installed nowhere, so the bare name reaches
79
- * esbuild and is reported as before.
80
- */
81
- resolveAliasTarget(requireFrom, target) {
82
- try {
83
- return requireFrom.resolve(target);
84
- }
85
- catch {
86
- // not in the customer folder — fall through to the additional module paths
87
- }
88
- for (const modulePath of this.additionalModulePaths) {
89
- try {
90
- return createRequire(resolve(modulePath, "noop.js")).resolve(target);
91
- }
92
- catch {
93
- // keep looking
94
- }
95
- }
96
- return null;
97
- }
98
48
  entryPointsPlugin(entryPoints) {
99
49
  // eslint-disable-next-line @typescript-eslint/no-this-alias
100
50
  const me = this;
@@ -1,62 +1,9 @@
1
1
  import { expect } from "chai";
2
2
  import { after, before, describe, it } from "mocha";
3
- import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
3
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
4
4
  import { tmpdir } from "node:os";
5
5
  import { join } from "node:path";
6
6
  import { BundleService } from "./bundle-service.js";
7
- /**
8
- * The customer checkout installs the modules a document handler imports, so resolution must follow
9
- * the customer directory rather than the directory `sk` happens to run in. The fixture below is
10
- * only reachable from the customer directory, which is what makes the assertion meaningful.
11
- */
12
- describe("BundleService alias resolution", () => {
13
- const MARKER = "fixture-fetch-implementation";
14
- let customerDirectory;
15
- before(() => {
16
- customerDirectory = mkdtempSync(join(tmpdir(), "sk-bundle-"));
17
- const packageDirectory = join(customerDirectory, "node_modules", "fetch-native-fixture");
18
- mkdirSync(packageDirectory, { recursive: true });
19
- writeFileSync(join(packageDirectory, "package.json"), JSON.stringify({
20
- name: "fetch-native-fixture",
21
- version: "1.0.0",
22
- main: "index.js",
23
- }));
24
- writeFileSync(join(packageDirectory, "index.js"), `module.exports = "${MARKER}";`);
25
- });
26
- after(() => {
27
- rmSync(customerDirectory, { recursive: true, force: true });
28
- });
29
- it("resolves an alias target from the customer directory, not from the working directory", async () => {
30
- const bundle = await new BundleService(customerDirectory).bundle({
31
- entryPoints: {
32
- "handler.js": "module.exports = require('node-fetch');",
33
- },
34
- minify: false,
35
- platform: "node",
36
- logLevel: "silent",
37
- alias: { "node-fetch": "fetch-native-fixture" },
38
- });
39
- expect(bundle).to.contain(MARKER);
40
- });
41
- it("leaves an uninstalled alias target to esbuild", async () => {
42
- const bundling = new BundleService(customerDirectory).bundle({
43
- entryPoints: {
44
- "handler.js": "module.exports = require('node-fetch');",
45
- },
46
- minify: false,
47
- platform: "node",
48
- logLevel: "silent",
49
- alias: { "node-fetch": "not-installed-anywhere" },
50
- });
51
- const error = await bundling.then(() => undefined, (reason) => reason);
52
- expect(String(error)).to.contain("not-installed-anywhere");
53
- });
54
- });
55
- /**
56
- * A document handler is shared with the Baqend app it is deployed to, so it can require a module
57
- * that only exists there. The bundler must reach for that copy only after the customer folder has
58
- * had its chance, and only for the relative specifiers Baqend modules are required by.
59
- */
60
7
  describe("BundleService resolve fallback", () => {
61
8
  const bundleWithFallback = (source, fallback, moduleDiscoveryDir = process.cwd()) => new BundleService(moduleDiscoveryDir).bundle({
62
9
  entryPoints: { "handler.js": source },
@@ -111,68 +58,55 @@ describe("BundleService resolve fallback", () => {
111
58
  });
112
59
  });
113
60
  /**
114
- * `sk` installs the packages a customer config declares under `dependencies` into its own cache,
115
- * not into the customer folder, so the bundler needs that directory on its search path as well.
61
+ * A document handler imports packages that must not be bundled a native addon locates its own
62
+ * binary through `import.meta.url`, which an esbuild CJS bundle leaves undefined. Only bare
63
+ * specifiers become runtime requires; the customer's own files still have to be bundled.
116
64
  */
117
- describe("BundleService additional module paths", () => {
118
- const MARKER = "fixture-additional-dependency";
119
- let cacheDirectory;
65
+ describe("BundleService external packages", () => {
66
+ const MARKER = "fixture-relative-module";
120
67
  let customerDirectory;
121
- let additionalModulePath;
122
68
  before(() => {
123
- cacheDirectory = mkdtempSync(join(tmpdir(), "sk-cache-"));
124
- customerDirectory = mkdtempSync(join(tmpdir(), "sk-customer-"));
125
- additionalModulePath = join(cacheDirectory, "node_modules");
126
- const packageDirectory = join(additionalModulePath, "additional-fixture");
127
- mkdirSync(packageDirectory, { recursive: true });
128
- writeFileSync(join(packageDirectory, "package.json"), JSON.stringify({
129
- name: "additional-fixture",
130
- version: "1.0.0",
131
- main: "index.js",
132
- }));
133
- writeFileSync(join(packageDirectory, "index.js"), `module.exports = "${MARKER}";`);
69
+ customerDirectory = mkdtempSync(join(tmpdir(), "sk-external-"));
70
+ writeFileSync(join(customerDirectory, "helper.js"), `module.exports = "${MARKER}";`);
134
71
  });
135
72
  after(() => {
136
- rmSync(cacheDirectory, { recursive: true, force: true });
137
73
  rmSync(customerDirectory, { recursive: true, force: true });
138
74
  });
139
- it("resolves a package installed outside the customer folder", async () => {
140
- const bundle = await new BundleService(customerDirectory, [
141
- additionalModulePath,
142
- ]).bundle({
75
+ it("leaves a package as a runtime require", async () => {
76
+ const bundle = await new BundleService(customerDirectory).bundle({
143
77
  entryPoints: {
144
- "handler.js": "module.exports = require('additional-fixture');",
78
+ "handler.js": "module.exports = require('parse5');",
145
79
  },
146
80
  minify: false,
147
81
  platform: "node",
148
82
  logLevel: "silent",
83
+ packages: "external",
149
84
  });
150
- expect(bundle).to.contain(MARKER);
85
+ expect(bundle).to.contain('require("parse5")');
151
86
  });
152
- it("fails without the additional path, as it did before", async () => {
153
- const bundling = new BundleService(customerDirectory).bundle({
87
+ it("still bundles a relative import", async () => {
88
+ const bundle = await new BundleService(customerDirectory).bundle({
154
89
  entryPoints: {
155
- "handler.js": "module.exports = require('additional-fixture');",
90
+ "handler.js": "module.exports = require('./helper.js');",
156
91
  },
157
92
  minify: false,
158
93
  platform: "node",
159
94
  logLevel: "silent",
95
+ packages: "external",
160
96
  });
161
- const error = await bundling.then(() => undefined, (reason) => reason);
162
- expect(String(error)).to.contain("additional-fixture");
97
+ expect(bundle).to.contain(MARKER);
163
98
  });
164
- it("resolves an alias target from an additional module path", async () => {
165
- const bundle = await new BundleService(customerDirectory, [
166
- additionalModulePath,
167
- ]).bundle({
99
+ it("applies an alias and keeps the target a bare specifier", async () => {
100
+ const bundle = await new BundleService(customerDirectory).bundle({
168
101
  entryPoints: {
169
102
  "handler.js": "module.exports = require('node-fetch');",
170
103
  },
171
104
  minify: false,
172
105
  platform: "node",
173
106
  logLevel: "silent",
174
- alias: { "node-fetch": "additional-fixture" },
107
+ alias: { "node-fetch": "node-fetch-native" },
108
+ packages: "external",
175
109
  });
176
- expect(bundle).to.contain(MARKER);
110
+ expect(bundle).to.contain('require("node-fetch-native")');
177
111
  });
178
112
  });
@@ -8,6 +8,7 @@ export declare class DocumentHandlerServer {
8
8
  private files;
9
9
  private bundler;
10
10
  private cli;
11
+ private workSpace;
11
12
  private verboseLevel;
12
13
  private entityManager?;
13
14
  private documentHandlerCode;
@@ -15,7 +16,11 @@ export declare class DocumentHandlerServer {
15
16
  private database;
16
17
  /** Sources of app modules already looked up; `null` marks a lookup that came back empty. */
17
18
  private readonly appModules;
18
- constructor(customerConfig: CustomerConfig, files: FileListInterface, bundler: BundleService, cli: CliService, verboseLevel?: boolean, entityManager?: EntityManager);
19
+ private moduleResolver;
20
+ /**
21
+ * @param workSpace - Directory the packages a customer config declares are installed into.
22
+ */
23
+ constructor(customerConfig: CustomerConfig, files: FileListInterface, bundler: BundleService, cli: CliService, workSpace: string, verboseLevel?: boolean, entityManager?: EntityManager);
19
24
  transform(contentType: string, html: string, variation: string, url: string, headers: Record<string, string>): Promise<DocumentHandlerResponse>;
20
25
  private getTransformFunctionWrapper;
21
26
  buildDocumentHandler(): Promise<void>;
@@ -43,6 +48,18 @@ export declare class DocumentHandlerServer {
43
48
  */
44
49
  bundleDynamicFetcher(basePaths: Record<string, string>, dynamicFetcher: string): Promise<string>;
45
50
  bundleDocumentHandlerCode(name: string, code: string): Promise<string>;
51
+ /**
52
+ * Resolves the packages the bundle leaves as runtime requires.
53
+ *
54
+ * Nothing a document handler imports by name is bundled, because a bundler cannot serve them
55
+ * all: a native addon such as `node-av` locates its own `.node` file through `import.meta.url`,
56
+ * which an esbuild CJS bundle leaves undefined. Requiring at runtime also matches production,
57
+ * where the handler runs against real `node_modules`.
58
+ *
59
+ * The workspace is searched before the CLI's own dependencies, so a config that declares a
60
+ * package under `dependencies` also decides its version.
61
+ */
62
+ private getModuleResolver;
46
63
  /**
47
64
  * Loads a module the customer folder does not contain from the app the config is deployed to.
48
65
  *
@@ -12,12 +12,14 @@ import { safe } from "../../helpers/safe.js";
12
12
  import { VmEmptyResponseError } from "../onboarding/error/vm-empty-response-error.js";
13
13
  import { DocumentHandlerTransformError } from "../onboarding/error/document-handler-transform-error.js";
14
14
  import { createRequire } from "node:module";
15
+ import { resolve } from "node:path";
15
16
  import ApplicationError from "../error-handling/error/application-error.js";
16
17
  export class DocumentHandlerServer {
17
18
  customerConfig;
18
19
  files;
19
20
  bundler;
20
21
  cli;
22
+ workSpace;
21
23
  verboseLevel;
22
24
  entityManager;
23
25
  documentHandlerCode;
@@ -25,11 +27,16 @@ export class DocumentHandlerServer {
25
27
  database;
26
28
  /** Sources of app modules already looked up; `null` marks a lookup that came back empty. */
27
29
  appModules = new Map();
28
- constructor(customerConfig, files, bundler, cli, verboseLevel = false, entityManager) {
30
+ moduleResolver;
31
+ /**
32
+ * @param workSpace - Directory the packages a customer config declares are installed into.
33
+ */
34
+ constructor(customerConfig, files, bundler, cli, workSpace, verboseLevel = false, entityManager) {
29
35
  this.customerConfig = customerConfig;
30
36
  this.files = files;
31
37
  this.bundler = bundler;
32
38
  this.cli = cli;
39
+ this.workSpace = workSpace;
33
40
  this.verboseLevel = verboseLevel;
34
41
  this.entityManager = entityManager;
35
42
  }
@@ -172,7 +179,7 @@ export class DocumentHandlerServer {
172
179
  createNodeVmContext() {
173
180
  const context = {
174
181
  process: this.createProcessPartial(),
175
- require: createRequire(import.meta.url),
182
+ require: this.getModuleResolver(),
176
183
  URL,
177
184
  URLSearchParams,
178
185
  fetch: this.createFetchMock(),
@@ -248,6 +255,8 @@ export class DocumentHandlerServer {
248
255
  platform: "node",
249
256
  logLevel: "silent",
250
257
  alias: { "node-fetch": "node-fetch-native" },
258
+ // Every package stays a runtime require, see getModuleResolver().
259
+ packages: "external",
251
260
  resolveFallback: this.loadAppModule,
252
261
  }));
253
262
  if (bundling.success === true) {
@@ -255,6 +264,42 @@ export class DocumentHandlerServer {
255
264
  }
256
265
  throw this.explainUnresolvedModules(bundling.errorObj ?? new Error(bundling.error));
257
266
  }
267
+ /**
268
+ * Resolves the packages the bundle leaves as runtime requires.
269
+ *
270
+ * Nothing a document handler imports by name is bundled, because a bundler cannot serve them
271
+ * all: a native addon such as `node-av` locates its own `.node` file through `import.meta.url`,
272
+ * which an esbuild CJS bundle leaves undefined. Requiring at runtime also matches production,
273
+ * where the handler runs against real `node_modules`.
274
+ *
275
+ * The workspace is searched before the CLI's own dependencies, so a config that declares a
276
+ * package under `dependencies` also decides its version.
277
+ */
278
+ getModuleResolver() {
279
+ if (this.moduleResolver) {
280
+ return this.moduleResolver;
281
+ }
282
+ // The filename never has to exist; only its directory decides where the lookup starts.
283
+ const requireFromWorkSpace = createRequire(resolve(this.workSpace, "noop.js"));
284
+ const requireFromCli = createRequire(import.meta.url);
285
+ // Resolve before requiring, so a module that exists but throws while it initialises
286
+ // reports that failure rather than falling through to the next candidate.
287
+ this.moduleResolver = (module) => {
288
+ const inWorkSpace = safe(() => requireFromWorkSpace.resolve(module));
289
+ if (inWorkSpace.success === true) {
290
+ return requireFromWorkSpace(inWorkSpace.data);
291
+ }
292
+ const withCli = safe(() => requireFromCli.resolve(module));
293
+ if (withCli.success === true) {
294
+ return requireFromCli(withCli.data);
295
+ }
296
+ throw new ApplicationError(`[documentHandler] could not load module: ${module}`, [
297
+ `"${module}" ships neither with the CLI nor with this customer config.`,
298
+ `Add it to config_customer.json under "dependencies" and start sk again.`,
299
+ ]);
300
+ };
301
+ return this.moduleResolver;
302
+ }
258
303
  /**
259
304
  * Loads a module the customer folder does not contain from the app the config is deployed to.
260
305
  *
@@ -18,5 +18,16 @@ export default class DocumentHandlerRuntimeServiceFactory {
18
18
  private preparePackageJson;
19
19
  private prepareTestCases;
20
20
  private getCustomerConfig;
21
+ /**
22
+ * Gives the workspace its own manifest before anything is installed into it.
23
+ *
24
+ * npm resolves the target package by walking up from its working directory, so an
25
+ * empty workspace installs into the nearest ancestor package instead — on a developer
26
+ * machine that is often the home directory. The module then resolves only by accident,
27
+ * through node's upward lookup, and not at all on a machine without that ancestor.
28
+ *
29
+ * @param workSpace - Directory the additional dependencies are installed into.
30
+ */
31
+ private prepareWorkSpaceManifest;
21
32
  private installAdditionalDependencies;
22
33
  }
@@ -7,7 +7,7 @@ import { DocumentHandlerServer } from "../document-handler-server.js";
7
7
  import { BundleServiceFactory } from "../../bundler/index.js";
8
8
  import { EntityManagerFactory } from "../../config-api/entity-manager-factory.js";
9
9
  import { safe } from "../../../helpers/safe.js";
10
- import { existsSync, mkdirSync } from "node:fs";
10
+ import { existsSync, mkdirSync, writeFileSync } from "node:fs";
11
11
  import { resolve } from "node:path";
12
12
  export default class DocumentHandlerRuntimeServiceFactory {
13
13
  context;
@@ -36,13 +36,9 @@ export default class DocumentHandlerRuntimeServiceFactory {
36
36
  const customerConfig = this.getCustomerConfig(files);
37
37
  const cliService = new CliServiceFactory().getService();
38
38
  await this.installAdditionalDependencies(customerConfig, cliService);
39
- // installAdditionalDependencies() installs into the cache, not into the customer folder, so
40
- // the bundler has to search there too or a declared dependency resolves nowhere.
41
- const bundler = new BundleServiceFactory(this.context.customerPath, [
42
- resolve(this.cliConfig.nodeModulesPath, "node_modules"),
43
- ]).buildService();
39
+ const bundler = new BundleServiceFactory(this.context.customerPath).buildService();
44
40
  const entityManagerResult = await safe(new EntityManagerFactory().getEntityManager(customerConfig.app));
45
- return new DocumentHandlerServer(customerConfig, files, bundler, cliService, this.context.verboseLevel, entityManagerResult.success ? entityManagerResult.data : null);
41
+ return new DocumentHandlerServer(customerConfig, files, bundler, cliService, this.cliConfig.nodeModulesPath, this.context.verboseLevel, entityManagerResult.success ? entityManagerResult.data : null);
46
42
  }
47
43
  prepareIntegrationApi() {
48
44
  const integrationApiContext = new IntegrationApiContext(this.context.customerPath, this.context.configName, this.context.fileDependencies, [".git", ".idea", "node_modules"]);
@@ -104,6 +100,27 @@ export default class DocumentHandlerRuntimeServiceFactory {
104
100
  const configFile = files.getByName("config_customer");
105
101
  return configFile.config;
106
102
  }
103
+ /**
104
+ * Gives the workspace its own manifest before anything is installed into it.
105
+ *
106
+ * npm resolves the target package by walking up from its working directory, so an
107
+ * empty workspace installs into the nearest ancestor package instead — on a developer
108
+ * machine that is often the home directory. The module then resolves only by accident,
109
+ * through node's upward lookup, and not at all on a machine without that ancestor.
110
+ *
111
+ * @param workSpace - Directory the additional dependencies are installed into.
112
+ */
113
+ prepareWorkSpaceManifest(workSpace) {
114
+ const manifest = resolve(workSpace, "package.json");
115
+ if (existsSync(manifest)) {
116
+ return;
117
+ }
118
+ writeFileSync(manifest, `${JSON.stringify({
119
+ name: "speed-kit-additional-dependencies",
120
+ version: "0.0.0",
121
+ private: true,
122
+ }, undefined, 2)}\n`);
123
+ }
107
124
  async installAdditionalDependencies(customerConfig, cli) {
108
125
  const workSpace = this.cliConfig.nodeModulesPath;
109
126
  if (!customerConfig.dependencies ||
@@ -114,6 +131,7 @@ export default class DocumentHandlerRuntimeServiceFactory {
114
131
  if (!existsSync(workSpace)) {
115
132
  mkdirSync(workSpace, { recursive: true });
116
133
  }
134
+ this.prepareWorkSpaceManifest(workSpace);
117
135
  cli.write("install additional dependencies");
118
136
  for (const packageName in customerConfig.dependencies) {
119
137
  const version = customerConfig.dependencies[packageName];
@@ -1220,5 +1220,5 @@
1220
1220
  ]
1221
1221
  }
1222
1222
  },
1223
- "version": "4.24.0"
1223
+ "version": "4.24.2"
1224
1224
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@speedkit/cli",
3
3
  "description": "Speed Kit CLI",
4
- "version": "4.24.0",
4
+ "version": "4.24.2",
5
5
  "author": {
6
6
  "name": "Baqend.com",
7
7
  "email": "info@baqend.com"
@@ -91,6 +91,8 @@
91
91
  "@eslint/eslintrc": "^3.3.5",
92
92
  "@eslint/js": "^10.0.1",
93
93
  "@inquirer/prompts": "^8.4.2",
94
+ "@nats-io/nats-core": "^3.2.0",
95
+ "@nats-io/transport-node": "^3.2.0",
94
96
  "@oclif/core": "^4.10.6",
95
97
  "@oclif/errors": "^1.3.6",
96
98
  "@oclif/plugin-autocomplete": "^3.2.46",
@@ -102,8 +104,13 @@
102
104
  "chokidar": "^5.0.0",
103
105
  "cli-progress": "^3.12.0",
104
106
  "clipboardy": "^5.3.1",
107
+ "css-select": "^5.1.0",
105
108
  "deepmerge": "^4.3.1",
106
109
  "diff": "^9.0.0",
110
+ "dom-serializer": "^2.0.0",
111
+ "domelementtype": "^2.3.0",
112
+ "domhandler": "^5.0.3",
113
+ "domutils": "^3.1.0",
107
114
  "dotenv": "^17.4.2",
108
115
  "encoding-japanese": "^2.2.0",
109
116
  "esbuild": "^0.27.3",
@@ -115,8 +122,11 @@
115
122
  "iconv-lite": "^0.7.2",
116
123
  "json2csv": "^6.0.0-alpha.2",
117
124
  "node-fetch": "^3.3.2",
125
+ "node-fetch-native": "^1.6.7",
118
126
  "nunjucks": "^3.2.4",
119
127
  "nypm": "^0.6.6",
128
+ "parse5": "^7.1.2",
129
+ "parse5-htmlparser2-tree-adapter": "^7.0.0",
120
130
  "puppeteer": "^24.41.0",
121
131
  "puppeteer-extra": "^3.3.6",
122
132
  "semver": "^7.7.4",