bare-stow 0.2.0 → 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/README.md +20 -2
- package/index.d.ts +18 -2
- package/index.js +35 -7
- package/lib/assets.js +4 -4
- package/lib/graph.js +6 -8
- package/lib/language/kotlin.js +41 -11
- package/lib/link.js +17 -9
- package/lib/rpc/bare-rpc.js +17 -90
- package/lib/target/bare-kit.js +121 -27
- package/lib/target/bare-sidecar.js +22 -13
- package/lib/target/bare-worker.js +4 -4
- package/lib/target/javascript.js +29 -12
- package/lib/target/swift.js +68 -24
- package/package.json +19 -19
package/README.md
CHANGED
|
@@ -69,11 +69,29 @@ The bundle imports `bare-stow-runtime`, and the RPC library if a `server` is req
|
|
|
69
69
|
|
|
70
70
|
`start()` accepts `timeout`, how long to wait in milliseconds for the bundle to signal ready, and `grace`, how long a terminating bundle gets to run `stop()` and exit before it's killed. `timeout` defaults to waiting indefinitely and `grace` to 5 seconds. On Windows, a sidecar is killed along with its host, so `stop()` only runs when the host terminates it.
|
|
71
71
|
|
|
72
|
+
For `bare-sidecar` and `bare-worker`, the harness also exports `connect()`, which takes a sidecar or worker that the host started itself, such as one running a development entry with arguments, and resolves like `start()`. It accepts the same options, except those of the sidecar or worker. As the entry doesn't go through `stow()`, it attaches to the IPC itself, as described in <https://github.com/holepunchto/bare-stow-runtime>:
|
|
73
|
+
|
|
74
|
+
```js
|
|
75
|
+
import runtime from 'bare-stow-runtime/process'
|
|
76
|
+
|
|
77
|
+
const ipc = runtime.attach(Bare.IPC)
|
|
78
|
+
|
|
79
|
+
ipc.run(() => import('./core.js'), ipc)
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```js
|
|
83
|
+
const sidecar = new Sidecar('./dev.js', ['--verbose'], { stdio: 'inherit' })
|
|
84
|
+
|
|
85
|
+
const { ipc } = await harness.connect(sidecar)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`stow()` and `link()` read the module graph from disk by default. To read it from elsewhere, such as modules that aren't written to disk, pass `readModule`, `listPrefix`, and `resolveModule`. Generated files are still written to disk. A `readModule` that changes the type of a module also passes `aliases`, as for <https://github.com/holepunchto/bare-pack>, such as `{ '.mjs': '.js' }` to have ES modules converted to CommonJS loaded as such. They're merged over the aliases that `bare-stow` uses for TypeScript.
|
|
89
|
+
|
|
72
90
|
## Targets
|
|
73
91
|
|
|
74
92
|
- `bare-sidecar` runs the bundle in a separate process using <https://github.com/holepunchto/bare-sidecar>, with the `javascript`, `python`, and `swift` languages on desktop. In JavaScript and Python, `start()` also accepts the options of `bare-sidecar`.
|
|
75
93
|
- `bare-worker` runs the bundle in a thread of a Bare host using <https://github.com/holepunchto/bare-worker>, with the `javascript` language.
|
|
76
|
-
- `bare-kit` runs the bundle in a worklet using <https://github.com/holepunchto/bare-kit>, with the `swift` language on macOS and iOS and the `kotlin` language on Android. `start()` also accepts the options of the worklet and returns it alongside `ipc`, and
|
|
94
|
+
- `bare-kit` runs the bundle in a worklet using <https://github.com/holepunchto/bare-kit>, with the `swift` language on macOS and iOS and the `kotlin` language on Android and the JVM. `start()` also accepts the options of the worklet and returns it alongside `ipc`, and on Android takes the `Context` first. In Kotlin, the worklet is closed once it exits, so it needs no closing of its own, and in Swift it's released with its last reference. Native addons are linked with <https://github.com/holepunchto/bare-link> and declared in the manifest, and assets are written next to the bundle. With the target option `linking` set to `'app'`, both are left to `link()` instead. On the JVM, addons and assets are written next to the bundle as files, which `start()` extracts along with the bundle to a temporary directory, so `linking` must be `'package'`.
|
|
77
95
|
|
|
78
96
|
## Linking
|
|
79
97
|
|
|
@@ -101,7 +119,7 @@ The language lays out the package and the target adds what loads the addons. For
|
|
|
101
119
|
The harness is generated in one of these languages, selected with `language` and configured with `languageOptions`:
|
|
102
120
|
|
|
103
121
|
- `javascript` writes an npm package. The `module` option is either `'esm'`, the default, or `'cjs'`.
|
|
104
|
-
- `kotlin` writes
|
|
122
|
+
- `kotlin` writes a Gradle module. The `package` option names the package of the generated code and is required. The `platform` option is either `'android'`, the default, for an Android library module, or `'jvm'` for a Kotlin/JVM library module on Linux, macOS, and Windows. The dependencies are published to JitPack (<https://jitpack.io>), so add it to the repositories of the application.
|
|
105
123
|
- `python` writes a Python package.
|
|
106
124
|
- `swift` writes a Swift package.
|
|
107
125
|
|
package/index.d.ts
CHANGED
|
@@ -31,6 +31,11 @@ interface Layout {
|
|
|
31
31
|
addons?: URL
|
|
32
32
|
/** The directory the assets of linked bundles are linked into, or unset if unsupported. */
|
|
33
33
|
resources?: URL
|
|
34
|
+
/**
|
|
35
|
+
* Whether the files written next to the bundle are listed in `<bundle>.files`, one `/<path>` per
|
|
36
|
+
* line, for a harness that extracts them along with the bundle.
|
|
37
|
+
*/
|
|
38
|
+
listing?: boolean
|
|
34
39
|
}
|
|
35
40
|
|
|
36
41
|
/** The context passed to a language's `manifest()`. */
|
|
@@ -68,6 +73,8 @@ interface JavaScriptOptions {
|
|
|
68
73
|
interface KotlinOptions {
|
|
69
74
|
/** The package of the generated code, such as `'com.example.core'`, which is also its namespace. */
|
|
70
75
|
package: string
|
|
76
|
+
/** The platform of the module, either `'android'`, the default, or `'jvm'`. */
|
|
77
|
+
platform: 'android' | 'jvm'
|
|
71
78
|
}
|
|
72
79
|
|
|
73
80
|
type LanguageName = 'javascript' | 'kotlin' | 'python' | 'swift'
|
|
@@ -269,7 +276,7 @@ interface RPC {
|
|
|
269
276
|
}
|
|
270
277
|
|
|
271
278
|
/** Options for `stow()`. */
|
|
272
|
-
interface StowOptions extends Omit<PackOptions, 'offload' | 'linked' | 'resolve'
|
|
279
|
+
interface StowOptions extends Omit<PackOptions, 'offload' | 'linked' | 'resolve'> {
|
|
273
280
|
/** The language of the harness (or its name). Defaults to `javascript`. */
|
|
274
281
|
language?: Language | LanguageName
|
|
275
282
|
/** The options given to the language, such as `{ module: 'cjs' }` for `javascript`. */
|
|
@@ -295,6 +302,15 @@ interface StowOptions extends Omit<PackOptions, 'offload' | 'linked' | 'resolve'
|
|
|
295
302
|
* @param name - The language name to resolve to a `Language`.
|
|
296
303
|
*/
|
|
297
304
|
resolveLanguage?(name: string): Language
|
|
305
|
+
/**
|
|
306
|
+
* Read the source of the module at `url`, or `null` if there is none, such as to stow modules
|
|
307
|
+
* that aren't written to disk. Defaults to reading the file.
|
|
308
|
+
*/
|
|
309
|
+
readModule?: (url: URL) => Promise<Uint8Array | string | null>
|
|
310
|
+
/** List the files under the prefix `url`. Defaults to listing the directory. */
|
|
311
|
+
listPrefix?: (url: URL) => AsyncIterable<URL>
|
|
312
|
+
/** Resolve the module at `url`. Defaults to its real path, following symbolic links. */
|
|
313
|
+
resolveModule?: (url: URL) => Promise<URL>
|
|
298
314
|
}
|
|
299
315
|
|
|
300
316
|
/** Options for `stow.link()`, which shape the graphs as for `stow()`. */
|
|
@@ -325,7 +341,7 @@ interface StowArtifact {
|
|
|
325
341
|
* @returns An async generator that yields each written artifact's `url` as it is produced: any
|
|
326
342
|
* addons linked into the package, the harness and its type declaration, the RPC client and its type
|
|
327
343
|
* declaration, the package manifest, the bundle along with its `base`, and then any assets of a
|
|
328
|
-
* linked bundle
|
|
344
|
+
* linked bundle, any offloaded addon or asset files, and the listing of those files.
|
|
329
345
|
* @throws The `target` argument is missing.
|
|
330
346
|
* @throws The `out` argument is missing.
|
|
331
347
|
* @throws A language option is unknown or invalid, or `out` is not valid as a package.
|
package/index.js
CHANGED
|
@@ -23,10 +23,13 @@ module.exports = exports = async function* stow(entry, target, out, opts = {}) {
|
|
|
23
23
|
resolveTarget,
|
|
24
24
|
resolveRPC,
|
|
25
25
|
resolveLanguage,
|
|
26
|
+
readModule = fs.readModule,
|
|
27
|
+
listPrefix = fs.listPrefix,
|
|
28
|
+
resolveModule = fs.resolveModule,
|
|
26
29
|
...packOpts
|
|
27
30
|
} = opts
|
|
28
31
|
|
|
29
|
-
entry = await
|
|
32
|
+
entry = await resolveModule(new URL(entry))
|
|
30
33
|
out = new URL(out)
|
|
31
34
|
|
|
32
35
|
if (!out.pathname.endsWith('/')) out.pathname += '/'
|
|
@@ -81,7 +84,11 @@ module.exports = exports = async function* stow(entry, target, out, opts = {}) {
|
|
|
81
84
|
|
|
82
85
|
const main = shim(entry, target.isolation, server)
|
|
83
86
|
|
|
84
|
-
const
|
|
87
|
+
const hooks = {
|
|
88
|
+
readModule: graph.readModule(main.modules, readModule),
|
|
89
|
+
listPrefix,
|
|
90
|
+
resolveModule
|
|
91
|
+
}
|
|
85
92
|
|
|
86
93
|
const offloaded = []
|
|
87
94
|
|
|
@@ -96,9 +103,9 @@ module.exports = exports = async function* stow(entry, target, out, opts = {}) {
|
|
|
96
103
|
let bundle = await pack(
|
|
97
104
|
main.url,
|
|
98
105
|
{ ...traversal, linked: target.linked, offload: target.offload },
|
|
99
|
-
readModule,
|
|
100
|
-
|
|
101
|
-
|
|
106
|
+
hooks.readModule,
|
|
107
|
+
hooks.listPrefix,
|
|
108
|
+
hooks.resolveModule,
|
|
102
109
|
writeFile
|
|
103
110
|
)
|
|
104
111
|
|
|
@@ -109,7 +116,7 @@ module.exports = exports = async function* stow(entry, target, out, opts = {}) {
|
|
|
109
116
|
let unpacked = []
|
|
110
117
|
|
|
111
118
|
if (target.linked) {
|
|
112
|
-
const result = await assets.unpack(bundle, base, name)
|
|
119
|
+
const result = await assets.unpack(bundle, base, name, hooks.readModule)
|
|
113
120
|
|
|
114
121
|
bundle = result.bundle
|
|
115
122
|
|
|
@@ -126,6 +133,8 @@ module.exports = exports = async function* stow(entry, target, out, opts = {}) {
|
|
|
126
133
|
artifacts.push(new URL(name + '/', bundleURL), new URL(name + '.assets', bundleURL))
|
|
127
134
|
}
|
|
128
135
|
|
|
136
|
+
if (layout.listing) artifacts.push(listingURL(bundleURL))
|
|
137
|
+
|
|
129
138
|
const addons = []
|
|
130
139
|
|
|
131
140
|
// Targets that leave linking to the application use `link()` instead.
|
|
@@ -134,7 +143,7 @@ module.exports = exports = async function* stow(entry, target, out, opts = {}) {
|
|
|
134
143
|
throw new Error(`Language '${language.name}' does not support linked addons`)
|
|
135
144
|
}
|
|
136
145
|
|
|
137
|
-
for await (const url of graph.link([main.url], layout.addons, traversal,
|
|
146
|
+
for await (const url of graph.link([main.url], layout.addons, traversal, hooks)) {
|
|
138
147
|
addons.push(url)
|
|
139
148
|
yield { url }
|
|
140
149
|
}
|
|
@@ -179,6 +188,25 @@ module.exports = exports = async function* stow(entry, target, out, opts = {}) {
|
|
|
179
188
|
await fs.writeFile(artifact.url, artifact.source)
|
|
180
189
|
yield { url: artifact.url }
|
|
181
190
|
}
|
|
191
|
+
|
|
192
|
+
if (layout.listing) {
|
|
193
|
+
const url = listingURL(bundleURL)
|
|
194
|
+
const dir = path.posix.dirname(bundleURL.pathname)
|
|
195
|
+
|
|
196
|
+
const listing = offloaded.map(
|
|
197
|
+
(artifact) => '/' + decodeURIComponent(path.posix.relative(dir, artifact.url.pathname)) + '\n'
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
await fs.writeFile(url, listing.join(''))
|
|
201
|
+
yield { url }
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// Languages that package the bundle where the runtime can't read it, such as
|
|
206
|
+
// in a jar, list the files beside it, one `/<path>` per line, for the harness to
|
|
207
|
+
// extract along with the bundle.
|
|
208
|
+
function listingURL(bundleURL) {
|
|
209
|
+
return new URL(path.basename(bundleURL.pathname) + '.files', bundleURL)
|
|
182
210
|
}
|
|
183
211
|
|
|
184
212
|
function resolveBase(base, bundle) {
|
package/lib/assets.js
CHANGED
|
@@ -7,11 +7,11 @@ exports.app = 'bare-stow'
|
|
|
7
7
|
|
|
8
8
|
// Assets are laid out the way Bare Kit expects: the assets of `<name>.bundle`
|
|
9
9
|
// live in `<name>/`, listed in `<name>.assets` for platforms that extract them.
|
|
10
|
-
exports.unpack = async function (bundle, base, name) {
|
|
10
|
+
exports.unpack = async function (bundle, base, name, readModule) {
|
|
11
11
|
const assets = []
|
|
12
12
|
|
|
13
13
|
bundle = await unpack(bundle, { files: false, assets: true }, async (key) => {
|
|
14
|
-
const relative = await exports.path(new URL('.' + key, base))
|
|
14
|
+
const relative = await exports.path(new URL('.' + key, base), readModule)
|
|
15
15
|
|
|
16
16
|
assets.push({ path: relative, source: bundle.read(key) })
|
|
17
17
|
|
|
@@ -23,13 +23,13 @@ exports.unpack = async function (bundle, base, name) {
|
|
|
23
23
|
|
|
24
24
|
// Assets of dependencies keep their path from `node_modules`, and others their
|
|
25
25
|
// path within their package, so that every bundle places an asset alike.
|
|
26
|
-
exports.path = async function (url) {
|
|
26
|
+
exports.path = async function (url, readModule) {
|
|
27
27
|
const nm = url.pathname.indexOf('/node_modules/')
|
|
28
28
|
|
|
29
29
|
if (nm >= 0) return url.pathname.slice(nm + 1)
|
|
30
30
|
|
|
31
31
|
for (let dir = new URL('./', url); ; dir = new URL('../', dir)) {
|
|
32
|
-
if (await
|
|
32
|
+
if ((await readModule(new URL('package.json', dir))) !== null) {
|
|
33
33
|
return url.pathname.slice(dir.pathname.length)
|
|
34
34
|
}
|
|
35
35
|
|
package/lib/graph.js
CHANGED
|
@@ -2,7 +2,6 @@ const { fileURLToPath, pathToFileURL } = require('url')
|
|
|
2
2
|
const bareLink = require('bare-link')
|
|
3
3
|
const strip = require('bare-type-stripper')
|
|
4
4
|
const { resolve } = require('bare-module-traverse')
|
|
5
|
-
const fs = require('./fs')
|
|
6
5
|
const targets = require('./target')
|
|
7
6
|
const languages = require('./language')
|
|
8
7
|
const { select } = require('./provider')
|
|
@@ -55,17 +54,18 @@ exports.traversal = function traversal(opts, hosts) {
|
|
|
55
54
|
aliases: {
|
|
56
55
|
'.ts': '.js',
|
|
57
56
|
'.mts': '.mjs',
|
|
58
|
-
'.cts': '.cjs'
|
|
57
|
+
'.cts': '.cjs',
|
|
58
|
+
...opts.aliases
|
|
59
59
|
},
|
|
60
60
|
hosts
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
exports.readModule = function readModule(modules) {
|
|
64
|
+
exports.readModule = function readModule(modules, read) {
|
|
65
65
|
return async function (url) {
|
|
66
66
|
if (modules.has(url.href)) return modules.get(url.href)
|
|
67
67
|
|
|
68
|
-
const source = await
|
|
68
|
+
const source = await read(url)
|
|
69
69
|
|
|
70
70
|
if (source === null) return null
|
|
71
71
|
|
|
@@ -77,16 +77,14 @@ exports.readModule = function readModule(modules) {
|
|
|
77
77
|
|
|
78
78
|
// The layout must not depend on the hosts, as Swift packages only accept
|
|
79
79
|
// XCFrameworks. Resources linked outside of the directory are intermediate.
|
|
80
|
-
exports.link = async function* link(entries, dir, traversal,
|
|
80
|
+
exports.link = async function* link(entries, dir, traversal, hooks) {
|
|
81
81
|
for await (const resource of bareLink(
|
|
82
82
|
entries.map((url) => fileURLToPath(url)),
|
|
83
83
|
{
|
|
84
84
|
...traversal,
|
|
85
85
|
out: fileURLToPath(dir),
|
|
86
86
|
alwaysMultiHost: true,
|
|
87
|
-
|
|
88
|
-
listPrefix: fs.listPrefix,
|
|
89
|
-
resolveModule: fs.resolveModule
|
|
87
|
+
...hooks
|
|
90
88
|
}
|
|
91
89
|
)) {
|
|
92
90
|
const url = pathToFileURL(resource)
|
package/lib/language/kotlin.js
CHANGED
|
@@ -3,7 +3,7 @@ const path = require('path')
|
|
|
3
3
|
exports.name = 'kotlin'
|
|
4
4
|
|
|
5
5
|
exports.options = function options(given = {}) {
|
|
6
|
-
const { package: name, ...unknown } = given
|
|
6
|
+
const { package: name, platform = 'android', ...unknown } = given
|
|
7
7
|
|
|
8
8
|
for (const option of Object.keys(unknown)) {
|
|
9
9
|
throw new Error(`Unknown option '${option}' for language 'kotlin'`)
|
|
@@ -15,19 +15,33 @@ exports.options = function options(given = {}) {
|
|
|
15
15
|
throw new Error(`'${name}' is not a valid Kotlin package name`)
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
if (platform !== 'android' && platform !== 'jvm') {
|
|
19
|
+
throw new Error(`Option 'platform' must be 'android' or 'jvm', got '${platform}'`)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return { package: name, platform }
|
|
19
23
|
}
|
|
20
24
|
|
|
21
|
-
// The bundle is an asset of the module, under the directory of its
|
|
22
|
-
//
|
|
25
|
+
// The bundle is an asset or resource of the module, under the directory of its
|
|
26
|
+
// package as those of every module of the application share one namespace. A
|
|
27
|
+
// JVM module can't read its resources as files, so it lists the files beside
|
|
28
|
+
// the bundle for the harness to extract.
|
|
23
29
|
exports.layout = function layout(out, options) {
|
|
24
30
|
const dir = options.package.replace(/\./g, '/')
|
|
25
31
|
|
|
26
|
-
|
|
32
|
+
const module = {
|
|
27
33
|
harness: new URL(`src/main/kotlin/${dir}/Harness.kt`, out),
|
|
28
|
-
artifacts: new URL(`src/main/assets/${dir}/`, out),
|
|
29
34
|
name: options.package,
|
|
30
|
-
manifest: new URL('build.gradle', out)
|
|
35
|
+
manifest: new URL('build.gradle', out)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (options.platform === 'jvm') {
|
|
39
|
+
return { ...module, artifacts: new URL(`src/main/resources/${dir}/`, out), listing: true }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
...module,
|
|
44
|
+
artifacts: new URL(`src/main/assets/${dir}/`, out),
|
|
31
45
|
addons: new URL('src/main/addons/', out),
|
|
32
46
|
resources: new URL('src/main/assets/', out)
|
|
33
47
|
}
|
|
@@ -37,11 +51,14 @@ exports.sibling = function sibling(url, name) {
|
|
|
37
51
|
return new URL(capitalize(name) + '.kt', url)
|
|
38
52
|
}
|
|
39
53
|
|
|
40
|
-
// Assets are referenced by their path within
|
|
54
|
+
// Assets and resources are referenced by their path within those of the
|
|
55
|
+
// application.
|
|
41
56
|
exports.reference = function reference(from, to) {
|
|
42
|
-
const
|
|
57
|
+
for (const dir of ['/src/main/assets/', '/src/main/resources/']) {
|
|
58
|
+
const index = to.pathname.lastIndexOf(dir)
|
|
43
59
|
|
|
44
|
-
|
|
60
|
+
if (index !== -1) return to.pathname.slice(index + dir.length)
|
|
61
|
+
}
|
|
45
62
|
|
|
46
63
|
return path.posix.relative(path.posix.dirname(from.pathname), to.pathname)
|
|
47
64
|
}
|
|
@@ -49,11 +66,24 @@ exports.reference = function reference(from, to) {
|
|
|
49
66
|
// The dependencies of Kotlin generators map Maven coordinates without a version
|
|
50
67
|
// to the version that the module uses. They are part of the API of the module,
|
|
51
68
|
// as the harness exposes their types.
|
|
52
|
-
exports.manifest = function manifest({ name, dependencies, addons = [] }) {
|
|
69
|
+
exports.manifest = function manifest({ name, dependencies, options = {}, addons = [] }) {
|
|
53
70
|
const apis = Object.entries(dependencies).map(
|
|
54
71
|
([coordinates, version]) => ` api ${JSON.stringify(coordinates + ':' + version)}`
|
|
55
72
|
)
|
|
56
73
|
|
|
74
|
+
if (options.platform === 'jvm') {
|
|
75
|
+
return `\
|
|
76
|
+
plugins {
|
|
77
|
+
id "java-library"
|
|
78
|
+
id "org.jetbrains.kotlin.jvm"
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
dependencies {
|
|
82
|
+
${apis.join('\n')}
|
|
83
|
+
}
|
|
84
|
+
`
|
|
85
|
+
}
|
|
86
|
+
|
|
57
87
|
// Linked addons are native libraries in a directory per ABI, along with the
|
|
58
88
|
// archives of any Java classes they use.
|
|
59
89
|
const libraries = [
|
package/lib/link.js
CHANGED
|
@@ -18,10 +18,13 @@ module.exports = async function* link(entries, target, out, opts = {}) {
|
|
|
18
18
|
resolveTarget,
|
|
19
19
|
resolveRPC,
|
|
20
20
|
resolveLanguage,
|
|
21
|
+
readModule = fs.readModule,
|
|
22
|
+
listPrefix = fs.listPrefix,
|
|
23
|
+
resolveModule = fs.resolveModule,
|
|
21
24
|
...packOpts
|
|
22
25
|
} = opts
|
|
23
26
|
|
|
24
|
-
entries = await Promise.all([].concat(entries).map((entry) =>
|
|
27
|
+
entries = await Promise.all([].concat(entries).map((entry) => resolveModule(new URL(entry))))
|
|
25
28
|
|
|
26
29
|
if (entries.length === 0) throw new Error("'entries' must not be empty")
|
|
27
30
|
out = new URL(out)
|
|
@@ -61,7 +64,12 @@ module.exports = async function* link(entries, target, out, opts = {}) {
|
|
|
61
64
|
const main = shim(entries[0], target.isolation, server)
|
|
62
65
|
|
|
63
66
|
const traversal = graph.traversal(packOpts, hosts)
|
|
64
|
-
|
|
67
|
+
|
|
68
|
+
const hooks = {
|
|
69
|
+
readModule: graph.readModule(main.modules, readModule),
|
|
70
|
+
listPrefix,
|
|
71
|
+
resolveModule
|
|
72
|
+
}
|
|
65
73
|
|
|
66
74
|
const addons = []
|
|
67
75
|
|
|
@@ -69,13 +77,13 @@ module.exports = async function* link(entries, target, out, opts = {}) {
|
|
|
69
77
|
[main.url, ...entries.slice(1)],
|
|
70
78
|
layout.addons,
|
|
71
79
|
traversal,
|
|
72
|
-
|
|
80
|
+
hooks
|
|
73
81
|
)) {
|
|
74
82
|
addons.push(url)
|
|
75
83
|
yield { url }
|
|
76
84
|
}
|
|
77
85
|
|
|
78
|
-
const linked = await collectAssets(entries, traversal,
|
|
86
|
+
const linked = await collectAssets(entries, traversal, hooks)
|
|
79
87
|
|
|
80
88
|
if (linked.length > 0) {
|
|
81
89
|
if (!layout.resources) {
|
|
@@ -109,21 +117,21 @@ module.exports = async function* link(entries, target, out, opts = {}) {
|
|
|
109
117
|
}
|
|
110
118
|
}
|
|
111
119
|
|
|
112
|
-
async function collectAssets(entries, traversal,
|
|
120
|
+
async function collectAssets(entries, traversal, hooks) {
|
|
113
121
|
const collected = new Map()
|
|
114
122
|
|
|
115
123
|
for (const entry of entries) {
|
|
116
124
|
const bundle = await pack(
|
|
117
125
|
entry,
|
|
118
126
|
{ ...traversal, linked: true },
|
|
119
|
-
readModule,
|
|
120
|
-
|
|
121
|
-
|
|
127
|
+
hooks.readModule,
|
|
128
|
+
hooks.listPrefix,
|
|
129
|
+
hooks.resolveModule
|
|
122
130
|
)
|
|
123
131
|
|
|
124
132
|
for (const key of bundle.assets) {
|
|
125
133
|
const url = new URL(key)
|
|
126
|
-
const relative = await assets.path(url)
|
|
134
|
+
const relative = await assets.path(url, hooks.readModule)
|
|
127
135
|
const source = bundle.read(key)
|
|
128
136
|
const existing = collected.get(relative)
|
|
129
137
|
|
package/lib/rpc/bare-rpc.js
CHANGED
|
@@ -15,21 +15,18 @@ import kotlinx.coroutines.CoroutineScope
|
|
|
15
15
|
import kotlinx.coroutines.Dispatchers
|
|
16
16
|
import to.holepunch.bare.rpc.CommandRouter
|
|
17
17
|
import to.holepunch.bare.rpc.Rpc
|
|
18
|
+
import to.holepunch.bare.rpc.Transport
|
|
18
19
|
import to.holepunch.bare.stow.host.Ipc
|
|
19
20
|
|
|
20
|
-
typealias Client = Rpc
|
|
21
|
-
|
|
22
21
|
typealias Handlers = CommandRouter.() -> Unit
|
|
23
22
|
|
|
24
|
-
internal fun attach(ipc: Ipc, handlers: Handlers):
|
|
23
|
+
internal fun attach(ipc: Ipc, handlers: Handlers): Rpc {
|
|
25
24
|
val router = CommandRouter().apply(handlers)
|
|
26
25
|
|
|
27
26
|
return Rpc(
|
|
28
|
-
ipc.data,
|
|
29
|
-
ipc::write,
|
|
27
|
+
Transport(ipc.data, ipc::write, ipc::close),
|
|
30
28
|
CoroutineScope(Dispatchers.Default),
|
|
31
|
-
|
|
32
|
-
onRequest = router::dispatch,
|
|
29
|
+
router,
|
|
33
30
|
)
|
|
34
31
|
}
|
|
35
32
|
`
|
|
@@ -37,8 +34,8 @@ internal fun attach(ipc: Ipc, handlers: Handlers): Client {
|
|
|
37
34
|
return {
|
|
38
35
|
source,
|
|
39
36
|
dependencies: {
|
|
40
|
-
'com.github.holepunchto.bare-rpc-kotlin:bare-rpc-kotlin': 'v0.
|
|
41
|
-
'com.github.holepunchto.bare-stow-host-kotlin:bare-stow-host-kotlin': 'v0.
|
|
37
|
+
'com.github.holepunchto.bare-rpc-kotlin:bare-rpc-kotlin': 'v0.5.0',
|
|
38
|
+
'com.github.holepunchto.bare-stow-host-kotlin:bare-stow-host-kotlin': 'v0.2.1'
|
|
42
39
|
}
|
|
43
40
|
}
|
|
44
41
|
}
|
|
@@ -49,80 +46,16 @@ import BareRPC
|
|
|
49
46
|
import BareStowHost
|
|
50
47
|
import Foundation
|
|
51
48
|
|
|
52
|
-
public final class Client: @unchecked Sendable {
|
|
53
|
-
public let rpc: RPC
|
|
54
|
-
|
|
55
|
-
private let delegate: Delegate
|
|
56
|
-
|
|
57
|
-
fileprivate init(rpc: RPC, delegate: Delegate) {
|
|
58
|
-
self.rpc = rpc
|
|
59
|
-
self.delegate = delegate
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
public func request(_ command: UInt, data: Data? = nil) async throws -> Data? {
|
|
63
|
-
try await rpc.request(command, data: data)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
public func event(_ command: UInt, data: Data? = nil) async {
|
|
67
|
-
await rpc.event(command, data: data)
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
49
|
public typealias Handlers = (CommandRouter) -> Void
|
|
72
50
|
|
|
73
|
-
|
|
51
|
+
extension BareStowHost.IPC: @retroactive BareRPC.Transport {}
|
|
52
|
+
|
|
53
|
+
func attach(_ ipc: IPC, _ handlers: Handlers?) -> RPC {
|
|
74
54
|
let router = CommandRouter()
|
|
75
55
|
|
|
76
56
|
handlers?(router)
|
|
77
57
|
|
|
78
|
-
|
|
79
|
-
let rpc = RPC(delegate: delegate)
|
|
80
|
-
|
|
81
|
-
Task {
|
|
82
|
-
for await frame in delegate.frames {
|
|
83
|
-
try? await ipc.write(frame)
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
Task {
|
|
88
|
-
do {
|
|
89
|
-
for try await data in ipc {
|
|
90
|
-
await rpc.receive(data)
|
|
91
|
-
}
|
|
92
|
-
} catch {}
|
|
93
|
-
|
|
94
|
-
delegate.finish()
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return Client(rpc: rpc, delegate: delegate)
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
private final class Delegate: RPCDelegate, @unchecked Sendable {
|
|
101
|
-
let router: CommandRouter
|
|
102
|
-
let frames: AsyncStream<Data>
|
|
103
|
-
|
|
104
|
-
private let continuation: AsyncStream<Data>.Continuation
|
|
105
|
-
|
|
106
|
-
init(router: CommandRouter) {
|
|
107
|
-
self.router = router
|
|
108
|
-
(frames, continuation) = AsyncStream.makeStream()
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
func rpc(_ rpc: RPC, send data: Data) {
|
|
112
|
-
continuation.yield(data)
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
func rpc(_ rpc: RPC, didReceiveRequest request: IncomingRequest) async throws {
|
|
116
|
-
await router.dispatch(request)
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
func rpc(_ rpc: RPC, didReceiveEvent event: IncomingEvent) async {
|
|
120
|
-
await router.dispatch(event)
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
func finish() {
|
|
124
|
-
continuation.finish()
|
|
125
|
-
}
|
|
58
|
+
return RPC(ipc, handler: router)
|
|
126
59
|
}
|
|
127
60
|
`
|
|
128
61
|
|
|
@@ -131,12 +64,12 @@ private final class Delegate: RPCDelegate, @unchecked Sendable {
|
|
|
131
64
|
dependencies: {
|
|
132
65
|
'bare-rpc-swift': {
|
|
133
66
|
url: 'https://github.com/holepunchto/bare-rpc-swift',
|
|
134
|
-
from: '
|
|
67
|
+
from: '2.0.0',
|
|
135
68
|
products: ['BareRPC']
|
|
136
69
|
},
|
|
137
70
|
'bare-stow-host-swift': {
|
|
138
71
|
url: 'https://github.com/holepunchto/bare-stow-host-swift',
|
|
139
|
-
from: '0.
|
|
72
|
+
from: '0.2.0',
|
|
140
73
|
products: ['BareStowHost']
|
|
141
74
|
}
|
|
142
75
|
}
|
|
@@ -150,28 +83,22 @@ from collections.abc import Callable
|
|
|
150
83
|
from bare_rpc import RPC, CommandRouter
|
|
151
84
|
from bare_stow_host import IPC
|
|
152
85
|
|
|
153
|
-
Client = RPC
|
|
154
|
-
|
|
155
86
|
Handlers = Callable[[CommandRouter], None]
|
|
156
87
|
|
|
157
88
|
|
|
158
|
-
def attach(ipc: IPC, handlers: Handlers) ->
|
|
89
|
+
def attach(ipc: IPC, handlers: Handlers) -> RPC:
|
|
159
90
|
router = CommandRouter()
|
|
160
91
|
|
|
161
92
|
handlers(router)
|
|
162
93
|
|
|
163
|
-
return RPC(
|
|
164
|
-
ipc,
|
|
165
|
-
on_request=router.dispatch_request,
|
|
166
|
-
on_event=router.dispatch_event,
|
|
167
|
-
)
|
|
94
|
+
return RPC(ipc, handler=router)
|
|
168
95
|
`
|
|
169
96
|
|
|
170
97
|
return {
|
|
171
98
|
source,
|
|
172
99
|
dependencies: {
|
|
173
|
-
'bare-rpc': '~=0.
|
|
174
|
-
'bare-stow-host': '~=0.
|
|
100
|
+
'bare-rpc': '~=0.2.0',
|
|
101
|
+
'bare-stow-host': '~=0.2.0'
|
|
175
102
|
}
|
|
176
103
|
}
|
|
177
104
|
}
|
|
@@ -225,5 +152,5 @@ export type Handlers = (router: import('bare-rpc').CommandRouter) => void
|
|
|
225
152
|
export function attach(ipc: import('bare-stow-host').IPC, handlers: Handlers): import('bare-rpc')
|
|
226
153
|
`
|
|
227
154
|
|
|
228
|
-
return { source, types, dependencies: { 'bare-rpc': '^1.
|
|
155
|
+
return { source, types, dependencies: { 'bare-rpc': '^1.5.0' } }
|
|
229
156
|
}
|
package/lib/target/bare-kit.js
CHANGED
|
@@ -1,14 +1,32 @@
|
|
|
1
|
+
const path = require('path')
|
|
1
2
|
const swift = require('./swift')
|
|
2
3
|
|
|
3
4
|
exports.name = 'bare-kit'
|
|
4
5
|
exports.isolation = 'thread'
|
|
5
|
-
exports.linked =
|
|
6
|
-
exports.linking =
|
|
7
|
-
exports.offload =
|
|
6
|
+
exports.linked = (context) => !isJvm(context)
|
|
7
|
+
exports.linking = linking
|
|
8
|
+
exports.offload = isJvm
|
|
8
9
|
exports.format = 'bundle'
|
|
9
10
|
exports.encoding = null
|
|
10
11
|
exports.extension = '.bundle'
|
|
11
|
-
exports.hosts = (
|
|
12
|
+
exports.hosts = (context) => (isJvm(context) ? desktop : hosts[context.language])
|
|
13
|
+
|
|
14
|
+
// On the JVM, the harness extracts the bundle from the resources of its module,
|
|
15
|
+
// so the addons and assets of the bundle are offloaded beside it rather than
|
|
16
|
+
// linked.
|
|
17
|
+
function isJvm({ language, options }) {
|
|
18
|
+
return language === 'kotlin' && options.platform === 'jvm'
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function linking(context) {
|
|
22
|
+
const { targetOptions } = context
|
|
23
|
+
|
|
24
|
+
if (isJvm(context) && targetOptions.linking === 'app') {
|
|
25
|
+
throw new Error("Option 'linking' must be 'package' for the JVM")
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return targetOptions.linking
|
|
29
|
+
}
|
|
12
30
|
|
|
13
31
|
// Applications with several bundles link their addons once with `link()`.
|
|
14
32
|
exports.options = function options(given = {}) {
|
|
@@ -27,14 +45,23 @@ exports.options = function options(given = {}) {
|
|
|
27
45
|
|
|
28
46
|
const bareKitSwift = {
|
|
29
47
|
url: 'https://github.com/holepunchto/bare-kit-swift',
|
|
30
|
-
from: '2.
|
|
48
|
+
from: '2.2.0',
|
|
31
49
|
products: ['BareKit']
|
|
32
50
|
}
|
|
33
51
|
|
|
34
52
|
const bareKitKotlin = {
|
|
35
|
-
'com.github.holepunchto.bare-kit-kotlin:bare-kit-kotlin': 'v0.
|
|
53
|
+
'com.github.holepunchto.bare-kit-kotlin:bare-kit-kotlin': 'v0.2.2'
|
|
36
54
|
}
|
|
37
55
|
|
|
56
|
+
const desktop = [
|
|
57
|
+
'darwin-arm64',
|
|
58
|
+
'darwin-x64',
|
|
59
|
+
'linux-arm64',
|
|
60
|
+
'linux-x64',
|
|
61
|
+
'win32-arm64',
|
|
62
|
+
'win32-x64'
|
|
63
|
+
]
|
|
64
|
+
|
|
38
65
|
const hosts = {
|
|
39
66
|
kotlin: ['android-arm64', 'android-arm', 'android-x64', 'android-ia32'],
|
|
40
67
|
swift: ['darwin-arm64', 'darwin-x64', 'ios-arm64', 'ios-arm64-simulator', 'ios-x64-simulator']
|
|
@@ -62,7 +89,7 @@ exports.languages = {
|
|
|
62
89
|
|
|
63
90
|
return swift(context, {
|
|
64
91
|
module: 'BareKit',
|
|
65
|
-
conformance: 'extension BareKit.IPC: @retroactive Transport {}',
|
|
92
|
+
conformance: 'extension BareKit.IPC: @retroactive BareStowHost.Transport {}',
|
|
66
93
|
fields: { worklet: 'Worklet' },
|
|
67
94
|
params: ['options: Worklet.Options = Worklet.Options()'],
|
|
68
95
|
setup: `\
|
|
@@ -82,36 +109,53 @@ Task { ipc.exited(code: await worklet.wait()) }`,
|
|
|
82
109
|
}
|
|
83
110
|
}
|
|
84
111
|
|
|
85
|
-
function kotlin(
|
|
86
|
-
const
|
|
87
|
-
|
|
88
|
-
|
|
112
|
+
function kotlin(context) {
|
|
113
|
+
const { client, options } = context
|
|
114
|
+
|
|
115
|
+
const platform = options.platform === 'jvm' ? jvm(context) : android(context)
|
|
116
|
+
|
|
117
|
+
const imports = [
|
|
118
|
+
...platform.imports,
|
|
119
|
+
'kotlin.time.Duration',
|
|
120
|
+
'kotlinx.coroutines.CoroutineScope',
|
|
121
|
+
'kotlinx.coroutines.Dispatchers',
|
|
122
|
+
'kotlinx.coroutines.launch',
|
|
123
|
+
'to.holepunch.bare.kit.Ipc as WorkletIpc',
|
|
124
|
+
'to.holepunch.bare.kit.Worklet',
|
|
125
|
+
'to.holepunch.bare.stow.host.Ipc',
|
|
126
|
+
...(client ? ['to.holepunch.bare.rpc.Rpc'] : [])
|
|
127
|
+
].sort()
|
|
89
128
|
|
|
90
129
|
const source = `\
|
|
91
130
|
package ${options.package}
|
|
92
131
|
|
|
93
|
-
import
|
|
94
|
-
import kotlin.time.Duration
|
|
95
|
-
import kotlinx.coroutines.CoroutineScope
|
|
96
|
-
import kotlinx.coroutines.Dispatchers
|
|
97
|
-
import kotlinx.coroutines.launch
|
|
98
|
-
import to.holepunch.bare.kit.Ipc as WorkletIpc
|
|
99
|
-
import to.holepunch.bare.kit.Worklet
|
|
100
|
-
import to.holepunch.bare.stow.host.Ipc
|
|
101
|
-
|
|
102
|
-
class Started(val worklet: Worklet, val ipc: Ipc${client ? ', val rpc: Client' : ''})
|
|
132
|
+
${imports.map((name) => 'import ' + name).join('\n')}
|
|
103
133
|
|
|
134
|
+
class Started(val worklet: Worklet, val ipc: Ipc${client ? ', val rpc: Rpc' : ''})
|
|
135
|
+
${platform.declarations}
|
|
104
136
|
suspend fun start(
|
|
105
|
-
|
|
137
|
+
${platform.params}\
|
|
106
138
|
options: Worklet.Options = Worklet.Options(),
|
|
107
139
|
timeout: Duration? = null,
|
|
108
140
|
grace: Duration = Ipc.DEFAULT_GRACE,${client ? '\n handlers: Handlers = {},' : ''}
|
|
109
141
|
): Started {
|
|
110
|
-
val worklet =
|
|
111
|
-
|
|
112
|
-
val transport =
|
|
142
|
+
val worklet = ${platform.worklet}
|
|
143
|
+
|
|
144
|
+
val transport =
|
|
145
|
+
try {
|
|
146
|
+
WorkletIpc(worklet)
|
|
147
|
+
} catch (error: Throwable) {
|
|
148
|
+
worklet.close()
|
|
149
|
+
throw error
|
|
150
|
+
}
|
|
113
151
|
|
|
114
|
-
|
|
152
|
+
try {
|
|
153
|
+
${platform.start}
|
|
154
|
+
} catch (error: Throwable) {
|
|
155
|
+
transport.close()
|
|
156
|
+
worklet.close()
|
|
157
|
+
throw error
|
|
158
|
+
}
|
|
115
159
|
|
|
116
160
|
val ipc =
|
|
117
161
|
Ipc(
|
|
@@ -126,6 +170,7 @@ suspend fun start(
|
|
|
126
170
|
ipc.exited(worklet.wait())
|
|
127
171
|
|
|
128
172
|
transport.close()
|
|
173
|
+
worklet.close()
|
|
129
174
|
}
|
|
130
175
|
${client ? '\n val rpc = attach(ipc, handlers)\n' : ''}
|
|
131
176
|
ipc.ready()
|
|
@@ -138,7 +183,56 @@ ${client ? '\n val rpc = attach(ipc, handlers)\n' : ''}
|
|
|
138
183
|
source,
|
|
139
184
|
dependencies: {
|
|
140
185
|
...bareKitKotlin,
|
|
141
|
-
'com.github.holepunchto.bare-stow-host-kotlin:bare-stow-host-kotlin': 'v0.
|
|
186
|
+
'com.github.holepunchto.bare-stow-host-kotlin:bare-stow-host-kotlin': 'v0.2.1'
|
|
142
187
|
}
|
|
143
188
|
}
|
|
144
189
|
}
|
|
190
|
+
|
|
191
|
+
function android({ bundle, resources }) {
|
|
192
|
+
return {
|
|
193
|
+
imports: ['android.content.Context'],
|
|
194
|
+
declarations: '',
|
|
195
|
+
params: ' context: Context,\n',
|
|
196
|
+
worklet: 'Worklet(context, options)',
|
|
197
|
+
start: resources
|
|
198
|
+
? `worklet.start(${JSON.stringify(resources + '.bundle')}, context.assets.open(${JSON.stringify(bundle)}).use { it.readBytes() })`
|
|
199
|
+
: `worklet.start(${JSON.stringify(bundle)})`
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// The bundle and the files listed beside it are extracted once per process, to
|
|
204
|
+
// a directory of its own that is removed on exit.
|
|
205
|
+
function jvm({ bundle }) {
|
|
206
|
+
const dir = path.posix.dirname(bundle)
|
|
207
|
+
const name = '/' + path.posix.basename(bundle)
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
imports: ['java.io.File', 'java.nio.file.Files', 'kotlinx.coroutines.withContext'],
|
|
211
|
+
declarations: `
|
|
212
|
+
private val bundle: File by lazy {
|
|
213
|
+
val dir = Files.createTempDirectory("bare-stow").toFile()
|
|
214
|
+
|
|
215
|
+
Runtime.getRuntime().addShutdownHook(Thread { dir.deleteRecursively() })
|
|
216
|
+
|
|
217
|
+
val loader = Started::class.java.classLoader
|
|
218
|
+
|
|
219
|
+
fun open(name: String) = loader.getResourceAsStream(${JSON.stringify(dir)} + name)!!
|
|
220
|
+
|
|
221
|
+
val listed = open(${JSON.stringify(name + '.files')}).bufferedReader().use { it.readLines() }
|
|
222
|
+
|
|
223
|
+
for (name in listOf(${JSON.stringify(name)}) + listed) {
|
|
224
|
+
val file = File(dir, name)
|
|
225
|
+
|
|
226
|
+
file.parentFile.mkdirs()
|
|
227
|
+
|
|
228
|
+
open(name).use { input -> file.outputStream().use { input.copyTo(it) } }
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
File(dir, ${JSON.stringify(name)})
|
|
232
|
+
}
|
|
233
|
+
`,
|
|
234
|
+
params: '',
|
|
235
|
+
worklet: 'Worklet(options)',
|
|
236
|
+
start: 'worklet.start(withContext(Dispatchers.IO) { bundle }.path)'
|
|
237
|
+
}
|
|
238
|
+
}
|
|
@@ -22,12 +22,12 @@ exports.languages = {
|
|
|
22
22
|
generate(context) {
|
|
23
23
|
return harness(context, {
|
|
24
24
|
name: 'bare-sidecar',
|
|
25
|
-
version: '^0.5.
|
|
25
|
+
version: '^0.5.15',
|
|
26
26
|
binding: 'Sidecar',
|
|
27
27
|
url: true,
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
handle: 'sidecar',
|
|
29
|
+
spawn: "new Sidecar(bundle, { stdio: 'inherit', ...opts })",
|
|
30
|
+
connect: `\
|
|
31
31
|
const ipc = stow.wrap(sidecar, { kill: () => sidecar.kill('SIGKILL'), timeout, grace })
|
|
32
32
|
|
|
33
33
|
sidecar.on('exit', (code, signal) => ipc.exited(code, signal))`,
|
|
@@ -40,10 +40,10 @@ sidecar.on('exit', (code, signal) => ipc.exited(code, signal))`,
|
|
|
40
40
|
generate(context) {
|
|
41
41
|
return swift(context, {
|
|
42
42
|
module: 'BareSidecar',
|
|
43
|
-
conformance: 'extension Sidecar: @retroactive Transport {}',
|
|
43
|
+
conformance: 'extension Sidecar: @retroactive BareStowHost.Transport {}',
|
|
44
|
+
handle: { name: 'sidecar', type: 'Sidecar' },
|
|
45
|
+
spawn: 'try await Sidecar.spawn(bundle)',
|
|
44
46
|
setup: `\
|
|
45
|
-
let sidecar = try await Sidecar.spawn(bundle)
|
|
46
|
-
|
|
47
47
|
let ipc = BareStowHost.IPC(sidecar, kill: { sidecar.kill() }, timeout: timeout, grace: grace)
|
|
48
48
|
|
|
49
49
|
Task {
|
|
@@ -55,7 +55,7 @@ Task {
|
|
|
55
55
|
dependencies: {
|
|
56
56
|
'bare-sidecar-swift': {
|
|
57
57
|
url: 'https://github.com/holepunchto/bare-sidecar-swift',
|
|
58
|
-
from: '0.1.
|
|
58
|
+
from: '0.1.3',
|
|
59
59
|
products: ['BareSidecar']
|
|
60
60
|
}
|
|
61
61
|
}
|
|
@@ -70,16 +70,16 @@ import asyncio
|
|
|
70
70
|
from pathlib import Path
|
|
71
71
|
from typing import NamedTuple
|
|
72
72
|
|
|
73
|
-
from bare_sidecar import spawn
|
|
73
|
+
${client ? 'from bare_rpc import RPC\n' : ''}from bare_sidecar import Sidecar, spawn
|
|
74
74
|
from bare_stow_host import DEFAULT_GRACE, IPC, wrap
|
|
75
|
-
${client ? `\nfrom ${client} import
|
|
75
|
+
${client ? `\nfrom ${client} import Handlers, attach\n` : ''}
|
|
76
76
|
bundle = Path(__file__).parent / ${JSON.stringify(bundle)}
|
|
77
77
|
|
|
78
78
|
_watching = set()
|
|
79
79
|
|
|
80
80
|
|
|
81
81
|
class Started(NamedTuple):
|
|
82
|
-
ipc: IPC${client ? '\n rpc:
|
|
82
|
+
ipc: IPC${client ? '\n rpc: RPC' : ''}
|
|
83
83
|
|
|
84
84
|
|
|
85
85
|
async def start(
|
|
@@ -90,6 +90,15 @@ async def start(
|
|
|
90
90
|
) -> Started:
|
|
91
91
|
sidecar = await spawn(bundle, **opts)
|
|
92
92
|
|
|
93
|
+
return await connect(sidecar, timeout=timeout, grace=grace${client ? ', handlers=handlers' : ''})
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
async def connect(
|
|
97
|
+
sidecar: Sidecar,
|
|
98
|
+
*,
|
|
99
|
+
timeout=None,
|
|
100
|
+
grace=DEFAULT_GRACE,${client ? '\n handlers: Handlers = lambda router: None,' : ''}
|
|
101
|
+
) -> Started:
|
|
93
102
|
ipc = wrap(sidecar, kill=sidecar.kill, timeout=timeout, grace=grace)
|
|
94
103
|
|
|
95
104
|
watching = asyncio.ensure_future(_watch(sidecar, ipc))
|
|
@@ -112,8 +121,8 @@ async def _watch(sidecar, ipc):
|
|
|
112
121
|
return {
|
|
113
122
|
source,
|
|
114
123
|
dependencies: {
|
|
115
|
-
'bare-sidecar': '~=0.1.
|
|
116
|
-
'bare-stow-host': '~=0.
|
|
124
|
+
'bare-sidecar': '~=0.1.3',
|
|
125
|
+
'bare-stow-host': '~=0.2.0'
|
|
117
126
|
}
|
|
118
127
|
}
|
|
119
128
|
}
|
|
@@ -21,11 +21,11 @@ exports.languages = {
|
|
|
21
21
|
generate(context) {
|
|
22
22
|
return harness(context, {
|
|
23
23
|
name: 'bare-worker',
|
|
24
|
-
version: '^4.3
|
|
24
|
+
version: '^4.5.3',
|
|
25
25
|
binding: 'Worker',
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
handle: 'worker',
|
|
27
|
+
spawn: 'new Worker(bundle, opts)',
|
|
28
|
+
connect: `\
|
|
29
29
|
const ipc = stow.wrap(worker.IPC, { kill: () => worker.terminate(), timeout, grace })
|
|
30
30
|
|
|
31
31
|
worker.IPC.on('end', () => worker.IPC.destroy())
|
package/lib/target/javascript.js
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
module.exports = function harness(context, target) {
|
|
2
2
|
const { bundle, client, options } = context
|
|
3
|
-
const { name, version, binding, url = false,
|
|
3
|
+
const { name, version, binding, url = false, handle, spawn, connect, types } = target
|
|
4
4
|
|
|
5
5
|
const esm = options.module === 'esm'
|
|
6
6
|
|
|
7
|
+
const params = `{ timeout, grace${client ? ', handlers = () => {}' : ''} } = {}`
|
|
8
|
+
|
|
9
|
+
const start = `\
|
|
10
|
+
const ${handle} = ${spawn}
|
|
11
|
+
|
|
12
|
+
return await connect(${handle}, { timeout, grace${client ? ', handlers' : ''} })`
|
|
13
|
+
|
|
7
14
|
const body = `\
|
|
8
|
-
${
|
|
15
|
+
${connect}
|
|
9
16
|
${client ? '\nconst rpc = client.attach(ipc, handlers)\n' : ''}
|
|
10
17
|
await ipc.ready
|
|
11
18
|
|
|
@@ -18,7 +25,11 @@ import stow from 'bare-stow-host'
|
|
|
18
25
|
${client ? `import * as client from ${JSON.stringify(client)}\n` : ''}
|
|
19
26
|
const bundle = ${url ? `new URL(import.meta.resolve(${JSON.stringify(bundle)}))` : `import.meta.resolve(${JSON.stringify(bundle)})`}
|
|
20
27
|
|
|
21
|
-
export async function start({ timeout, grace, ${client ? 'handlers
|
|
28
|
+
export async function start({ timeout, grace, ${client ? 'handlers, ' : ''}...opts } = {}) {
|
|
29
|
+
${indent(start, 2)}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function connect(${handle}, ${params}) {
|
|
22
33
|
${indent(body, 2)}
|
|
23
34
|
}
|
|
24
35
|
`
|
|
@@ -28,14 +39,18 @@ const stow = require('bare-stow-host')
|
|
|
28
39
|
${client ? `const client = require(${JSON.stringify(client)})\n` : ''}
|
|
29
40
|
const bundle = require.resolve(${JSON.stringify(bundle)})
|
|
30
41
|
|
|
31
|
-
module.exports = {
|
|
32
|
-
|
|
33
|
-
${
|
|
34
|
-
|
|
42
|
+
module.exports = { start, connect }
|
|
43
|
+
|
|
44
|
+
async function start({ timeout, grace, ${client ? 'handlers, ' : ''}...opts } = {}) {
|
|
45
|
+
${indent(start, 2)}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function connect(${handle}, ${params}) {
|
|
49
|
+
${indent(body, 2)}
|
|
35
50
|
}
|
|
36
51
|
`
|
|
37
52
|
|
|
38
|
-
const
|
|
53
|
+
const started = client
|
|
39
54
|
? `{ ipc: import('bare-stow-host').IPC; rpc: ReturnType<typeof import(${JSON.stringify(client)}).attach> }`
|
|
40
55
|
: `{ ipc: import('bare-stow-host').IPC }`
|
|
41
56
|
|
|
@@ -43,12 +58,14 @@ ${indent(body, 4)}
|
|
|
43
58
|
source,
|
|
44
59
|
dependencies: {
|
|
45
60
|
[name]: version,
|
|
46
|
-
'bare-stow-host': '^0.
|
|
61
|
+
'bare-stow-host': '^0.2.0'
|
|
47
62
|
},
|
|
48
63
|
types: `\
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
): Promise<${
|
|
64
|
+
type ConnectOptions = Omit<import('bare-stow-host').IPCOptions, 'kill'>${client ? ` & { handlers?: import(${JSON.stringify(client)}).Handlers }` : ''}
|
|
65
|
+
|
|
66
|
+
export function start(opts?: ${types} & ConnectOptions): Promise<${started}>
|
|
67
|
+
|
|
68
|
+
export function connect(${handle}: import('${name}'), opts?: ConnectOptions): Promise<${started}>
|
|
52
69
|
`
|
|
53
70
|
}
|
|
54
71
|
}
|
package/lib/target/swift.js
CHANGED
|
@@ -2,15 +2,62 @@
|
|
|
2
2
|
// an `IPC` of their own.
|
|
3
3
|
module.exports = function harness(context, target) {
|
|
4
4
|
const { bundle, client } = context
|
|
5
|
-
const {
|
|
5
|
+
const {
|
|
6
|
+
module,
|
|
7
|
+
conformance,
|
|
8
|
+
fields = {},
|
|
9
|
+
params = [],
|
|
10
|
+
handle,
|
|
11
|
+
spawn,
|
|
12
|
+
setup,
|
|
13
|
+
dependencies
|
|
14
|
+
} = target
|
|
6
15
|
|
|
7
16
|
const started = { ...fields, ipc: 'BareStowHost.IPC' }
|
|
8
17
|
|
|
9
|
-
if (client) started.rpc = '
|
|
18
|
+
if (client) started.rpc = 'RPC'
|
|
19
|
+
|
|
20
|
+
const options = [
|
|
21
|
+
'timeout: TimeInterval? = nil',
|
|
22
|
+
'grace: TimeInterval = BareStowHost.IPC.defaultGrace',
|
|
23
|
+
...(client ? ['handlers: Handlers? = nil'] : [])
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
const forwarded = ['timeout', 'grace', ...(client ? ['handlers'] : [])].map(
|
|
27
|
+
(name) => `${name}: ${name}`
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
const body = `\
|
|
31
|
+
${setup}
|
|
32
|
+
${client ? '\nlet rpc = attach(ipc, handlers)\n' : ''}
|
|
33
|
+
try await ipc.ready()
|
|
34
|
+
|
|
35
|
+
return Started(${Object.keys(started)
|
|
36
|
+
.map((name) => `${name}: ${name}`)
|
|
37
|
+
.join(', ')})`
|
|
38
|
+
|
|
39
|
+
const resource = `let bundle = Bundle.module.url(forResource: ${JSON.stringify(bundle)}, withExtension: nil)!`
|
|
40
|
+
|
|
41
|
+
// Targets that spawn the bundle let hosts connect to one they spawned, such
|
|
42
|
+
// as from another entry.
|
|
43
|
+
const start = handle
|
|
44
|
+
? `\
|
|
45
|
+
${resource}
|
|
46
|
+
|
|
47
|
+
let ${handle.name} = ${spawn}
|
|
48
|
+
|
|
49
|
+
return try await connect(${[handle.name, ...forwarded].join(', ')})`
|
|
50
|
+
: `${resource}\n\n${body}`
|
|
51
|
+
|
|
52
|
+
const functions = [func('start', [...params, ...options], start)]
|
|
53
|
+
|
|
54
|
+
if (handle) {
|
|
55
|
+
functions.push(func('connect', [`_ ${handle.name}: ${handle.type}`, ...options], body))
|
|
56
|
+
}
|
|
10
57
|
|
|
11
58
|
const source = `\
|
|
12
59
|
import ${module}
|
|
13
|
-
import BareStowHost
|
|
60
|
+
${client ? 'import BareRPC\n' : ''}import BareStowHost
|
|
14
61
|
import Foundation
|
|
15
62
|
|
|
16
63
|
${conformance}
|
|
@@ -21,26 +68,7 @@ ${Object.entries(started)
|
|
|
21
68
|
.join('\n')}
|
|
22
69
|
}
|
|
23
70
|
|
|
24
|
-
|
|
25
|
-
${[
|
|
26
|
-
...params,
|
|
27
|
-
'timeout: TimeInterval? = nil',
|
|
28
|
-
'grace: TimeInterval = BareStowHost.IPC.defaultGrace',
|
|
29
|
-
...(client ? ['handlers: Handlers? = nil'] : [])
|
|
30
|
-
]
|
|
31
|
-
.map((param) => ' ' + param)
|
|
32
|
-
.join(',\n')}
|
|
33
|
-
) async throws -> Started {
|
|
34
|
-
let bundle = Bundle.module.url(forResource: ${JSON.stringify(bundle)}, withExtension: nil)!
|
|
35
|
-
|
|
36
|
-
${setup.replace(/^(?=.)/gm, ' ')}
|
|
37
|
-
${client ? '\n let rpc = attach(ipc, handlers)\n' : ''}
|
|
38
|
-
try await ipc.ready()
|
|
39
|
-
|
|
40
|
-
return Started(${Object.keys(started)
|
|
41
|
-
.map((name) => `${name}: ${name}`)
|
|
42
|
-
.join(', ')})
|
|
43
|
-
}
|
|
71
|
+
${functions.join('\n\n')}
|
|
44
72
|
`
|
|
45
73
|
|
|
46
74
|
return {
|
|
@@ -49,9 +77,25 @@ ${client ? '\n let rpc = attach(ipc, handlers)\n' : ''}
|
|
|
49
77
|
...dependencies,
|
|
50
78
|
'bare-stow-host-swift': {
|
|
51
79
|
url: 'https://github.com/holepunchto/bare-stow-host-swift',
|
|
52
|
-
from: '0.
|
|
80
|
+
from: '0.2.0',
|
|
53
81
|
products: ['BareStowHost']
|
|
54
82
|
}
|
|
55
83
|
}
|
|
56
84
|
}
|
|
57
85
|
}
|
|
86
|
+
|
|
87
|
+
function func(name, params, body) {
|
|
88
|
+
return `\
|
|
89
|
+
public func ${name}(
|
|
90
|
+
${params.map((param) => ' ' + param).join(',\n')}
|
|
91
|
+
) async throws -> Started {
|
|
92
|
+
${indent(body)}
|
|
93
|
+
}`
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function indent(source) {
|
|
97
|
+
return source
|
|
98
|
+
.split('\n')
|
|
99
|
+
.map((line) => (line.length === 0 ? line : ' ' + line))
|
|
100
|
+
.join('\n')
|
|
101
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bare-stow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Module bundling and harness generation for Bare",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./package": "./package.json",
|
|
@@ -54,27 +54,27 @@
|
|
|
54
54
|
},
|
|
55
55
|
"homepage": "https://github.com/holepunchto/bare-stow#readme",
|
|
56
56
|
"dependencies": {
|
|
57
|
-
"bare-bundle": "^1.
|
|
58
|
-
"bare-bundle-id": "^1.0.
|
|
59
|
-
"bare-fs": "^4.
|
|
60
|
-
"bare-link": "^4.
|
|
61
|
-
"bare-module-traverse": "^2.
|
|
62
|
-
"bare-pack": "^3.0.
|
|
63
|
-
"bare-path": "^3.
|
|
64
|
-
"bare-type-stripper": "^0.1.
|
|
65
|
-
"bare-unpack": "^1.1.
|
|
66
|
-
"paparam": "^1.
|
|
57
|
+
"bare-bundle": "^1.11.0",
|
|
58
|
+
"bare-bundle-id": "^1.0.3",
|
|
59
|
+
"bare-fs": "^4.8.4",
|
|
60
|
+
"bare-link": "^4.2.0",
|
|
61
|
+
"bare-module-traverse": "^2.7.4",
|
|
62
|
+
"bare-pack": "^3.0.2",
|
|
63
|
+
"bare-path": "^3.1.2",
|
|
64
|
+
"bare-type-stripper": "^0.1.7",
|
|
65
|
+
"bare-unpack": "^1.1.4",
|
|
66
|
+
"paparam": "^1.15.0"
|
|
67
67
|
},
|
|
68
68
|
"devDependencies": {
|
|
69
|
-
"bare-os": "^3.
|
|
70
|
-
"bare-rpc": "^1.
|
|
71
|
-
"bare-sidecar": "^0.5.
|
|
72
|
-
"bare-stow-host": "^0.
|
|
69
|
+
"bare-os": "^3.9.4",
|
|
70
|
+
"bare-rpc": "^1.5.0",
|
|
71
|
+
"bare-sidecar": "^0.5.15",
|
|
72
|
+
"bare-stow-host": "^0.2.0",
|
|
73
73
|
"bare-stow-runtime": "^0.1.0",
|
|
74
|
-
"bare-worker": "^4.3
|
|
75
|
-
"brittle": "^4.0
|
|
76
|
-
"lunte": "^1.
|
|
77
|
-
"prettier": "^3.
|
|
74
|
+
"bare-worker": "^4.5.3",
|
|
75
|
+
"brittle": "^4.5.0",
|
|
76
|
+
"lunte": "^1.8.6",
|
|
77
|
+
"prettier": "^3.9.10",
|
|
78
78
|
"prettier-config-holepunch": "^2.0.0",
|
|
79
79
|
"which-runtime": "^1.4.0"
|
|
80
80
|
},
|