@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 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
- contents: `module.exports = globalThis.__polo.sharedModules[${JSON.stringify(args.path)}];`,
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
- __polo: {
402
- defineExtension: (definition) => definitions.push(definition),
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
- export const H2KEXT_API_VERSION = 14
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 `__polo.sharedModules`.
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 `globalThis.__polo.sharedModules[...]` lookups its build emitted.
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 = /__polo\s*\.\s*sharedModules\s*\[\s*["']([^"']+)["']\s*\]/g
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ham2k/extension-tools",
3
- "version": "1.14.0",
3
+ "version": "1.16.0",
4
4
  "description": "Build and package extensions for the Ham2K Logger",
5
5
  "keywords": [
6
6
  "ham2k",