@ham2k/extension-tools 1.14.0 → 1.16.0
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/h2kext-build.mjs +38 -16
- package/h2kext.mjs +16 -5
- package/package.json +1 -1
package/h2kext-build.mjs
CHANGED
|
@@ -25,7 +25,7 @@ import { createRequire } from "node:module"
|
|
|
25
25
|
import { dirname, join, resolve } from "node:path"
|
|
26
26
|
import vm from "node:vm"
|
|
27
27
|
|
|
28
|
-
import { H2KEXT_API_VERSION, MANIFEST_ENTRY, TIMERS_API_VERSION, sharedModulesFor } from "./h2kext.mjs"
|
|
28
|
+
import { H2KEXT_API_VERSION, HALO_GLOBAL_API_VERSION, MANIFEST_ENTRY, TIMERS_API_VERSION, sharedModulesFor } from "./h2kext.mjs"
|
|
29
29
|
|
|
30
30
|
/// What an extension's own `build.mjs` says, for everything that has to write
|
|
31
31
|
/// one: `h2kext-init`'s scaffold, and the samples the SDK ships.
|
|
@@ -160,7 +160,8 @@ export function sharedModulesPlugin({ sharedDependencies = {}, api = H2KEXT_API_
|
|
|
160
160
|
}
|
|
161
161
|
|
|
162
162
|
build.onLoad({ filter: /.*/, namespace: "ham2k-shared-module" }, (args) => ({
|
|
163
|
-
|
|
163
|
+
// `__polo` is the kernel's name on an app before extension API 16.
|
|
164
|
+
contents: `module.exports = (globalThis.__halo ?? globalThis.__polo).sharedModules[${JSON.stringify(args.path)}];`,
|
|
164
165
|
loader: "js",
|
|
165
166
|
}))
|
|
166
167
|
},
|
|
@@ -346,6 +347,7 @@ export async function buildExtension(esbuild, { dir, entry = "src/index.ts", out
|
|
|
346
347
|
}),
|
|
347
348
|
)
|
|
348
349
|
|
|
350
|
+
await checkKernelName({ bundle: join(out, "index.js"), manifest })
|
|
349
351
|
await checkSettingsPanelDeclared({ bundle: join(out, "index.js"), manifest, dir })
|
|
350
352
|
|
|
351
353
|
// The packer reads both from one directory, so the manifest the bundle was
|
|
@@ -376,6 +378,22 @@ export async function buildExtension(esbuild, { dir, entry = "src/index.ts", out
|
|
|
376
378
|
/// told the check did not run.
|
|
377
379
|
const ACTIVATION_DEADLINE_MS = 2000
|
|
378
380
|
|
|
381
|
+
/// Refuses a bundle that declares extension API 16 or more but was built with
|
|
382
|
+
/// an SDK that reaches the kernel only as \`__polo\`, which such an app answers
|
|
383
|
+
/// for nothing that declares that level. The SDK and the tools are separate
|
|
384
|
+
/// packages, so a project can update one and not the other.
|
|
385
|
+
async function checkKernelName({ bundle, manifest }) {
|
|
386
|
+
const api = manifest.api ?? H2KEXT_API_VERSION
|
|
387
|
+
if (api < HALO_GLOBAL_API_VERSION) return
|
|
388
|
+
const source = await readFile(bundle, "utf8")
|
|
389
|
+
if (source.includes("__halo") || !source.includes("__polo")) return
|
|
390
|
+
throw new BuildError(
|
|
391
|
+
`${MANIFEST_ENTRY} declares extension API ${api}, where the app's kernel is \`__halo\`, but this bundle's ` +
|
|
392
|
+
`@ham2k/extension-sdk reaches it only as \`__polo\`, which the app keeps for bundles declaring a level below ` +
|
|
393
|
+
`${HALO_GLOBAL_API_VERSION}. Update @ham2k/extension-sdk to 1.${HALO_GLOBAL_API_VERSION} or later, or declare a lower "api".`,
|
|
394
|
+
)
|
|
395
|
+
}
|
|
396
|
+
|
|
379
397
|
async function checkSettingsPanelDeclared({ bundle, manifest, dir }) {
|
|
380
398
|
const require = createRequire(resolve(dir, "noop.js"))
|
|
381
399
|
const sharedModules = new Proxy(
|
|
@@ -395,23 +413,27 @@ async function checkSettingsPanelDeclared({ bundle, manifest, dir }) {
|
|
|
395
413
|
)
|
|
396
414
|
const hostCall = async (method) => (method === "getSettings" ? {} : method.startsWith("dbLookup") ? [] : null)
|
|
397
415
|
const definitions = []
|
|
416
|
+
const kernel = {
|
|
417
|
+
defineExtension: (definition) => definitions.push(definition),
|
|
418
|
+
hostCall,
|
|
419
|
+
log: () => {},
|
|
420
|
+
invokeLocal: async () => [],
|
|
421
|
+
invokeLocalSequential: async () => [],
|
|
422
|
+
updateInterpretation: () => {},
|
|
423
|
+
getAccountKvKey: (extensionKey) => extensionKey,
|
|
424
|
+
registerDynamicForm: () => {},
|
|
425
|
+
unregisterDynamicForm: () => {},
|
|
426
|
+
host: { showForm: async () => null },
|
|
427
|
+
sharedModules,
|
|
428
|
+
sharedVersions: {},
|
|
429
|
+
}
|
|
430
|
+
// The names the app answers for the level the manifest declares: `__polo`
|
|
431
|
+
// only below HALO_GLOBAL_API_VERSION, whose SDKs may predate `__halo`.
|
|
398
432
|
const sandbox = {
|
|
399
433
|
console: { log() {}, info() {}, warn() {}, error() {}, debug() {} },
|
|
400
434
|
sendMessage: () => {},
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
hostCall,
|
|
404
|
-
log: () => {},
|
|
405
|
-
invokeLocal: async () => [],
|
|
406
|
-
invokeLocalSequential: async () => [],
|
|
407
|
-
updateInterpretation: () => {},
|
|
408
|
-
getAccountKvKey: (extensionKey) => extensionKey,
|
|
409
|
-
registerDynamicForm: () => {},
|
|
410
|
-
unregisterDynamicForm: () => {},
|
|
411
|
-
host: { showForm: async () => null },
|
|
412
|
-
sharedModules,
|
|
413
|
-
sharedVersions: {},
|
|
414
|
-
},
|
|
435
|
+
__halo: kernel,
|
|
436
|
+
...((manifest.api ?? H2KEXT_API_VERSION) < HALO_GLOBAL_API_VERSION ? { __polo: kernel } : {}),
|
|
415
437
|
}
|
|
416
438
|
sandbox.globalThis = sandbox
|
|
417
439
|
|
package/h2kext.mjs
CHANGED
|
@@ -55,8 +55,10 @@ import yazl from "yazl"
|
|
|
55
55
|
/// power, PTT, connection, and the words `radio.mode`, `radio.tuner` and
|
|
56
56
|
/// `radio.status` — `when` on layers and controls, and the `radio` panel
|
|
57
57
|
/// trigger; 14 adds `host.fyi`, messages on the FYI line under the call
|
|
58
|
-
/// info
|
|
59
|
-
|
|
58
|
+
/// info; 15 adds `incomplete` on a live scoring verdict, the fields a
|
|
59
|
+
/// contact still needs before Enter logs it; 16 names the kernel `__halo`,
|
|
60
|
+
/// and keeps `__polo` only for a bundle that declares a lower level.
|
|
61
|
+
export const H2KEXT_API_VERSION = 16
|
|
60
62
|
|
|
61
63
|
/// The first API with `host.webSocket`. A manifest declaring `webSockets`
|
|
62
64
|
/// under an older one is refused: an older app would install it and fail only
|
|
@@ -79,6 +81,12 @@ export const CLUSTER_API_VERSION = 10
|
|
|
79
81
|
/// install it and refuse every raw command it sends.
|
|
80
82
|
export const RADIO_RAW_API_VERSION = 11
|
|
81
83
|
|
|
84
|
+
/// The first API whose app names the kernel `__halo`. Below it, the app also
|
|
85
|
+
/// answers `__polo`; from it, nothing answers that name for the bundle. A
|
|
86
|
+
/// bundle declaring this level or more whose SDK reaches only `__polo` is
|
|
87
|
+
/// refused at build: it would fail to load on the app it declares.
|
|
88
|
+
export const HALO_GLOBAL_API_VERSION = 16
|
|
89
|
+
|
|
82
90
|
/// The first API with timers. The build points a bundle's bare `setTimeout`
|
|
83
91
|
/// and friends at the SDK's only from here on: an older app's kernel hands an
|
|
84
92
|
/// extension no timers, and a bundle declaring less says it runs there.
|
|
@@ -147,7 +155,7 @@ export const ASSETS_PREFIX = "assets/"
|
|
|
147
155
|
|
|
148
156
|
/// The libraries the extension host carries, per API version, so a bundle does
|
|
149
157
|
/// not have to ship its own copy of each. The kernel holds one instance and
|
|
150
|
-
/// hands it to every extension through `
|
|
158
|
+
/// hands it to every extension through `__halo.sharedModules`.
|
|
151
159
|
///
|
|
152
160
|
/// NAMES only. The VERSION a given app carries is that build's business, which
|
|
153
161
|
/// is the whole reason a bundle declares a RANGE in `sharedDependencies` and
|
|
@@ -187,6 +195,8 @@ SHARED_MODULES_BY_API[11] = SHARED_MODULES_BY_API[10]
|
|
|
187
195
|
SHARED_MODULES_BY_API[12] = SHARED_MODULES_BY_API[11]
|
|
188
196
|
SHARED_MODULES_BY_API[13] = SHARED_MODULES_BY_API[12]
|
|
189
197
|
SHARED_MODULES_BY_API[14] = SHARED_MODULES_BY_API[13]
|
|
198
|
+
SHARED_MODULES_BY_API[15] = SHARED_MODULES_BY_API[14]
|
|
199
|
+
SHARED_MODULES_BY_API[16] = SHARED_MODULES_BY_API[15]
|
|
190
200
|
|
|
191
201
|
/// What a scaffolded extension declares on day one: the eight modules the SDK
|
|
192
202
|
/// barrel itself reaches, so a project that has written no code yet still
|
|
@@ -509,7 +519,8 @@ export function validateManifest(manifest, { source = MANIFEST_ENTRY, forceName
|
|
|
509
519
|
}
|
|
510
520
|
|
|
511
521
|
/// Every library a bundle takes from the host rather than carrying its own
|
|
512
|
-
/// copy — the `
|
|
522
|
+
/// copy — the `sharedModules[...]` lookups on the kernel its build emitted,
|
|
523
|
+
/// under either of the kernel's names.
|
|
513
524
|
///
|
|
514
525
|
/// Taking them is the supported path and worth roughly thirty times the bundle
|
|
515
526
|
/// size. What the caller checks is that each one was DECLARED: the host carries
|
|
@@ -520,7 +531,7 @@ export function validateManifest(manifest, { source = MANIFEST_ENTRY, forceName
|
|
|
520
531
|
/// else's app.
|
|
521
532
|
export function findSharedModuleReferences(source) {
|
|
522
533
|
const hits = []
|
|
523
|
-
const pattern = /
|
|
534
|
+
const pattern = /__(?:halo|polo)\s*\)?\s*\.\s*sharedModules\s*\[\s*["']([^"']+)["']\s*\]/g
|
|
524
535
|
for (const match of source.matchAll(pattern)) hits.push(match[1])
|
|
525
536
|
return [...new Set(hits)]
|
|
526
537
|
}
|