buninu 0.2.5 → 0.3.1
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 +40 -0
- package/README.md +100 -2
- package/apps/bunmsh/CHANGELOG.md +262 -0
- package/apps/bunmsh/LICENSE +21 -0
- package/apps/bunmsh/LICENSE-MKSH +295 -0
- package/apps/bunmsh/PORTING.md +192 -0
- package/apps/bunmsh/README.md +801 -0
- package/apps/bunmsh/bunmsh +7 -0
- package/apps/bunmsh/bunshell.md +61 -0
- package/apps/bunmsh/help/README.md +1945 -0
- package/apps/bunmsh/help/alias.md +22 -0
- package/apps/bunmsh/help/basename.md +38 -0
- package/apps/bunmsh/help/builtin.md +25 -0
- package/apps/bunmsh/help/bun.md +27 -0
- package/apps/bunmsh/help/bunmsh.md +47 -0
- package/apps/bunmsh/help/cat.md +31 -0
- package/apps/bunmsh/help/catfancy.md +35 -0
- package/apps/bunmsh/help/cd.md +22 -0
- package/apps/bunmsh/help/chmod.md +27 -0
- package/apps/bunmsh/help/clear.md +21 -0
- package/apps/bunmsh/help/colon.md +22 -0
- package/apps/bunmsh/help/command.md +27 -0
- package/apps/bunmsh/help/cp.md +28 -0
- package/apps/bunmsh/help/cut.md +25 -0
- package/apps/bunmsh/help/date.md +25 -0
- package/apps/bunmsh/help/dirname.md +25 -0
- package/apps/bunmsh/help/echo.md +25 -0
- package/apps/bunmsh/help/env.md +27 -0
- package/apps/bunmsh/help/eval.md +22 -0
- package/apps/bunmsh/help/exec.md +22 -0
- package/apps/bunmsh/help/exit.md +21 -0
- package/apps/bunmsh/help/export.md +22 -0
- package/apps/bunmsh/help/false.md +22 -0
- package/apps/bunmsh/help/find.md +33 -0
- package/apps/bunmsh/help/getopts.md +28 -0
- package/apps/bunmsh/help/grep.md +34 -0
- package/apps/bunmsh/help/head.md +27 -0
- package/apps/bunmsh/help/kill.md +27 -0
- package/apps/bunmsh/help/ln.md +28 -0
- package/apps/bunmsh/help/ls.md +37 -0
- package/apps/bunmsh/help/lsbun.md +29 -0
- package/apps/bunmsh/help/lsfancy.md +37 -0
- package/apps/bunmsh/help/md5sum.md +21 -0
- package/apps/bunmsh/help/mkdir.md +27 -0
- package/apps/bunmsh/help/mktemp.md +26 -0
- package/apps/bunmsh/help/mv.md +26 -0
- package/apps/bunmsh/help/print.md +30 -0
- package/apps/bunmsh/help/printf.md +25 -0
- package/apps/bunmsh/help/pwd.md +21 -0
- package/apps/bunmsh/help/read.md +27 -0
- package/apps/bunmsh/help/readonly.md +27 -0
- package/apps/bunmsh/help/realpath.md +21 -0
- package/apps/bunmsh/help/rm.md +33 -0
- package/apps/bunmsh/help/rmdir.md +27 -0
- package/apps/bunmsh/help/sed.md +29 -0
- package/apps/bunmsh/help/seq.md +29 -0
- package/apps/bunmsh/help/serve.md +50 -0
- package/apps/bunmsh/help/set.md +26 -0
- package/apps/bunmsh/help/sha256sum.md +21 -0
- package/apps/bunmsh/help/shift.md +23 -0
- package/apps/bunmsh/help/sleep.md +25 -0
- package/apps/bunmsh/help/sort.md +29 -0
- package/apps/bunmsh/help/source.md +23 -0
- package/apps/bunmsh/help/tab.md +30 -0
- package/apps/bunmsh/help/tac.md +29 -0
- package/apps/bunmsh/help/tail.md +27 -0
- package/apps/bunmsh/help/tee.md +25 -0
- package/apps/bunmsh/help/test.md +30 -0
- package/apps/bunmsh/help/time.md +29 -0
- package/apps/bunmsh/help/touch.md +26 -0
- package/apps/bunmsh/help/tr.md +26 -0
- package/apps/bunmsh/help/true.md +22 -0
- package/apps/bunmsh/help/type.md +21 -0
- package/apps/bunmsh/help/umask.md +22 -0
- package/apps/bunmsh/help/unalias.md +28 -0
- package/apps/bunmsh/help/uname.md +31 -0
- package/apps/bunmsh/help/unset.md +28 -0
- package/apps/bunmsh/help/wc.md +27 -0
- package/apps/bunmsh/help/whence.md +26 -0
- package/apps/bunmsh/help/which.md +41 -0
- package/apps/bunmsh/help/yes.md +23 -0
- package/apps/bunmsh/package.json +26 -0
- package/apps/bunmsh/serve.js +468 -0
- package/apps/bunmsh/single-exe/README.md +402 -0
- package/apps/bunmsh/single-exe/assetsHelper.js +371 -0
- package/apps/bunmsh/single-exe/assetsLoader.mjs +85 -0
- package/apps/bunmsh/single-exe/assetsPacker.js +218 -0
- package/apps/bunmsh/single-exe/compiled.js +527 -0
- package/apps/bunmsh/single-exe/entry.mjs +17 -0
- package/apps/bunmsh/src/cat-fancy.js +117 -0
- package/apps/bunmsh/src/cat-operands.js +31 -0
- package/apps/bunmsh/src/completion.js +247 -0
- package/apps/bunmsh/src/environment.js +61 -0
- package/apps/bunmsh/src/fancy-ls.js +186 -0
- package/apps/bunmsh/src/find.js +119 -0
- package/apps/bunmsh/src/history.js +198 -0
- package/apps/bunmsh/src/main.js +776 -0
- package/apps/bunmsh/src/mouse.js +103 -0
- package/apps/bunmsh/src/shell.js +3557 -0
- package/apps/bunmsh/test/catfancy.test.js +102 -0
- package/apps/bunmsh/test/reference-source.sh +1 -0
- package/apps/bunmsh/test/reference.test.js +677 -0
- package/apps/bunmsh/test/serve/README.md +3 -0
- package/apps/bunmsh/test/serve/bad.json +1 -0
- package/apps/bunmsh/test/serve/data.json +1 -0
- package/apps/bunmsh/test/serve/data.json5 +1 -0
- package/apps/bunmsh/test/serve/data.jsonc +2 -0
- package/apps/bunmsh/test/serve/data.jsonl +2 -0
- package/apps/bunmsh/test/serve/data.ndjson +1 -0
- package/apps/bunmsh/test/serve/data.toml +3 -0
- package/apps/bunmsh/test/serve/data.ts +15 -0
- package/apps/bunmsh/test/serve/data.xml +1 -0
- package/apps/bunmsh/test/serve/data.yaml +3 -0
- package/apps/bunmsh/test/serve/data.yml +1 -0
- package/apps/bunmsh/test/serve.test.js +241 -0
- package/apps/bunmsh/test/shell.test.js +1778 -0
- package/apps/bunmsh/test/t.sh +12 -0
- package/apps/native-bridge/native-bridge.js +45 -0
- package/apps/xdg-open/xdg-open.js +74 -2
- package/bin/init.js +11 -3
- package/demo-server.js +64 -0
- package/package.json +4 -2
|
@@ -101,6 +101,51 @@ export const tts = ttsStatus
|
|
|
101
101
|
// the receiving app on modern Android.
|
|
102
102
|
export const xdgOpen = (target) => call("xdgOpen", [target])
|
|
103
103
|
|
|
104
|
+
// The host has exactly two WebViews, both alive from startup and neither ever
|
|
105
|
+
// created nor closed at runtime: WEBVIEW_CONSOLE (0) is the jsgotty terminal
|
|
106
|
+
// this shell is being rendered in, WEBVIEW_APP (1) is the app WebView, which
|
|
107
|
+
// simply starts out blank and behind. WEBVIEW_CURRENT (-1) means whichever
|
|
108
|
+
// one is in front right now, and is the default wherever an id is optional.
|
|
109
|
+
// All four functions also go by openwv/evalwv/showwv/currwv, both here and
|
|
110
|
+
// over the wire (the Java dispatcher answers to either spelling and lists
|
|
111
|
+
// both in _discover), on the same grounds as getcb/setcb above.
|
|
112
|
+
export const WEBVIEW_CURRENT = -1
|
|
113
|
+
export const WEBVIEW_CONSOLE = 0
|
|
114
|
+
export const WEBVIEW_APP = 1
|
|
115
|
+
|
|
116
|
+
// Loads url into a WebView *without* bringing it to the front, so
|
|
117
|
+
// openWebView(WEBVIEW_APP, url) while the user is looking at the terminal is
|
|
118
|
+
// a plain background load -- showWebView is the only thing that changes what
|
|
119
|
+
// is on screen. Omitting url loads nothing and just reports the id back,
|
|
120
|
+
// which is how -1 gets resolved to a real id. Returns the id loaded into.
|
|
121
|
+
export const openWebView = (id = WEBVIEW_CURRENT, url = "") =>
|
|
122
|
+
call("openWebView", [id, url])
|
|
123
|
+
export const openwv = openWebView
|
|
124
|
+
|
|
125
|
+
// Resolves to the value the expression produced -- a real number/string/
|
|
126
|
+
// object, not a string containing one -- because the host sends
|
|
127
|
+
// evaluateJavascript's own JSON text straight back as the response body.
|
|
128
|
+
// undefined, a function, and a thrown exception all arrive as null: WebView
|
|
129
|
+
// itself does not distinguish them, so return something JSON can carry when
|
|
130
|
+
// you need to tell them apart. A page that never answers is given up on by
|
|
131
|
+
// the host (inside this module's own call timeout) and comes back as an
|
|
132
|
+
// "error: ..." string rather than hanging.
|
|
133
|
+
export const evalWebView = (id, js) => call("evalWebView", [id, js])
|
|
134
|
+
export const evalwv = evalWebView
|
|
135
|
+
|
|
136
|
+
// Brings a WebView to the front. The bottom key bar (Ctrl/Alt/Shift and
|
|
137
|
+
// friends), the volume-key menu and the back key all act on whatever is in
|
|
138
|
+
// front, so this is also what re-points them. An id of -1 -- the default --
|
|
139
|
+
// switches to the next WebView rather than being a no-op, which with two of
|
|
140
|
+
// them is simply a toggle. Returns the id now in front.
|
|
141
|
+
export const showWebView = (id = WEBVIEW_CURRENT) => call("showWebView", [id])
|
|
142
|
+
export const showwv = showWebView
|
|
143
|
+
|
|
144
|
+
// The id of the WebView in front, for code that wants to put something back
|
|
145
|
+
// the way it found it.
|
|
146
|
+
export const currWebView = () => call("currWebView")
|
|
147
|
+
export const currwv = currWebView
|
|
148
|
+
|
|
104
149
|
// `bun native-bridge.js <func> [args...]`, dispatched straight to call(func,
|
|
105
150
|
// args) -- not restricted to toast/clipboardRead/clipboardWrite, exactly like
|
|
106
151
|
// calling rpcraw(func, args) would be. This is a raw RPC, equivalent to a
|
|
@@ -7,9 +7,74 @@
|
|
|
7
7
|
// the default handler" mechanism -- macOS/Windows are real OS
|
|
8
8
|
// commands/APIs, but Linux's is conventionally *also* a command named
|
|
9
9
|
// xdg-open, so this script has to be careful not to find itself on PATH.
|
|
10
|
-
import {
|
|
10
|
+
import {
|
|
11
|
+
xdgOpen as bridgeOpen, available, openWebView, showWebView,
|
|
12
|
+
} from "../native-bridge/native-bridge.js"
|
|
11
13
|
const fs = require("fs")
|
|
12
14
|
|
|
15
|
+
// MINAPK_WEBVIEW names *which* of the host app's WebViews to open into (0 is
|
|
16
|
+
// the console, 1 the app WebView, -1 whichever is in front); unset means the
|
|
17
|
+
// unchanged behavior of handing the target to the system's default handler.
|
|
18
|
+
// It is read here rather than being detected by the host, because it is an
|
|
19
|
+
// environment variable of this Bun process tree -- the host app's own process
|
|
20
|
+
// cannot see a value exported in this shell a moment ago, and the whole point
|
|
21
|
+
// of the variable is that it can be.
|
|
22
|
+
//
|
|
23
|
+
// Deliberately never Number()/parseInt(): Number("") is 0, and 0 is a
|
|
24
|
+
// perfectly valid WebView id, so an exported-but-empty MINAPK_WEBVIEW= (the
|
|
25
|
+
// usual way to switch something off) would come out meaning "open in the
|
|
26
|
+
// console" -- i.e. navigate the terminal away. parseInt would likewise turn
|
|
27
|
+
// "1x" into 1. Only a bare optionally-signed integer counts; anything else is
|
|
28
|
+
// reported and treated as unset.
|
|
29
|
+
function webViewIdFromEnv() {
|
|
30
|
+
const raw = (process.env.MINAPK_WEBVIEW ?? "").trim()
|
|
31
|
+
if (raw === "") return null
|
|
32
|
+
if (!/^-?\d+$/.test(raw)) {
|
|
33
|
+
process.stderr.write(
|
|
34
|
+
`xdg-open: ignoring MINAPK_WEBVIEW=${raw} (not an integer WebView id)\n`)
|
|
35
|
+
return null
|
|
36
|
+
}
|
|
37
|
+
return Number(raw)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Only URLs. A filesystem path stays on the bridgeOpen path even when
|
|
41
|
+
// MINAPK_WEBVIEW is set: WebView's setAllowFileAccess defaults to false from
|
|
42
|
+
// API 30 on, so a file:// URL under Buninu's home would load as a blank page,
|
|
43
|
+
// whereas the host's xdgOpen already serves that file properly through its
|
|
44
|
+
// read-only content:// provider.
|
|
45
|
+
const isUrl = (target) => /^[a-zA-Z][a-zA-Z0-9+.-]*:\/\//.test(target)
|
|
46
|
+
|
|
47
|
+
// The host answers with the id it actually used -- a JSON number -- and with
|
|
48
|
+
// a string when something went wrong: "error: no such WebView: 9" from the
|
|
49
|
+
// dispatcher, or rpc.mjs's own "Unknown func ..." when the APK is older than
|
|
50
|
+
// these functions and does not list them in _discover. rpc.mjs also *resolves*
|
|
51
|
+
// to an Error object instead of throwing when the fetch itself fails. So
|
|
52
|
+
// success is checked as "is genuinely a number", never by parsing the result:
|
|
53
|
+
// see webViewIdFromEnv above for why a coercing parse is dangerous here.
|
|
54
|
+
//
|
|
55
|
+
// showWebView(-1) means "switch to the next one", not "the one just loaded",
|
|
56
|
+
// so the switch uses the id openWebView resolved and handed back, not the id
|
|
57
|
+
// that was asked for.
|
|
58
|
+
async function openInWebView(id, url) {
|
|
59
|
+
const opened = await openWebView(id, url)
|
|
60
|
+
if (typeof opened !== "number" || !Number.isInteger(opened)) {
|
|
61
|
+
process.stderr.write(
|
|
62
|
+
`xdg-open: MINAPK_WEBVIEW=${id}: ${opened?.message ?? opened}\n`)
|
|
63
|
+
return false
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const shown = await showWebView(opened)
|
|
67
|
+
if (typeof shown !== "number" || !Number.isInteger(shown)) {
|
|
68
|
+
// Loaded but not brought to the front: say so rather than reporting
|
|
69
|
+
// success for something the user cannot see, but do not fall back to the
|
|
70
|
+
// system handler either -- the page really is loaded in that WebView.
|
|
71
|
+
process.stderr.write(
|
|
72
|
+
`xdg-open: loaded in WebView ${opened} but could not switch to it: ` +
|
|
73
|
+
`${shown?.message ?? shown}\n`)
|
|
74
|
+
}
|
|
75
|
+
return true
|
|
76
|
+
}
|
|
77
|
+
|
|
13
78
|
// Buninu's own bin/ is prepended to PATH ahead of any real system binary of
|
|
14
79
|
// the same name (same concern as xclip.js/tts.js), and this script is
|
|
15
80
|
// itself registered as Buninu's "xdg-open" command -- a naive PATH search
|
|
@@ -46,7 +111,14 @@ let ok = false
|
|
|
46
111
|
try {
|
|
47
112
|
if (process.platform === "android") {
|
|
48
113
|
if (available()) {
|
|
49
|
-
|
|
114
|
+
const webViewId = webViewIdFromEnv()
|
|
115
|
+
// A failed WebView open falls back to the system handler rather than
|
|
116
|
+
// failing outright: the request was "open this", and MINAPK_WEBVIEW
|
|
117
|
+
// only says where it would be preferred. openInWebView has already
|
|
118
|
+
// explained on stderr why it could not.
|
|
119
|
+
ok = webViewId !== null && isUrl(target)
|
|
120
|
+
? (await openInWebView(webViewId, target)) || (await bridgeOpen(target))
|
|
121
|
+
: await bridgeOpen(target)
|
|
50
122
|
} else if (Bun.which("termux-open")) {
|
|
51
123
|
// Plain Termux (no minapk) still reports platform "android" but has
|
|
52
124
|
// no PKG_BRIDGE_SOCK -- termux-open (Termux:API) does the same
|
package/bin/init.js
CHANGED
|
@@ -546,7 +546,11 @@ function shellCommandArguments(shell, command, exitAfterCmd) {
|
|
|
546
546
|
return [shell, "-c", wrapped, shell, command];
|
|
547
547
|
}
|
|
548
548
|
|
|
549
|
-
|
|
549
|
+
let startScript = pkg.scripts?.start;
|
|
550
|
+
|
|
551
|
+
if(process.argv.includes('--local'))
|
|
552
|
+
startScript = pkg.scripts?.local ;
|
|
553
|
+
|
|
550
554
|
if (typeof startScript !== "string" || !startScript.trim()) {
|
|
551
555
|
fail("package.json does not define scripts.start");
|
|
552
556
|
}
|
|
@@ -606,9 +610,13 @@ const hasExplicitPort = forwardedArguments.some((argument) =>
|
|
|
606
610
|
const portArguments = hasExplicitPort ? [] : ["-p", String(getFreePort())];
|
|
607
611
|
const childEnvironment = await createChildEnvironment(environment);
|
|
608
612
|
|
|
613
|
+
const infoSuffix =
|
|
614
|
+
process.argv.includes('--local') ?
|
|
615
|
+
'bunmsh' :
|
|
616
|
+
`port=${portArguments[1] || "user-defined"}, shell=${environment.shell}` ;
|
|
617
|
+
|
|
609
618
|
console.error(
|
|
610
|
-
`${pkg.name}@${pkg.version}: ${environment.name}/${process.arch}, ` +
|
|
611
|
-
`port=${portArguments[1] || "user-defined"}, shell=${environment.shell}`,
|
|
619
|
+
`${pkg.name}@${pkg.version}: ${environment.name}/${process.arch}, ` + infoSuffix
|
|
612
620
|
);
|
|
613
621
|
|
|
614
622
|
const child = Bun.spawn(
|
package/demo-server.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
// Smallest end-to-end demo of the WebView bridge: serve one fixed page on a
|
|
4
|
+
// port nobody picked in advance, then have xdg-open put it in the host app's
|
|
5
|
+
// app WebView and switch to it. Run it from a Buninu shell inside minapk's
|
|
6
|
+
// APK:
|
|
7
|
+
//
|
|
8
|
+
// bun demo-server.js
|
|
9
|
+
//
|
|
10
|
+
// Outside such an APK it still runs and still serves; xdg-open just falls
|
|
11
|
+
// back to whatever that platform's default handler is (see apps/xdg-open).
|
|
12
|
+
|
|
13
|
+
const BODY = "Hello 你好 こんにちは"
|
|
14
|
+
|
|
15
|
+
// port: 0 asks the OS for a free port instead of guessing one and racing
|
|
16
|
+
// whatever already has it -- Buninu's own launcher picks jsgotty's port the
|
|
17
|
+
// same way. The real number is only knowable after the listener exists, so
|
|
18
|
+
// the URL is built from server.port, never from a constant.
|
|
19
|
+
// hostname stays on loopback: this is a demo page for the WebView on this
|
|
20
|
+
// device, not something other machines on the network should reach.
|
|
21
|
+
const server = Bun.serve({
|
|
22
|
+
hostname: "127.0.0.1",
|
|
23
|
+
port: 0,
|
|
24
|
+
fetch() {
|
|
25
|
+
// charset is not optional here: without it the WebView guesses an
|
|
26
|
+
// encoding for the response, and the CJK half of BODY is what shows up
|
|
27
|
+
// as mojibake when it guesses wrong.
|
|
28
|
+
return new Response(BODY, {
|
|
29
|
+
headers: { "Content-Type": "text/plain; charset=utf-8" },
|
|
30
|
+
})
|
|
31
|
+
},
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// 127.0.0.1 rather than localhost: the listener is bound to the v4 loopback
|
|
35
|
+
// address specifically, while "localhost" can resolve to ::1 first and give
|
|
36
|
+
// the WebView a connection refused before it ever tries v4.
|
|
37
|
+
const url = `http://127.0.0.1:${server.port}`
|
|
38
|
+
console.log(`demo-server: serving ${JSON.stringify(BODY)} at ${url}`)
|
|
39
|
+
|
|
40
|
+
// Buninu puts its own bin/ on PATH, so "xdg-open" normally resolves to
|
|
41
|
+
// apps/xdg-open through the shloader. Fall back to running the script
|
|
42
|
+
// directly for the case where this file is run outside a Buninu shell (a bare
|
|
43
|
+
// `bun demo-server.js` in a checkout), where that PATH entry is not there.
|
|
44
|
+
const xdgOpen = Bun.which("xdg-open")
|
|
45
|
+
const command = xdgOpen
|
|
46
|
+
? [xdgOpen, url]
|
|
47
|
+
: [process.execPath, `${import.meta.dir}/apps/xdg-open/xdg-open.js`, url]
|
|
48
|
+
|
|
49
|
+
// MINAPK_WEBVIEW is read by xdg-open itself, not by the host app -- it names
|
|
50
|
+
// which WebView to load into (1 is the app WebView) and brings it to the
|
|
51
|
+
// front. Passed to this one child rather than exported, so it changes nothing
|
|
52
|
+
// for the rest of the session.
|
|
53
|
+
const child = Bun.spawn(command, {
|
|
54
|
+
env: { ...process.env, MINAPK_WEBVIEW: "1" },
|
|
55
|
+
stdout: "inherit",
|
|
56
|
+
stderr: "inherit",
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
if (await child.exited !== 0)
|
|
60
|
+
console.error(`demo-server: ${command[0]} exited with ${child.exitCode}; page is still served at ${url}`)
|
|
61
|
+
|
|
62
|
+
// The server has to outlive the open: the WebView fetches the page after
|
|
63
|
+
// xdg-open has already returned. Ctrl-C to stop.
|
|
64
|
+
console.log("demo-server: Ctrl-C to stop")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "buninu",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Buninu is a portable, self-bootstrapping Unix-like userspace built on Bun that runs across operating systems",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./bin/init.js",
|
|
@@ -23,10 +23,12 @@
|
|
|
23
23
|
"macos": null,
|
|
24
24
|
"windows": null
|
|
25
25
|
},
|
|
26
|
-
"exitAfterCmd": false
|
|
26
|
+
"exitAfterCmd": false,
|
|
27
|
+
"backToConsole": true
|
|
27
28
|
},
|
|
28
29
|
"scripts": {
|
|
29
30
|
"start": "bun ./apps/jsgotty/gotty.js --reconnect -r -w --webgl -a 127.0.0.1",
|
|
31
|
+
"local": "bun ./apps/bunmsh/bunmsh -c 'exec bunmsh'",
|
|
30
32
|
"check": "node --check ./bin/init.js"
|
|
31
33
|
}
|
|
32
34
|
}
|