@exodus/stasis 1.0.0-beta.1 → 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
@@ -8,6 +8,7 @@ import { basename, dirname, resolve } from 'node:path'
8
8
  import { existsSync, realpathSync } from 'node:fs'
9
9
  import { constants as osConstants } from 'node:os'
10
10
  import assert from 'node:assert/strict'
11
+ import { parseBrotliQuality } from '@exodus/stasis-core/util'
11
12
  import pkg from '../package.json' with { type: 'json' }
12
13
 
13
14
  const argv = [...process.argv]
@@ -20,14 +21,15 @@ assert(basename(jsname) === 'stasis' || pathsEqual(jsname, fileURLToPath(import.
20
21
 
21
22
  function usage(prefix = '') {
22
23
  console.error(`${prefix}\nUsage:
23
- 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 ...
24
25
  stasis bundle [--mapping=path/to/remappings(.txt|.toml)] [--output=(path|-)] path/to/file.sol ...
25
26
  stasis bundle [--output=(path|-)] path/to/file.php ...
26
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) ...
27
28
  stasis bundle --metro --platforms=ios,android [--platforms=web] [--lockfile=path/to/stasis.lock.json] [--output=(path|-)] path/to/file.(js|ts) ...
28
29
  stasis bundle [--output=(path|-)] path/to/file.(sh|bash) ...
29
30
  stasis bundle [--output=(path|-)] path/to/file.rs ...
30
- (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)
31
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]
32
34
  stasis extract [--output=path/to/dir] path/to/bundle.stasis.code.br
33
35
  stasis diff --stat [--imports] path/to/(lockfile|bundle) path/to/(lockfile|bundle)
@@ -52,7 +54,7 @@ if (command === '-v' || command === '--version') {
52
54
  process.exit(0)
53
55
  } else if (command === 'run') {
54
56
  const flags = []
55
- 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'])
56
58
  while (argv.length > 0 && (argv[0].startsWith('-') || valueFlags.has(flags.at(-1)))) {
57
59
  flags.push(argv.shift())
58
60
  }
@@ -68,6 +70,7 @@ if (command === '-v' || command === '--version') {
68
70
  mock: { type: 'boolean' },
69
71
  fs: { type: 'string' },
70
72
  resources: { type: 'string' },
73
+ 'brotli-quality': { type: 'string' },
71
74
  }
72
75
 
73
76
  let values
@@ -104,10 +107,19 @@ if (command === '-v' || command === '--version') {
104
107
  // --resources: comma-separated extension/filename allowlist for `--fs` resource captures
105
108
  // (e.g. png,svg,LICENSE). The child's Config validates each entry via parseResourcesOption.
106
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
+ }
107
119
  // --child-process: forward forked-child (e.g. Metro transform worker) capture to the root
108
120
  // via per-pid shards. Opt-in -- it stands up a process-coordination channel, off by default.
109
121
  const childProcess = values['child-process'] ? '1' : ''
110
- 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 }) })
111
123
  if (debug) console.warn(`[stasis] Warning: stasis debug mode active`)
112
124
  setEnv('EXODUS_STASIS_LOCK', lock)
113
125
  setEnv('EXODUS_STASIS_SCOPE', scope)
@@ -118,6 +130,9 @@ if (command === '-v' || command === '--version') {
118
130
  setEnv('EXODUS_STASIS_CHILD_PROCESS', childProcess)
119
131
  setEnv('EXODUS_STASIS_FS', captureFs)
120
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))
121
136
  // --mock: capture imports by running user code with side-effects denied,
122
137
  // fail-closed. Node's permission system blocks fs writes, child processes,
123
138
  // worker threads, native addons (no --allow-addons -- addons would bypass
@@ -173,7 +188,7 @@ if (command === '-v' || command === '--version') {
173
188
  process.exitCode = code ?? 128 + (osConstants.signals[signal] ?? 0)
174
189
  } else if (command === 'bundle') {
175
190
  const flags = []
176
- 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'])
177
192
  while (argv.length > 0 && (argv[0].startsWith('-') || valueFlags.has(flags.at(-1)))) {
178
193
  flags.push(argv.shift())
179
194
  }
@@ -186,6 +201,7 @@ if (command === '-v' || command === '--version') {
186
201
  mainFields: { type: 'string' },
187
202
  metro: { type: 'boolean' },
188
203
  platforms: { type: 'string', multiple: true },
204
+ 'brotli-quality': { type: 'string' },
189
205
  }
190
206
  let values
191
207
  try {
@@ -255,6 +271,15 @@ if (command === '-v' || command === '--version') {
255
271
  } else if (platforms.length > 0) {
256
272
  usage('Error: --platforms is only valid with --metro')
257
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
+ }
258
283
  const { bundleCommand } = await import('../src/cmd/bundle.js')
259
284
  await bundleCommand({
260
285
  cwd: process.cwd(),
@@ -267,6 +292,7 @@ if (command === '-v' || command === '--version') {
267
292
  mainFields,
268
293
  metro,
269
294
  platforms,
295
+ brotliQuality,
270
296
  })
271
297
  } else if (command === 'build') {
272
298
  const flags = []
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exodus/stasis",
3
- "version": "1.0.0-beta.1",
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.1",
72
- "@exodus/stasis-plugins": "1.0.0-beta.1",
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'