@exodus/stasis 1.0.0-beta.0 → 1.0.0-beta.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/bin/stasis.js CHANGED
@@ -6,7 +6,9 @@ import { once } from 'node:events'
6
6
  import { fileURLToPath } from 'node:url'
7
7
  import { basename, dirname, resolve } from 'node:path'
8
8
  import { existsSync, realpathSync } from 'node:fs'
9
+ import { constants as osConstants } from 'node:os'
9
10
  import assert from 'node:assert/strict'
11
+ import { parseBrotliQuality } from '@exodus/stasis-core/util'
10
12
  import pkg from '../package.json' with { type: 'json' }
11
13
 
12
14
  const argv = [...process.argv]
@@ -19,14 +21,15 @@ assert(basename(jsname) === 'stasis' || pathsEqual(jsname, fileURLToPath(import.
19
21
 
20
22
  function usage(prefix = '') {
21
23
  console.error(`${prefix}\nUsage:
22
- stasis run --lock=(add|replace|frozen|ignore) [--bundle=(add|replace|load|frozen|ignore)] [--bundle-file=path/to/bundle.br] [--resources-bundle-file=path/to/resources.br] [--dependencies] [--child-process] [--mock] [--fs=(sync|async)] [--resources=ext,ext] path/to/file.js ...
24
+ stasis run --lock=(add|replace|frozen|ignore) [--bundle=(add|replace|load|frozen|ignore)] [--bundle-file=path/to/bundle.br] [--resources-bundle-file=path/to/resources.br] [--dependencies] [--child-process] [--mock] [--fs=(sync|async)] [--resources=ext,ext] [--brotli-quality=0..11] path/to/file.js ...
23
25
  stasis bundle [--mapping=path/to/remappings(.txt|.toml)] [--output=(path|-)] path/to/file.sol ...
24
26
  stasis bundle [--output=(path|-)] path/to/file.php ...
25
27
  stasis bundle [--scope=(node_modules|full)] [--conditions=cond1,cond2] [--mainFields=field1,field2] [--lockfile=path/to/stasis.lock.json] [--output=(path|-)] path/to/file.(js|ts) ...
26
28
  stasis bundle --metro --platforms=ios,android [--platforms=web] [--lockfile=path/to/stasis.lock.json] [--output=(path|-)] path/to/file.(js|ts) ...
27
29
  stasis bundle [--output=(path|-)] path/to/file.(sh|bash) ...
28
30
  stasis bundle [--output=(path|-)] path/to/file.rs ...
29
- (stasis bundle writes to stasis.code.br by default; --output=- streams to stdout)
31
+ (stasis bundle writes to stasis.code.br by default; --output=- streams to stdout;
32
+ --brotli-quality=0..11 tunes bundle compression for any entry language, default 11 = smallest/slowest)
30
33
  stasis build --output=(dir|file.js) [--format=(esm|cjs|iife)] [--platform=(node|browser|neutral)] [--minify] [--sourcemap] [--define=K=V ...] [--external=pkg ...] [--loader=.ext:name ...] path/to/(stasis.code.br|stasis.lock.json) [entry]
31
34
  stasis extract [--output=path/to/dir] path/to/bundle.stasis.code.br
32
35
  stasis diff --stat [--imports] path/to/(lockfile|bundle) path/to/(lockfile|bundle)
@@ -51,7 +54,7 @@ if (command === '-v' || command === '--version') {
51
54
  process.exit(0)
52
55
  } else if (command === 'run') {
53
56
  const flags = []
54
- const valueFlags = new Set(['--bundle', '--bundle-file', '--resources-bundle-file', '--lock', '--resources'])
57
+ const valueFlags = new Set(['--bundle', '--bundle-file', '--resources-bundle-file', '--lock', '--resources', '--brotli-quality'])
55
58
  while (argv.length > 0 && (argv[0].startsWith('-') || valueFlags.has(flags.at(-1)))) {
56
59
  flags.push(argv.shift())
57
60
  }
@@ -67,6 +70,7 @@ if (command === '-v' || command === '--version') {
67
70
  mock: { type: 'boolean' },
68
71
  fs: { type: 'string' },
69
72
  resources: { type: 'string' },
73
+ 'brotli-quality': { type: 'string' },
70
74
  }
71
75
 
72
76
  let values
@@ -103,10 +107,19 @@ if (command === '-v' || command === '--version') {
103
107
  // --resources: comma-separated extension/filename allowlist for `--fs` resource captures
104
108
  // (e.g. png,svg,LICENSE). The child's Config validates each entry via parseResourcesOption.
105
109
  const resources = values.resources ?? ''
110
+ // --brotli-quality: bundle compression quality (0..11; unset = brotli's default 11).
111
+ let brotliQuality
112
+ if (values['brotli-quality'] !== undefined) {
113
+ try {
114
+ brotliQuality = parseBrotliQuality('--brotli-quality', values['brotli-quality'])
115
+ } catch (cause) {
116
+ usage(`Error: ${cause.message}`)
117
+ }
118
+ }
106
119
  // --child-process: forward forked-child (e.g. Metro transform worker) capture to the root
107
120
  // via per-pid shards. Opt-in -- it stands up a process-coordination channel, off by default.
108
121
  const childProcess = values['child-process'] ? '1' : ''
109
- console.warn('[stasis] Running stasis with config:', { lock, scope, bundle, ...(bundleFile && { bundleFile }), ...(resourcesBundleFile && { resourcesBundleFile }), ...(childProcess && { childProcess: true }), ...(values.mock && { mock: true }), ...(values.fs && { fs: values.fs }), ...(resources && { resources }) })
122
+ console.warn('[stasis] Running stasis with config:', { lock, scope, bundle, ...(bundleFile && { bundleFile }), ...(resourcesBundleFile && { resourcesBundleFile }), ...(childProcess && { childProcess: true }), ...(values.mock && { mock: true }), ...(values.fs && { fs: values.fs }), ...(resources && { resources }), ...(brotliQuality !== undefined && { brotliQuality }) })
110
123
  if (debug) console.warn(`[stasis] Warning: stasis debug mode active`)
111
124
  setEnv('EXODUS_STASIS_LOCK', lock)
112
125
  setEnv('EXODUS_STASIS_SCOPE', scope)
@@ -117,6 +130,9 @@ if (command === '-v' || command === '--version') {
117
130
  setEnv('EXODUS_STASIS_CHILD_PROCESS', childProcess)
118
131
  setEnv('EXODUS_STASIS_FS', captureFs)
119
132
  setEnv('EXODUS_STASIS_RESOURCES', resources)
133
+ // Only set when given, so an ambient EXODUS_STASIS_BROTLI_QUALITY still passes through
134
+ // to the child (an unconditional setEnv('') would reject it as a conflict).
135
+ if (brotliQuality !== undefined) setEnv('EXODUS_STASIS_BROTLI_QUALITY', String(brotliQuality))
120
136
  // --mock: capture imports by running user code with side-effects denied,
121
137
  // fail-closed. Node's permission system blocks fs writes, child processes,
122
138
  // worker threads, native addons (no --allow-addons -- addons would bypass
@@ -167,11 +183,12 @@ if (command === '-v' || command === '--version') {
167
183
  const loaderEntry = values.mock ? '../src/loader-mock.js' : '@exodus/stasis-core/loader'
168
184
  nodeArgs.push('--import', import.meta.resolve(loaderEntry))
169
185
  const child = spawn(process.execPath, [...nodeArgs, ...argv], { stdio: 'inherit' })
170
- const [code] = await once(child, 'close')
171
- process.exitCode = code
186
+ const [code, signal] = await once(child, 'close')
187
+ // code is null when the child died from a signal; report 128+signo (shell convention) instead of the implicit 0
188
+ process.exitCode = code ?? 128 + (osConstants.signals[signal] ?? 0)
172
189
  } else if (command === 'bundle') {
173
190
  const flags = []
174
- const valueFlags = new Set(['--mapping', '--output', '--scope', '--lockfile', '--conditions', '--mainFields', '--platforms', '-o'])
191
+ const valueFlags = new Set(['--mapping', '--output', '--scope', '--lockfile', '--conditions', '--mainFields', '--platforms', '--brotli-quality', '-o'])
175
192
  while (argv.length > 0 && (argv[0].startsWith('-') || valueFlags.has(flags.at(-1)))) {
176
193
  flags.push(argv.shift())
177
194
  }
@@ -184,6 +201,7 @@ if (command === '-v' || command === '--version') {
184
201
  mainFields: { type: 'string' },
185
202
  metro: { type: 'boolean' },
186
203
  platforms: { type: 'string', multiple: true },
204
+ 'brotli-quality': { type: 'string' },
187
205
  }
188
206
  let values
189
207
  try {
@@ -253,6 +271,15 @@ if (command === '-v' || command === '--version') {
253
271
  } else if (platforms.length > 0) {
254
272
  usage('Error: --platforms is only valid with --metro')
255
273
  }
274
+ // --brotli-quality: output-encoding knob, valid for every entry language (no allJs gate).
275
+ let brotliQuality
276
+ if (values['brotli-quality'] !== undefined) {
277
+ try {
278
+ brotliQuality = parseBrotliQuality('--brotli-quality', values['brotli-quality'])
279
+ } catch (cause) {
280
+ usage(`Error: ${cause.message}`)
281
+ }
282
+ }
256
283
  const { bundleCommand } = await import('../src/cmd/bundle.js')
257
284
  await bundleCommand({
258
285
  cwd: process.cwd(),
@@ -265,6 +292,7 @@ if (command === '-v' || command === '--version') {
265
292
  mainFields,
266
293
  metro,
267
294
  platforms,
295
+ brotliQuality,
268
296
  })
269
297
  } else if (command === 'build') {
270
298
  const flags = []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/stasis",
3
- "version": "1.0.0-beta.0",
3
+ "version": "1.0.0-beta.2",
4
4
  "description": "File-level source bundles and lockfiles",
5
5
  "type": "module",
6
6
  "exports": {
@@ -68,8 +68,8 @@
68
68
  },
69
69
  "homepage": "https://github.com/ExodusOSS/stasis#readme",
70
70
  "dependencies": {
71
- "@exodus/stasis-core": "1.0.0-beta.0",
72
- "@exodus/stasis-plugins": "1.0.0-beta.0",
71
+ "@exodus/stasis-core": "1.0.0-beta.2",
72
+ "@exodus/stasis-plugins": "1.0.0-beta.2",
73
73
  "oxc-parser": "^0.94.0"
74
74
  },
75
75
  "peerDependencies": {
package/src/cmd/build.js CHANGED
@@ -21,13 +21,14 @@ export { bundleFromLockfile }
21
21
  const JS_EXTS = new Set(['.js', '.cjs', '.mjs', '.ts', '.cts', '.mts', '.jsx', '.tsx'])
22
22
 
23
23
  // Allowlist (NOT a blocklist) of per-file formats `stasis build` accepts in an artifact:
24
- // the JS/TS/JSON code formats esbuild builds, plus the non-code payload formats
25
- // (resource/directory) that are inert unless the SELECTED entry imports them. An allowlist
24
+ // the JS/TS/JSON code formats esbuild builds, plus the non-code formats -- resource/
25
+ // directory payloads and the payload-free stat records (`--fs` lstat/stat captures) --
26
+ // that are inert unless the SELECTED entry imports them. An allowlist
26
27
  // so a new language format added to `stasis bundle` later (e.g. a future `go`) is rejected
27
28
  // here by default, rather than slipping through to a cryptic esbuild failure in the build.
28
29
  const BUILDABLE_FORMATS = new Set([
29
30
  'module', 'commonjs', 'json', 'module-typescript', 'commonjs-typescript',
30
- 'resource', 'resource:base64', 'directory',
31
+ 'resource', 'resource:base64', 'directory', 'stat:file', 'stat:directory',
31
32
  ])
32
33
 
33
34
  // esbuild is an OPTIONAL peer dependency: `stasis build` is the only command that
package/src/cmd/bundle.js CHANGED
@@ -1020,7 +1020,9 @@ const DEFAULT_BUNDLE_FILE = 'stasis.code.br'
1020
1020
  // so it resolves different files on disk and fails closed (correctly -- the lockfile
1021
1021
  // genuinely attests a different graph). Pair such a lockfile with `--bundle=load`, or
1022
1022
  // replay the same `--conditions` at run time, so the attested and observed graphs agree.
1023
- export async function bundleCommand({ cwd = process.cwd(), entries, mappingFile, output, scope, lockfile, conditions, mainFields, platforms, metro } = {}) {
1023
+ // `brotliQuality` (integer 0..11, optional; unset = brotli's default 11) tunes the
1024
+ // output compression, forwarded to brotliOptions().
1025
+ export async function bundleCommand({ cwd = process.cwd(), entries, mappingFile, output, scope, lockfile, conditions, mainFields, platforms, metro, brotliQuality } = {}) {
1024
1026
  const kind = classifyEntries('bundleCommand', { entries, mappingFile, scope, lockfile, conditions, mainFields, platforms, metro })
1025
1027
 
1026
1028
  let bundle
@@ -1053,7 +1055,7 @@ export async function bundleCommand({ cwd = process.cwd(), entries, mappingFile,
1053
1055
  const files = [...bundle.sources.keys()]
1054
1056
  const modules = bundle.modules
1055
1057
 
1056
- const data = brotliCompressSync(serialized, brotliOptions())
1058
+ const data = brotliCompressSync(serialized, brotliOptions(brotliQuality))
1057
1059
  // Default to writing stasis.code.br in cwd; `-` is the conventional opt-in
1058
1060
  // for streaming the raw brotli bytes to stdout (e.g. to pipe somewhere else).
1059
1061
  const target = output ?? DEFAULT_BUNDLE_FILE
@@ -1,6 +1,7 @@
1
1
  // Public-export adapter: backs `@exodus/stasis/metro-transformer`. The transformer
2
2
  // lives in @exodus/stasis-plugins (which depends on @exodus/stasis-core for State; it
3
- // drives load-mode getFile directly). Wire it as Metro's `transformer.transformerPath`
4
- // in load mode; see the source for the load asymmetry that lets this run per-worker
3
+ // drives load-mode getFile directly). Wire it permanently as Metro's top-level
4
+ // `transformerPath`; it only acts under bundle=load (transparent pass-through
5
+ // otherwise). See the source for the load asymmetry that lets this run per-worker
5
6
  // without cross-worker state sync.
6
7
  export * from '@exodus/stasis-plugins/metro-transformer'