@hoardodile/host 0.1.2 → 0.1.3

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/dist/index.d.ts CHANGED
@@ -685,6 +685,18 @@ type PluginHooks = {
685
685
  * shape-validated (hex values, per-resource cap).
686
686
  */
687
687
  readonly runImageHashes: (api: ResourceAPI, pluginId: PluginManifestId) => Promise<ImageHashesResult | undefined>;
688
+ /**
689
+ * Run the plugin's optional `onInstall` hook — best-effort, invoked
690
+ * by the host after a successful install/update commit (marketplace
691
+ * install/update or a zip upload; never seed/dev plugins). The hook
692
+ * receives an install-scoped {@link ResourceAPI}: no resource is
693
+ * attached (the file surface answers empty, `context.detect` is
694
+ * `undefined`), while the asset methods still work and stay gated by
695
+ * the shared consent dialog. A throwing (or consent-denied) hook is
696
+ * logged via `hookFailureText` and swallowed — the install itself is
697
+ * never failed; plugins must re-check at runtime.
698
+ */
699
+ readonly runInstallHook: (pluginId: PluginManifestId) => Promise<void>;
688
700
  };
689
701
  declare function createPluginHooks(deps: PluginHooksDeps): PluginHooks;
690
702
 
package/dist/index.js CHANGED
@@ -2298,6 +2298,59 @@ function parseManifest(dirPath, dirName) {
2298
2298
  }
2299
2299
  return result.data;
2300
2300
  }
2301
+ function createInstallScopeApi() {
2302
+ const noResource = (method) => new Error(
2303
+ `${method}() \u2014 no resource is attached to the onInstall hook; use the asset methods (download/statAsset/readAsset/deleteAsset) for install-time work`
2304
+ );
2305
+ return {
2306
+ logInfo() {
2307
+ },
2308
+ logWarn() {
2309
+ },
2310
+ logError() {
2311
+ },
2312
+ context: { detect: void 0 },
2313
+ listFileNames: async () => [],
2314
+ readFile: async () => {
2315
+ throw noResource("readFile");
2316
+ },
2317
+ statFile: async () => void 0,
2318
+ statFiles: async (paths) => paths.map(() => void 0),
2319
+ sniff: async () => void 0,
2320
+ probe: async () => ({ kind: "unknown", reason: "unavailable" }),
2321
+ hashBytes: async () => {
2322
+ throw noResource("hashBytes");
2323
+ },
2324
+ computeImageHashes: async () => void 0,
2325
+ listContainer: async () => {
2326
+ throw noResource("listContainer");
2327
+ },
2328
+ extractArchive: async () => {
2329
+ throw noResource("extractArchive");
2330
+ },
2331
+ download: async () => {
2332
+ throw pluginAssetError(
2333
+ "UNAVAILABLE",
2334
+ "download() \u2014 this host has no plugin asset service; only the app server host can download into the plugin vault"
2335
+ );
2336
+ },
2337
+ statAsset: async () => {
2338
+ throw unavailableAsset2("statAsset");
2339
+ },
2340
+ readAsset: async () => {
2341
+ throw unavailableAsset2("readAsset");
2342
+ },
2343
+ deleteAsset: async () => {
2344
+ throw unavailableAsset2("deleteAsset");
2345
+ }
2346
+ };
2347
+ }
2348
+ function unavailableAsset2(method) {
2349
+ return pluginAssetError(
2350
+ "UNAVAILABLE",
2351
+ `${method}() \u2014 this host has no plugin asset vault; only the app server host manages vault files`
2352
+ );
2353
+ }
2301
2354
 
2302
2355
  // src/hooks.ts
2303
2356
  var MAX_IMAGE_HASHES_PER_RESOURCE = 2e3;
@@ -2472,6 +2525,17 @@ function createPluginHooks(deps) {
2472
2525
  pluginId
2473
2526
  );
2474
2527
  }
2528
+ async function runInstallHook(pluginId) {
2529
+ const entry = getRegistry().getById(pluginId);
2530
+ if (entry === void 0) return;
2531
+ await invokeHook(
2532
+ entry,
2533
+ entry.plugin.onInstall,
2534
+ createInstallScopeApi(),
2535
+ "onInstall",
2536
+ "warn"
2537
+ );
2538
+ }
2475
2539
  return {
2476
2540
  defaultPluginId,
2477
2541
  getEffectiveEntry,
@@ -2483,7 +2547,8 @@ function createPluginHooks(deps) {
2483
2547
  resolveLocalCoverSource,
2484
2548
  runMetaHooks,
2485
2549
  supportsImageHashes,
2486
- runImageHashes
2550
+ runImageHashes,
2551
+ runInstallHook
2487
2552
  };
2488
2553
  }
2489
2554
  var HEX_VALUE = /^[0-9a-f]+$/;
@@ -2692,7 +2757,7 @@ function createPluginSandbox(config = DEFAULT_SANDBOX_CONFIG) {
2692
2757
  await teardownChild(previous);
2693
2758
  }
2694
2759
  if (!opts.eager) {
2695
- void teardownChild(state);
2760
+ await teardownChild(state);
2696
2761
  }
2697
2762
  return createSandboxedPlugin(
2698
2763
  state.hooks ?? ["detect"],
@@ -2876,6 +2941,16 @@ function createPluginSandbox(config = DEFAULT_SANDBOX_CONFIG) {
2876
2941
  if (child === void 0) {
2877
2942
  throw new Error(`plugin ${state.id} sandbox unavailable`);
2878
2943
  }
2944
+ try {
2945
+ return await invokeOnChild(state, child, hook, api);
2946
+ } finally {
2947
+ if (hook === "onInstall") {
2948
+ state.respawnTimes = [];
2949
+ await teardownChild(state);
2950
+ }
2951
+ }
2952
+ }
2953
+ function invokeOnChild(state, child, hook, api) {
2879
2954
  const callId = nextCallId++;
2880
2955
  return new Promise((resolveCall, reject) => {
2881
2956
  const call = {