@mikrojs/quickjs 0.14.0 → 0.15.0-next.20260614113448

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.
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env node
2
+ /* eslint-disable no-console */
3
+ /**
4
+ * Apply mikrojs-local QuickJS patches to the submodule working tree.
5
+ *
6
+ * Patches live in patches/*.patch and are applied idempotently: if
7
+ * `git apply --reverse --check` succeeds the patch is already present.
8
+ *
9
+ * Importable (`applyPatches()`) and runnable (`node apply-patches.js`).
10
+ * The release publish job runs it explicitly: that job installs with
11
+ * --ignore-scripts, so the postinstall that normally applies the patch
12
+ * never runs. Without this the packed @mikrojs/quickjs tarball ships
13
+ * unpatched QuickJS source and every consumer fails to compile
14
+ * @mikrojs/native (which calls the patched module-management symbols).
15
+ */
16
+ import {execFileSync} from 'node:child_process'
17
+ import {existsSync, readdirSync} from 'node:fs'
18
+ import {dirname, join} from 'node:path'
19
+ import {fileURLToPath} from 'node:url'
20
+
21
+ const __dirname = dirname(fileURLToPath(import.meta.url))
22
+ const qjsDir = join(__dirname, 'deps', 'quickjs')
23
+ const patchesDir = join(__dirname, 'patches')
24
+
25
+ export function applyPatches() {
26
+ if (!existsSync(patchesDir)) return
27
+ const files = readdirSync(patchesDir)
28
+ .filter((f) => f.endsWith('.patch'))
29
+ .sort()
30
+ for (const f of files) {
31
+ const patch = join(patchesDir, f)
32
+ // Probe: is the patch already applied?
33
+ try {
34
+ execFileSync('git', ['apply', '--reverse', '--check', patch], {
35
+ cwd: qjsDir,
36
+ stdio: 'pipe',
37
+ })
38
+ continue // already applied
39
+ } catch {
40
+ // not applied — fall through
41
+ }
42
+ try {
43
+ execFileSync('git', ['apply', patch], {cwd: qjsDir, stdio: 'inherit'})
44
+ console.log(`@mikrojs/quickjs: applied patch ${f}`)
45
+ } catch (err) {
46
+ console.error(`@mikrojs/quickjs: failed to apply ${f}`, err.message)
47
+ process.exit(1)
48
+ }
49
+ }
50
+ }
51
+
52
+ // Run directly: sync the submodule, then apply patches.
53
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
54
+ const repoRoot = join(__dirname, '..', '..', '..')
55
+ try {
56
+ execFileSync(
57
+ 'git',
58
+ ['submodule', 'update', '--init', 'packages/@mikrojs/quickjs/deps/quickjs'],
59
+ {
60
+ cwd: repoRoot,
61
+ stdio: 'inherit',
62
+ },
63
+ )
64
+ } catch {
65
+ if (!existsSync(join(qjsDir, 'quickjs.c'))) {
66
+ console.error(
67
+ '@mikrojs/quickjs: Failed to initialize QuickJS submodule.\n' +
68
+ 'Run manually: git submodule update --init packages/@mikrojs/quickjs/deps/quickjs',
69
+ )
70
+ process.exit(1)
71
+ }
72
+ }
73
+ applyPatches()
74
+ }
@@ -29724,6 +29724,48 @@ static void js_free_module_def(JSContext *ctx, JSModuleDef *m)
29724
29724
  js_free(ctx, m);
29725
29725
  }
29726
29726
 
29727
+ /* mikrojs patch: public wrapper to free a single module (remove it from
29728
+ * the context's loaded-modules cache and release all its resources).
29729
+ * Callers must ensure the module is not mid-linking or mid-evaluating and
29730
+ * that no other modules hold live import bindings to its exports (or accept
29731
+ * that such bindings remain valid via refcounted JSVarRefs). */
29732
+ void JS_FreeModule(JSContext *ctx, JSModuleDef *m)
29733
+ {
29734
+ js_free_module_def(ctx, m);
29735
+ }
29736
+
29737
+ static JSModuleDef *js_find_loaded_module(JSContext *ctx, JSAtom name);
29738
+
29739
+ /* mikrojs patch: lookup a module in the context cache by name atom.
29740
+ * Returns NULL if no module with that name is loaded. */
29741
+ JSModuleDef *JS_FindLoadedModule(JSContext *ctx, JSAtom name)
29742
+ {
29743
+ return js_find_loaded_module(ctx, name);
29744
+ }
29745
+
29746
+ /* mikrojs patch: return the module's current status as an integer.
29747
+ * 0=UNLINKED, 1=LINKING, 2=LINKED, 3=EVALUATING, 4=EVALUATING_ASYNC, 5=EVALUATED. */
29748
+ int JS_GetModuleStatus(JSContext *ctx, JSModuleDef *m)
29749
+ {
29750
+ (void)ctx;
29751
+ return (int)m->status;
29752
+ }
29753
+
29754
+ /* mikrojs patch: find the loaded module whose namespace object is `ns`.
29755
+ * Reverse of JS_GetModuleNamespace. Returns NULL if `ns` is not the
29756
+ * namespace of any loaded module (or its namespace has not been built). */
29757
+ JSModuleDef *JS_FindModuleByNamespace(JSContext *ctx, JSValueConst ns)
29758
+ {
29759
+ struct list_head *el;
29760
+ list_for_each(el, &ctx->loaded_modules) {
29761
+ JSModuleDef *m = list_entry(el, JSModuleDef, link);
29762
+ if (!JS_IsUndefined(m->module_ns) &&
29763
+ JS_VALUE_GET_PTR(m->module_ns) == JS_VALUE_GET_PTR(ns))
29764
+ return m;
29765
+ }
29766
+ return NULL;
29767
+ }
29768
+
29727
29769
  #ifndef QJS_DISABLE_PARSER
29728
29770
 
29729
29771
  static int add_req_module_entry(JSContext *ctx, JSModuleDef *m,
@@ -1190,6 +1190,26 @@ JS_EXTERN JSValue JS_GetImportMeta(JSContext *ctx, JSModuleDef *m);
1190
1190
  JS_EXTERN JSAtom JS_GetModuleName(JSContext *ctx, JSModuleDef *m);
1191
1191
  JS_EXTERN JSValue JS_GetModuleNamespace(JSContext *ctx, JSModuleDef *m);
1192
1192
 
1193
+ /* mikrojs patch: evict a module from the context, releasing all its
1194
+ * resources. Caller is responsible for ensuring the module is in a safe
1195
+ * state (not linking, not evaluating). */
1196
+ JS_EXTERN void JS_FreeModule(JSContext *ctx, JSModuleDef *m);
1197
+
1198
+ /* mikrojs patch: lookup a module in the context cache by name atom.
1199
+ * Returns NULL if no module with that name is loaded. */
1200
+ JS_EXTERN JSModuleDef *JS_FindLoadedModule(JSContext *ctx, JSAtom name);
1201
+
1202
+ /* mikrojs patch: return the module's current status.
1203
+ * 0=UNLINKED, 1=LINKING, 2=LINKED, 3=EVALUATING, 4=EVALUATING_ASYNC, 5=EVALUATED.
1204
+ * Constants deliberately omitted here because the internal QuickJS enum
1205
+ * reuses the same symbol names; callers define their own mirrored names. */
1206
+ JS_EXTERN int JS_GetModuleStatus(JSContext *ctx, JSModuleDef *m);
1207
+
1208
+ /* mikrojs patch: find the loaded module whose namespace object is `ns`
1209
+ * (reverse of JS_GetModuleNamespace). Returns NULL if `ns` is not a loaded
1210
+ * module's namespace. */
1211
+ JS_EXTERN JSModuleDef *JS_FindModuleByNamespace(JSContext *ctx, JSValueConst ns);
1212
+
1193
1213
  /* associate a JSValue to a C module */
1194
1214
  JS_EXTERN int JS_SetModulePrivateValue(JSContext *ctx, JSModuleDef *m, JSValue val);
1195
1215
  JS_EXTERN JSValue JS_GetModulePrivateValue(JSContext *ctx, JSModuleDef *m);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mikrojs/quickjs",
3
- "version": "0.14.0",
3
+ "version": "0.15.0-next.20260614113448",
4
4
  "description": "QuickJS-NG engine source, shared CMake module, and qjsc bytecode compiler",
5
5
  "keywords": [
6
6
  "bytecode",
@@ -19,6 +19,7 @@
19
19
  "url": "git+https://github.com/mikrojs/mikro.git"
20
20
  },
21
21
  "files": [
22
+ "apply-patches.js",
22
23
  "deps/quickjs/*.c",
23
24
  "deps/quickjs/*.h",
24
25
  "deps/quickjs/LICENSE",
@@ -36,6 +37,8 @@
36
37
  "node": ">=24.0.0"
37
38
  },
38
39
  "scripts": {
39
- "postinstall": "node postinstall.js"
40
+ "apply-patches": "node apply-patches.js",
41
+ "postinstall": "node postinstall.js",
42
+ "verify-patches": "node verify-patches.js"
40
43
  }
41
44
  }
package/postinstall.js CHANGED
@@ -8,14 +8,15 @@
8
8
  * Skips gracefully if cc or the submodule is not available.
9
9
  */
10
10
  import {execFileSync} from 'node:child_process'
11
- import {existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync} from 'node:fs'
11
+ import {existsSync, mkdirSync, readFileSync, writeFileSync} from 'node:fs'
12
12
  import {dirname, join} from 'node:path'
13
13
  import {fileURLToPath} from 'node:url'
14
14
 
15
+ import {applyPatches} from './apply-patches.js'
16
+
15
17
  const __dirname = dirname(fileURLToPath(import.meta.url))
16
18
  const binDir = join(__dirname, 'bin')
17
19
  const qjsDir = join(__dirname, 'deps', 'quickjs')
18
- const patchesDir = join(__dirname, 'patches')
19
20
  const repoRoot = join(__dirname, '..', '..', '..')
20
21
 
21
22
  // Always sync submodule to the pinned commit
@@ -40,33 +41,6 @@ try {
40
41
  // patch is already present, skip it.
41
42
  applyPatches()
42
43
 
43
- function applyPatches() {
44
- if (!existsSync(patchesDir)) return
45
- const files = readdirSync(patchesDir)
46
- .filter((f) => f.endsWith('.patch'))
47
- .sort()
48
- for (const f of files) {
49
- const patch = join(patchesDir, f)
50
- // Probe: is the patch already applied?
51
- try {
52
- execFileSync('git', ['apply', '--reverse', '--check', patch], {
53
- cwd: qjsDir,
54
- stdio: 'pipe',
55
- })
56
- continue // already applied
57
- } catch {
58
- // not applied — fall through
59
- }
60
- try {
61
- execFileSync('git', ['apply', patch], {cwd: qjsDir, stdio: 'inherit'})
62
- console.log(`@mikrojs/quickjs: applied patch ${f}`)
63
- } catch (err) {
64
- console.error(`@mikrojs/quickjs: failed to apply ${f}`, err.message)
65
- process.exit(1)
66
- }
67
- }
68
- }
69
-
70
44
  // Determine the current submodule commit to detect changes
71
45
  let currentCommit = ''
72
46
  try {