@ham2k/extension-tools 1.15.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
@@ -56,8 +56,9 @@ import yazl from "yazl"
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
58
  /// info; 15 adds `incomplete` on a live scoring verdict, the fields a
59
- /// contact still needs before Enter logs it.
60
- export const H2KEXT_API_VERSION = 15
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
61
62
 
62
63
  /// The first API with `host.webSocket`. A manifest declaring `webSockets`
63
64
  /// under an older one is refused: an older app would install it and fail only
@@ -80,6 +81,12 @@ export const CLUSTER_API_VERSION = 10
80
81
  /// install it and refuse every raw command it sends.
81
82
  export const RADIO_RAW_API_VERSION = 11
82
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
+
83
90
  /// The first API with timers. The build points a bundle's bare `setTimeout`
84
91
  /// and friends at the SDK's only from here on: an older app's kernel hands an
85
92
  /// extension no timers, and a bundle declaring less says it runs there.
@@ -148,7 +155,7 @@ export const ASSETS_PREFIX = "assets/"
148
155
 
149
156
  /// The libraries the extension host carries, per API version, so a bundle does
150
157
  /// not have to ship its own copy of each. The kernel holds one instance and
151
- /// hands it to every extension through `__polo.sharedModules`.
158
+ /// hands it to every extension through `__halo.sharedModules`.
152
159
  ///
153
160
  /// NAMES only. The VERSION a given app carries is that build's business, which
154
161
  /// is the whole reason a bundle declares a RANGE in `sharedDependencies` and
@@ -189,6 +196,7 @@ SHARED_MODULES_BY_API[12] = SHARED_MODULES_BY_API[11]
189
196
  SHARED_MODULES_BY_API[13] = SHARED_MODULES_BY_API[12]
190
197
  SHARED_MODULES_BY_API[14] = SHARED_MODULES_BY_API[13]
191
198
  SHARED_MODULES_BY_API[15] = SHARED_MODULES_BY_API[14]
199
+ SHARED_MODULES_BY_API[16] = SHARED_MODULES_BY_API[15]
192
200
 
193
201
  /// What a scaffolded extension declares on day one: the eight modules the SDK
194
202
  /// barrel itself reaches, so a project that has written no code yet still
@@ -511,7 +519,8 @@ export function validateManifest(manifest, { source = MANIFEST_ENTRY, forceName
511
519
  }
512
520
 
513
521
  /// Every library a bundle takes from the host rather than carrying its own
514
- /// 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.
515
524
  ///
516
525
  /// Taking them is the supported path and worth roughly thirty times the bundle
517
526
  /// size. What the caller checks is that each one was DECLARED: the host carries
@@ -522,7 +531,7 @@ export function validateManifest(manifest, { source = MANIFEST_ENTRY, forceName
522
531
  /// else's app.
523
532
  export function findSharedModuleReferences(source) {
524
533
  const hits = []
525
- const pattern = /__polo\s*\.\s*sharedModules\s*\[\s*["']([^"']+)["']\s*\]/g
534
+ const pattern = /__(?:halo|polo)\s*\)?\s*\.\s*sharedModules\s*\[\s*["']([^"']+)["']\s*\]/g
526
535
  for (const match of source.matchAll(pattern)) hits.push(match[1])
527
536
  return [...new Set(hits)]
528
537
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ham2k/extension-tools",
3
- "version": "1.15.0",
3
+ "version": "1.16.0",
4
4
  "description": "Build and package extensions for the Ham2K Logger",
5
5
  "keywords": [
6
6
  "ham2k",