@gjsify/cli 0.4.29 → 0.4.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.
@@ -1,2 +1,2 @@
1
1
  import type { Command, CliBuildOptions } from '../types/index.js';
2
- export declare const buildCommand: Command<any, CliBuildOptions>;
2
+ export declare const buildCommand: Command<unknown, CliBuildOptions>;
@@ -5,5 +5,5 @@ interface CreateOptions {
5
5
  force: boolean;
6
6
  install: boolean;
7
7
  }
8
- export declare const createCommand: Command<any, CreateOptions>;
8
+ export declare const createCommand: Command<unknown, CreateOptions>;
9
9
  export {};
@@ -9,5 +9,5 @@ interface DlxOptions {
9
9
  verbose: boolean;
10
10
  registry?: string;
11
11
  }
12
- export declare const dlxCommand: Command<any, DlxOptions>;
12
+ export declare const dlxCommand: Command<unknown, DlxOptions>;
13
13
  export {};
@@ -152,7 +152,7 @@ async function resolveCommitForTag(cwd, tag, verbose) {
152
152
  }
153
153
  catch (err) {
154
154
  throw new Error(`[gjsify flatpak sync-flathub] tag ${tag} not found locally. Run \`git fetch --tags\` or pass --commit <sha>.\n` +
155
- ` underlying error: ${err?.message ?? err}`);
155
+ ` underlying error: ${err instanceof Error ? err.message : String(err)}`);
156
156
  }
157
157
  }
158
158
  function normaliseBranchSegment(version) {
@@ -13,5 +13,5 @@ interface ForeachOptions {
13
13
  jobs?: number;
14
14
  exec?: boolean;
15
15
  }
16
- export declare const foreachCommand: Command<any, ForeachOptions>;
16
+ export declare const foreachCommand: Command<unknown, ForeachOptions>;
17
17
  export {};
@@ -6,5 +6,5 @@ interface GenerateInstallerOptions {
6
6
  output: string;
7
7
  force: boolean;
8
8
  }
9
- export declare const generateInstallerCommand: Command<any, GenerateInstallerOptions>;
9
+ export declare const generateInstallerCommand: Command<unknown, GenerateInstallerOptions>;
10
10
  export {};
@@ -10,5 +10,5 @@ interface GettextOptions {
10
10
  removeXmlComments?: boolean;
11
11
  verbose?: boolean;
12
12
  }
13
- export declare const gettextCommand: Command<any, GettextOptions>;
13
+ export declare const gettextCommand: Command<unknown, GettextOptions>;
14
14
  export {};
@@ -179,15 +179,16 @@ export const gettextCommand = {
179
179
  }
180
180
  }
181
181
  catch (err) {
182
- if (err?.code === 'ENOENT') {
182
+ const e = err;
183
+ if (e?.code === 'ENOENT') {
183
184
  console.error('[gjsify gettext] msgfmt not found. Install it via your distro (package: gettext).');
184
185
  }
185
186
  else {
186
- if (err?.stderr)
187
- process.stderr.write(err.stderr);
188
- console.error(`[gjsify gettext] msgfmt failed${err?.code !== undefined ? ` (exit ${err.code})` : ''}`);
187
+ if (e?.stderr)
188
+ process.stderr.write(e.stderr);
189
+ console.error(`[gjsify gettext] msgfmt failed${e?.code !== undefined ? ` (exit ${e.code})` : ''}`);
189
190
  }
190
- process.exitCode = typeof err?.code === 'number' ? err.code : 1;
191
+ process.exitCode = typeof e?.code === 'number' ? e.code : 1;
191
192
  }
192
193
  },
193
194
  };
@@ -5,5 +5,5 @@ interface GResourceOptions {
5
5
  target?: string;
6
6
  verbose?: boolean;
7
7
  }
8
- export declare const gresourceCommand: Command<any, GResourceOptions>;
8
+ export declare const gresourceCommand: Command<unknown, GResourceOptions>;
9
9
  export {};
@@ -62,15 +62,16 @@ export const gresourceCommand = {
62
62
  }
63
63
  }
64
64
  catch (err) {
65
- if (err?.code === 'ENOENT') {
65
+ const e = err;
66
+ if (e?.code === 'ENOENT') {
66
67
  console.error('[gjsify gresource] glib-compile-resources not found. Install it via your distro (package: glib2-devel / libglib2.0-dev).');
67
68
  }
68
69
  else {
69
- if (err?.stderr)
70
- process.stderr.write(err.stderr);
71
- console.error(`[gjsify gresource] glib-compile-resources failed${err?.code !== undefined ? ` (exit ${err.code})` : ''}`);
70
+ if (e?.stderr)
71
+ process.stderr.write(e.stderr);
72
+ console.error(`[gjsify gresource] glib-compile-resources failed${e?.code !== undefined ? ` (exit ${e.code})` : ''}`);
72
73
  }
73
- process.exitCode = typeof err?.code === 'number' ? err.code : 1;
74
+ process.exitCode = typeof e?.code === 'number' ? e.code : 1;
74
75
  }
75
76
  },
76
77
  };
@@ -5,5 +5,5 @@ interface GSettingsOptions {
5
5
  strict?: boolean;
6
6
  verbose?: boolean;
7
7
  }
8
- export declare const gsettingsCommand: Command<any, GSettingsOptions>;
8
+ export declare const gsettingsCommand: Command<unknown, GSettingsOptions>;
9
9
  export {};
@@ -56,15 +56,16 @@ export const gsettingsCommand = {
56
56
  }
57
57
  }
58
58
  catch (err) {
59
- if (err?.code === 'ENOENT') {
59
+ const e = err;
60
+ if (e?.code === 'ENOENT') {
60
61
  console.error('[gjsify gsettings] glib-compile-schemas not found. Install it via your distro (package: glib2-devel / libglib2.0-dev).');
61
62
  }
62
63
  else {
63
- if (err?.stderr)
64
- process.stderr.write(err.stderr);
65
- console.error(`[gjsify gsettings] glib-compile-schemas failed${err?.code !== undefined ? ` (exit ${err.code})` : ''}`);
64
+ if (e?.stderr)
65
+ process.stderr.write(e.stderr);
66
+ console.error(`[gjsify gsettings] glib-compile-schemas failed${e?.code !== undefined ? ` (exit ${e.code})` : ''}`);
66
67
  }
67
- process.exitCode = typeof err?.code === 'number' ? err.code : 1;
68
+ process.exitCode = typeof e?.code === 'number' ? e.code : 1;
68
69
  }
69
70
  },
70
71
  };
@@ -3,5 +3,5 @@ interface InfoOptions {
3
3
  export: boolean;
4
4
  file?: string;
5
5
  }
6
- export declare const infoCommand: Command<any, InfoOptions>;
6
+ export declare const infoCommand: Command<unknown, InfoOptions>;
7
7
  export {};
@@ -9,5 +9,5 @@ interface InstallOptions {
9
9
  verbose: boolean;
10
10
  backend?: 'native' | 'npm';
11
11
  }
12
- export declare const installCommand: Command<any, InstallOptions>;
12
+ export declare const installCommand: Command<unknown, InstallOptions>;
13
13
  export {};
@@ -265,14 +265,39 @@ async function workspaceInstall(cwd, args) {
265
265
  for (const [depName, spec] of Object.entries(block)) {
266
266
  if (typeof spec !== 'string')
267
267
  continue;
268
- if (spec.startsWith('workspace:')) {
269
- const target = byName.get(depName);
270
- if (!target) {
271
- throw new Error(`gjsify install: ${ws.name} declares "${depName}: ${spec}" but ` +
272
- `no workspace with that name exists`);
268
+ // A dependency whose NAME matches a local workspace is always
269
+ // satisfied by that workspace — never by a same-named package on
270
+ // npm. This holds regardless of the spec form: an explicit
271
+ // `workspace:` protocol, a plain semver range (`^1.2.3`), or even
272
+ // a dist-tag. Yarn/npm resolve a workspace-named dep to the local
273
+ // package first; we MUST do the same. Routing such a dep into the
274
+ // native fetch/extract queue is the data-loss bug this guards
275
+ // against — `extractOne` would `rmSync` + drop the published
276
+ // tarball over the workspace's OWN source tree (the published
277
+ // tarball ships only `files`, so `src/**` gets wiped). Symlink
278
+ // it instead, exactly like a `workspace:` ref.
279
+ const localWorkspace = byName.get(depName);
280
+ if (localWorkspace) {
281
+ // Only an EXPLICIT out-of-workspace protocol (link:/file:/
282
+ // portal:/git+/http(s):) opts out of the local workspace. Any
283
+ // other shape — `workspace:` or a plain semver range/dist-tag
284
+ // (`^1.2.3`, `*`, `latest`) — resolves to the local package.
285
+ const explicitOverride = /^(link|file|portal|git\+|https?):/.test(spec);
286
+ if (!explicitOverride) {
287
+ symlinks.push({
288
+ fromWorkspaceName: ws.name,
289
+ depName,
290
+ targetLocation: localWorkspace.location,
291
+ });
292
+ continue;
273
293
  }
274
- symlinks.push({ fromWorkspaceName: ws.name, depName, targetLocation: target.location });
275
- continue;
294
+ // explicit override fall through to the existing handling.
295
+ }
296
+ if (spec.startsWith('workspace:')) {
297
+ // `workspace:` against a name that is NOT a discovered
298
+ // workspace is a hard error (typo / missing package).
299
+ throw new Error(`gjsify install: ${ws.name} declares "${depName}: ${spec}" but ` +
300
+ `no workspace with that name exists`);
276
301
  }
277
302
  if (/^(link|file|portal|git\+|https?):/.test(spec))
278
303
  continue;
@@ -23,7 +23,7 @@ interface PackResult {
23
23
  /** Absolute path of the written .tgz, or null on --dry-run. */
24
24
  absolutePath: string | null;
25
25
  }
26
- export declare const packCommand: Command<any, PackOptions>;
26
+ export declare const packCommand: Command<unknown, PackOptions>;
27
27
  export interface PackWorkspaceOptions {
28
28
  /** Directory to write the .tgz into. Defaults to the workspace itself. */
29
29
  destination?: string;
@@ -12,5 +12,5 @@ interface PublishOptions {
12
12
  trusted?: boolean | 'auto';
13
13
  'check-trusted'?: boolean;
14
14
  }
15
- export declare const publishCommand: Command<any, PublishOptions>;
15
+ export declare const publishCommand: Command<unknown, PublishOptions>;
16
16
  export {};
@@ -3,5 +3,5 @@ interface RunOptions {
3
3
  target: string;
4
4
  args: string[];
5
5
  }
6
- export declare const runCommand: Command<any, RunOptions>;
6
+ export declare const runCommand: Command<unknown, RunOptions>;
7
7
  export {};
@@ -4,5 +4,5 @@ interface SelfUpdateOptions {
4
4
  force?: boolean;
5
5
  tag: string;
6
6
  }
7
- export declare const selfUpdateCommand: Command<any, SelfUpdateOptions>;
7
+ export declare const selfUpdateCommand: Command<unknown, SelfUpdateOptions>;
8
8
  export {};
@@ -4,5 +4,5 @@ interface ShowcaseOptions {
4
4
  json: boolean;
5
5
  list: boolean;
6
6
  }
7
- export declare const showcaseCommand: Command<any, ShowcaseOptions>;
7
+ export declare const showcaseCommand: Command<unknown, ShowcaseOptions>;
8
8
  export {};
@@ -2,5 +2,5 @@ import type { Command } from '../types/index.js';
2
2
  interface CheckOptions {
3
3
  json: boolean;
4
4
  }
5
- export declare const systemCheckCommand: Command<any, CheckOptions>;
5
+ export declare const systemCheckCommand: Command<unknown, CheckOptions>;
6
6
  export {};
@@ -5,5 +5,5 @@ interface UninstallOptions {
5
5
  'dry-run'?: boolean;
6
6
  verbose?: boolean;
7
7
  }
8
- export declare const uninstallCommand: Command<any, UninstallOptions>;
8
+ export declare const uninstallCommand: Command<unknown, UninstallOptions>;
9
9
  export {};
@@ -4,5 +4,5 @@ interface WorkspaceCmdOptions {
4
4
  script: string;
5
5
  args?: string[];
6
6
  }
7
- export declare const workspaceCommand: Command<any, WorkspaceCmdOptions>;
7
+ export declare const workspaceCommand: Command<unknown, WorkspaceCmdOptions>;
8
8
  export {};
@@ -1,5 +1,5 @@
1
1
  import type { ArgumentsCamelCase, MiddlewareFunction, BuilderCallback } from 'yargs';
2
- export interface Command<T = any, U = T> {
2
+ export interface Command<T = unknown, U = T> {
3
3
  command: string | ReadonlyArray<string>;
4
4
  description: string;
5
5
  builder?: BuilderCallback<T, U>;
@@ -1,4 +1,4 @@
1
- export type CosmiconfigResult<C = any> = {
1
+ export type CosmiconfigResult<C = unknown> = {
2
2
  config: C;
3
3
  filepath: string;
4
4
  isEmpty?: boolean;
@@ -486,6 +486,15 @@ async function downloadAndExtractAll(nodes, prefix, npmrc, log) {
486
486
  }
487
487
  async function extractOne(node, prefix, npmrc, log) {
488
488
  const dest = path.join(prefix, node.installPath);
489
+ // Defense-in-depth against the workspace-source-wipe data-loss bug:
490
+ // every extractable node MUST land inside a `node_modules/` directory.
491
+ // The resolver only ever produces `installPath`s of that shape, so this
492
+ // can only fail if a workspace package leaked into the fetch/extract
493
+ // queue (the root cause fixed in `workspaceInstall`). Refusing here means
494
+ // a regression in the resolver can never again `rmSync` a working-tree
495
+ // source dir — the realpath check additionally rejects a `dest` that
496
+ // resolves THROUGH a symlink into a directory outside node_modules.
497
+ assertNodeModulesDest(dest, node);
489
498
  log('fetch: %s@%s ← %s (→ %s)', node.name, node.version, node.tarballUrl, node.installPath);
490
499
  const bytes = await fetchTarball(node.tarballUrl, {
491
500
  npmrc,
@@ -498,6 +507,44 @@ async function extractOne(node, prefix, npmrc, log) {
498
507
  fs.mkdirSync(dest, { recursive: true });
499
508
  await extractTarball(bytes, dest);
500
509
  }
510
+ /**
511
+ * Guard: a tarball may only be extracted into a `node_modules/` directory.
512
+ *
513
+ * Two checks, both belt-and-suspenders against ever wiping a working-tree
514
+ * source dir (the install-deletes-workspace-sources data-loss bug):
515
+ *
516
+ * 1. The logical `installPath` must contain a `node_modules` path segment.
517
+ * The resolver always produces such paths; a workspace package that
518
+ * slipped into the queue would not.
519
+ * 2. If `dest` already exists and resolves (via symlink) to a directory
520
+ * whose REAL path is not under a `node_modules/` segment, refuse. This
521
+ * catches the case where `node_modules/<name>` is a symlink to a
522
+ * workspace's source tree — `rmSync(dest, { recursive: true })` would
523
+ * then delete the link's target contents.
524
+ */
525
+ function assertNodeModulesDest(dest, node) {
526
+ const segments = dest.split(path.sep);
527
+ if (!segments.includes('node_modules')) {
528
+ throw new Error(`gjsify install: refusing to extract ${node.name}@${node.version} into ${dest} — ` +
529
+ `target is not inside a node_modules/ directory. This would overwrite working-tree files. ` +
530
+ `A workspace package likely leaked into the fetch queue (it must be symlinked, not fetched).`);
531
+ }
532
+ let real;
533
+ try {
534
+ real = fs.realpathSync(dest);
535
+ }
536
+ catch {
537
+ // `dest` doesn't exist yet (fresh install) — nothing to resolve, the
538
+ // logical-path check above is sufficient.
539
+ return;
540
+ }
541
+ const realSegments = real.split(path.sep);
542
+ if (!realSegments.includes('node_modules')) {
543
+ throw new Error(`gjsify install: refusing to extract ${node.name}@${node.version} — ${dest} resolves to ${real}, ` +
544
+ `which is outside any node_modules/ directory (likely a symlink to a workspace source tree). ` +
545
+ `Extracting here would delete working-tree source files.`);
546
+ }
547
+ }
501
548
  function depth(installPath) {
502
549
  // Count `node_modules/` segments to know nesting depth.
503
550
  // `node_modules/foo` = 1, `node_modules/foo/node_modules/bar` = 2, etc.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gjsify/cli",
3
- "version": "0.4.29",
3
+ "version": "0.4.31",
4
4
  "description": "CLI for Gjsify",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
@@ -120,18 +120,18 @@
120
120
  "cli"
121
121
  ],
122
122
  "dependencies": {
123
- "@gjsify/buffer": "^0.4.29",
124
- "@gjsify/create-app": "^0.4.29",
125
- "@gjsify/node-globals": "^0.4.29",
126
- "@gjsify/node-polyfills": "^0.4.29",
127
- "@gjsify/npm-registry": "^0.4.29",
128
- "@gjsify/resolve-npm": "^0.4.29",
129
- "@gjsify/rolldown-plugin-gjsify": "^0.4.29",
130
- "@gjsify/rolldown-plugin-pnp": "^0.4.29",
131
- "@gjsify/semver": "^0.4.29",
132
- "@gjsify/tar": "^0.4.29",
133
- "@gjsify/web-polyfills": "^0.4.29",
134
- "@gjsify/workspace": "^0.4.29",
123
+ "@gjsify/buffer": "^0.4.31",
124
+ "@gjsify/create-app": "^0.4.31",
125
+ "@gjsify/node-globals": "^0.4.31",
126
+ "@gjsify/node-polyfills": "^0.4.31",
127
+ "@gjsify/npm-registry": "^0.4.31",
128
+ "@gjsify/resolve-npm": "^0.4.31",
129
+ "@gjsify/rolldown-plugin-gjsify": "^0.4.31",
130
+ "@gjsify/rolldown-plugin-pnp": "^0.4.31",
131
+ "@gjsify/semver": "^0.4.31",
132
+ "@gjsify/tar": "^0.4.31",
133
+ "@gjsify/web-polyfills": "^0.4.31",
134
+ "@gjsify/workspace": "^0.4.31",
135
135
  "cosmiconfig": "^9.0.1",
136
136
  "get-tsconfig": "^4.14.0",
137
137
  "pkg-types": "^2.3.1",
@@ -139,12 +139,12 @@
139
139
  "yargs": "^18.0.0"
140
140
  },
141
141
  "devDependencies": {
142
- "@gjsify/unit": "^0.4.29",
142
+ "@gjsify/unit": "^0.4.31",
143
143
  "@types/yargs": "^17.0.35",
144
144
  "typescript": "^6.0.3"
145
145
  },
146
146
  "peerDependencies": {
147
- "@gjsify/rolldown-native": "^0.4.29"
147
+ "@gjsify/rolldown-native": "^0.4.31"
148
148
  },
149
149
  "peerDependenciesMeta": {
150
150
  "@gjsify/rolldown-native": {