@zhama/tego-plugin-sdk 1.36.1 → 1.37.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.
Files changed (99) hide show
  1. package/README.md +50 -3
  2. package/agent/RULES.md +123 -0
  3. package/agent/guides/access.md +39 -0
  4. package/agent/guides/backend.md +76 -0
  5. package/agent/guides/claw.md +18 -0
  6. package/agent/guides/desktop-web.md +48 -0
  7. package/agent/guides/frontend.md +83 -0
  8. package/agent/guides/host-api-versions.md +34 -0
  9. package/agent/guides/i18n.md +17 -0
  10. package/agent/guides/manifest.md +26 -0
  11. package/agent/guides/migrations.md +17 -0
  12. package/agent/guides/storage.md +18 -0
  13. package/agent/guides/testing.md +17 -0
  14. package/agent/guides/ui.md +22 -0
  15. package/agent/reference/plugin-sdk-api.md +561 -0
  16. package/bin/tego-plugin-pack.mjs +1 -1
  17. package/bin/tego-plugin.mjs +4 -2
  18. package/dist/chunk-2NTAWYCG.js +1 -0
  19. package/dist/chunk-2P6UEAPV.cjs +1 -0
  20. package/dist/chunk-45Y4CR4R.cjs +1 -0
  21. package/dist/{chunk-THMWRUAT.cjs → chunk-4LKRXDYY.cjs} +13 -12
  22. package/dist/{chunk-7XHF3YSK.js → chunk-56QOZQTY.js} +1 -1
  23. package/dist/{chunk-C2OCOPZQ.cjs → chunk-6HNG5OZI.cjs} +1 -1
  24. package/dist/chunk-DEGQDJF6.js +1 -0
  25. package/dist/chunk-EIVA5BR6.js +1 -0
  26. package/dist/chunk-FCYAAOHC.js +20 -0
  27. package/dist/chunk-HZ4YWRYX.cjs +5 -0
  28. package/dist/chunk-I5Z3ECC4.js +5 -0
  29. package/dist/chunk-INCA3GFW.js +1 -0
  30. package/dist/chunk-IVJECPGG.js +1 -0
  31. package/dist/chunk-MWMGJJ74.cjs +1 -0
  32. package/dist/chunk-N4SXZZPY.cjs +1 -0
  33. package/dist/chunk-SKNPNPJP.cjs +1 -0
  34. package/dist/dev.cjs +358 -0
  35. package/dist/dev.d.cts +144 -0
  36. package/dist/dev.d.ts +144 -0
  37. package/dist/dev.js +358 -0
  38. package/dist/doctor.cjs +8 -0
  39. package/dist/doctor.d.cts +62 -0
  40. package/dist/doctor.d.ts +62 -0
  41. package/dist/doctor.js +8 -0
  42. package/dist/frontend.d.cts +33 -1
  43. package/dist/frontend.d.ts +33 -1
  44. package/dist/hono.cjs +1 -0
  45. package/dist/hono.d.cts +84 -0
  46. package/dist/hono.d.ts +84 -0
  47. package/dist/hono.js +1 -0
  48. package/dist/index.cjs +1 -1
  49. package/dist/index.d.cts +8 -3
  50. package/dist/index.d.ts +8 -3
  51. package/dist/index.js +1 -1
  52. package/dist/{manifest-types-DQARCQJt.d.cts → manifest-types-CUTNfJTa.d.cts} +1 -1
  53. package/dist/{manifest-types-DQARCQJt.d.ts → manifest-types-CUTNfJTa.d.ts} +1 -1
  54. package/dist/manifest.cjs +1 -1
  55. package/dist/manifest.d.cts +55 -2
  56. package/dist/manifest.d.ts +55 -2
  57. package/dist/manifest.js +1 -1
  58. package/dist/migrations-sync.cjs +1 -0
  59. package/dist/migrations-sync.d.cts +20 -0
  60. package/dist/migrations-sync.d.ts +20 -0
  61. package/dist/migrations-sync.js +1 -0
  62. package/dist/pack.cjs +1 -1
  63. package/dist/pack.d.cts +8 -2
  64. package/dist/pack.d.ts +8 -2
  65. package/dist/pack.js +1 -1
  66. package/dist/publish.cjs +1 -1
  67. package/dist/publish.d.cts +1 -1
  68. package/dist/publish.d.ts +1 -1
  69. package/dist/publish.js +1 -1
  70. package/dist/react.cjs +1 -0
  71. package/dist/react.d.cts +101 -0
  72. package/dist/react.d.ts +101 -0
  73. package/dist/react.js +1 -0
  74. package/dist/scaffold.cjs +672 -17
  75. package/dist/scaffold.d.cts +42 -18
  76. package/dist/scaffold.d.ts +42 -18
  77. package/dist/scaffold.js +674 -19
  78. package/dist/server-runtime.cjs +1 -1
  79. package/dist/server-runtime.d.cts +27 -2
  80. package/dist/server-runtime.d.ts +27 -2
  81. package/dist/server-runtime.js +1 -1
  82. package/dist/testing.cjs +6 -2
  83. package/dist/testing.d.cts +19 -1
  84. package/dist/testing.d.ts +19 -1
  85. package/dist/testing.js +6 -2
  86. package/dist/verify-FF2RD2MV.cjs +1 -0
  87. package/dist/verify-W7ZTUCO4.js +1 -0
  88. package/dist/vite.cjs +1 -1
  89. package/dist/vite.d.cts +27 -1
  90. package/dist/vite.d.ts +27 -1
  91. package/dist/vite.js +1 -1
  92. package/package.json +37 -3
  93. package/dist/chunk-3KUOBGJN.js +0 -19
  94. package/dist/chunk-3WA3M347.cjs +0 -1
  95. package/dist/chunk-5CU33JVY.js +0 -1
  96. package/dist/chunk-7K37PVGV.js +0 -5
  97. package/dist/chunk-BTHXQWHT.js +0 -1
  98. package/dist/chunk-ROFUQLOZ.cjs +0 -1
  99. package/dist/chunk-W7C357A6.cjs +0 -5
@@ -624,4 +624,4 @@ interface ResolvedPluginParts {
624
624
  */
625
625
  declare function resolvePluginParts(manifest: TegoPluginManifest): ResolvedPluginParts;
626
626
 
627
- export { resolvePluginParts as $, type PluginConfigDeclaration as A, type PluginConfigFieldSchema as B, CLAW_MANIFEST_FILENAME as C, type PluginConfigFieldType as D, EMPTY_CONFIG_SECTION_SUMMARY as E, type PluginConfigSchema as F, type PluginConfigUiHint as G, type PluginFrontendPart as H, type PluginParts as I, type PluginPermissions as J, type PluginPublicFrontendPart as K, type LocalizedText as L, type PluginPublicRouteMethod as M, type PluginSchemaRepair as N, type PluginWorkDelegationDeclaration as O, type PluginPublicRoute as P, type ResolvedClawPart as Q, type ResolvedBackendPart as R, type ResolvedFrontendPart as S, type TegoPluginManifest as T, type ResolvedPluginParts as U, type ResolvedPublicFrontendPart as V, applyConfigDefaults as W, configFieldLabel as X, declaredPartNames as Y, redactSecrets as Z, resolvePluginDeclares as _, type PluginBackendRuntime as a, resolvePluginScope as a0, restoreSecrets as a1, summarizeClawConfig as a2, summarizeConfigDeclaration as a3, type PluginScope as b, type PluginPartName as c, type PluginDeclares as d, type PluginConfigSection as e, type PluginConfigSectionSummary as f, type PluginNavIcon as g, type PluginNavEntry as h, type PluginAgentReadinessDeclaration as i, PLUGIN_BACKEND_PLATFORMS as j, PLUGIN_BACKEND_RUNTIMES as k, PLUGIN_CONFIG_SECRET_SENTINEL as l, PLUGIN_CONFIG_SECTIONS as m, PLUGIN_MANIFEST_FILENAME as n, PLUGIN_NAV_ICONS as o, PLUGIN_NAV_ICON_FALLBACK as p, PLUGIN_PART_DEFAULTS as q, PLUGIN_PART_NAMES as r, type PluginAgentReadinessItem as s, type PluginAgentReadinessResponse as t, type PluginAuditDeclarations as u, type PluginAuditToolDeclaration as v, type PluginBackendLimits as w, type PluginBackendPart as x, type PluginBackendPlatform as y, type PluginClawPart as z };
627
+ export { resolvePluginParts as $, type PluginConfigDeclaration as A, type PluginConfigFieldSchema as B, CLAW_MANIFEST_FILENAME as C, type PluginConfigFieldType as D, EMPTY_CONFIG_SECTION_SUMMARY as E, type PluginConfigSchema as F, type PluginConfigUiHint as G, type PluginFrontendPart as H, type PluginParts as I, type PluginPermissions as J, type PluginPublicFrontendPart as K, type LocalizedText as L, type PluginPublicRouteMethod as M, type PluginSchemaRepair as N, type PluginWorkDelegationDeclaration as O, type PluginNavEntry as P, type ResolvedClawPart as Q, type ResolvedBackendPart as R, type ResolvedFrontendPart as S, type TegoPluginManifest as T, type ResolvedPluginParts as U, type ResolvedPublicFrontendPart as V, applyConfigDefaults as W, configFieldLabel as X, declaredPartNames as Y, redactSecrets as Z, resolvePluginDeclares as _, type PluginPublicRoute as a, resolvePluginScope as a0, restoreSecrets as a1, summarizeClawConfig as a2, summarizeConfigDeclaration as a3, type PluginBackendRuntime as b, type PluginScope as c, type PluginPartName as d, type PluginDeclares as e, type PluginConfigSection as f, type PluginConfigSectionSummary as g, type PluginNavIcon as h, type PluginAgentReadinessDeclaration as i, PLUGIN_BACKEND_PLATFORMS as j, PLUGIN_BACKEND_RUNTIMES as k, PLUGIN_CONFIG_SECRET_SENTINEL as l, PLUGIN_CONFIG_SECTIONS as m, PLUGIN_MANIFEST_FILENAME as n, PLUGIN_NAV_ICONS as o, PLUGIN_NAV_ICON_FALLBACK as p, PLUGIN_PART_DEFAULTS as q, PLUGIN_PART_NAMES as r, type PluginAgentReadinessItem as s, type PluginAgentReadinessResponse as t, type PluginAuditDeclarations as u, type PluginAuditToolDeclaration as v, type PluginBackendLimits as w, type PluginBackendPart as x, type PluginBackendPlatform as y, type PluginClawPart as z };
@@ -624,4 +624,4 @@ interface ResolvedPluginParts {
624
624
  */
625
625
  declare function resolvePluginParts(manifest: TegoPluginManifest): ResolvedPluginParts;
626
626
 
627
- export { resolvePluginParts as $, type PluginConfigDeclaration as A, type PluginConfigFieldSchema as B, CLAW_MANIFEST_FILENAME as C, type PluginConfigFieldType as D, EMPTY_CONFIG_SECTION_SUMMARY as E, type PluginConfigSchema as F, type PluginConfigUiHint as G, type PluginFrontendPart as H, type PluginParts as I, type PluginPermissions as J, type PluginPublicFrontendPart as K, type LocalizedText as L, type PluginPublicRouteMethod as M, type PluginSchemaRepair as N, type PluginWorkDelegationDeclaration as O, type PluginPublicRoute as P, type ResolvedClawPart as Q, type ResolvedBackendPart as R, type ResolvedFrontendPart as S, type TegoPluginManifest as T, type ResolvedPluginParts as U, type ResolvedPublicFrontendPart as V, applyConfigDefaults as W, configFieldLabel as X, declaredPartNames as Y, redactSecrets as Z, resolvePluginDeclares as _, type PluginBackendRuntime as a, resolvePluginScope as a0, restoreSecrets as a1, summarizeClawConfig as a2, summarizeConfigDeclaration as a3, type PluginScope as b, type PluginPartName as c, type PluginDeclares as d, type PluginConfigSection as e, type PluginConfigSectionSummary as f, type PluginNavIcon as g, type PluginNavEntry as h, type PluginAgentReadinessDeclaration as i, PLUGIN_BACKEND_PLATFORMS as j, PLUGIN_BACKEND_RUNTIMES as k, PLUGIN_CONFIG_SECRET_SENTINEL as l, PLUGIN_CONFIG_SECTIONS as m, PLUGIN_MANIFEST_FILENAME as n, PLUGIN_NAV_ICONS as o, PLUGIN_NAV_ICON_FALLBACK as p, PLUGIN_PART_DEFAULTS as q, PLUGIN_PART_NAMES as r, type PluginAgentReadinessItem as s, type PluginAgentReadinessResponse as t, type PluginAuditDeclarations as u, type PluginAuditToolDeclaration as v, type PluginBackendLimits as w, type PluginBackendPart as x, type PluginBackendPlatform as y, type PluginClawPart as z };
627
+ export { resolvePluginParts as $, type PluginConfigDeclaration as A, type PluginConfigFieldSchema as B, CLAW_MANIFEST_FILENAME as C, type PluginConfigFieldType as D, EMPTY_CONFIG_SECTION_SUMMARY as E, type PluginConfigSchema as F, type PluginConfigUiHint as G, type PluginFrontendPart as H, type PluginParts as I, type PluginPermissions as J, type PluginPublicFrontendPart as K, type LocalizedText as L, type PluginPublicRouteMethod as M, type PluginSchemaRepair as N, type PluginWorkDelegationDeclaration as O, type PluginNavEntry as P, type ResolvedClawPart as Q, type ResolvedBackendPart as R, type ResolvedFrontendPart as S, type TegoPluginManifest as T, type ResolvedPluginParts as U, type ResolvedPublicFrontendPart as V, applyConfigDefaults as W, configFieldLabel as X, declaredPartNames as Y, redactSecrets as Z, resolvePluginDeclares as _, type PluginPublicRoute as a, resolvePluginScope as a0, restoreSecrets as a1, summarizeClawConfig as a2, summarizeConfigDeclaration as a3, type PluginBackendRuntime as b, type PluginScope as c, type PluginPartName as d, type PluginDeclares as e, type PluginConfigSection as f, type PluginConfigSectionSummary as g, type PluginNavIcon as h, type PluginAgentReadinessDeclaration as i, PLUGIN_BACKEND_PLATFORMS as j, PLUGIN_BACKEND_RUNTIMES as k, PLUGIN_CONFIG_SECRET_SENTINEL as l, PLUGIN_CONFIG_SECTIONS as m, PLUGIN_MANIFEST_FILENAME as n, PLUGIN_NAV_ICONS as o, PLUGIN_NAV_ICON_FALLBACK as p, PLUGIN_PART_DEFAULTS as q, PLUGIN_PART_NAMES as r, type PluginAgentReadinessItem as s, type PluginAgentReadinessResponse as t, type PluginAuditDeclarations as u, type PluginAuditToolDeclaration as v, type PluginBackendLimits as w, type PluginBackendPart as x, type PluginBackendPlatform as y, type PluginClawPart as z };
package/dist/manifest.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var n=Object.defineProperty;var e=(r,t)=>n(r,"name",{value:t,configurable:!0});var chunk3WA3M347_cjs=require("./chunk-3WA3M347.cjs");require("./chunk-ROFUQLOZ.cjs"),require("./chunk-C2OCOPZQ.cjs"),require("./chunk-AJY7ZHXK.cjs"),require("./chunk-OP64Y4IN.cjs"),Object.defineProperty(exports,"PYTHON_LOCK_FILENAME",{enumerable:!0,get:e(function(){return chunk3WA3M347_cjs.PYTHON_LOCK_FILENAME},"get")}),Object.defineProperty(exports,"PYTHON_RUNNER_VERSION",{enumerable:!0,get:e(function(){return chunk3WA3M347_cjs.PYTHON_RUNNER_VERSION},"get")}),Object.defineProperty(exports,"PYTHON_WHEELS_DIR",{enumerable:!0,get:e(function(){return chunk3WA3M347_cjs.PYTHON_WHEELS_DIR},"get")}),Object.defineProperty(exports,"checkPluginAccessCatalog",{enumerable:!0,get:e(function(){return chunk3WA3M347_cjs.checkPluginAccessCatalog},"get")}),Object.defineProperty(exports,"checkPythonVendoring",{enumerable:!0,get:e(function(){return chunk3WA3M347_cjs.checkPythonVendoring},"get")}),Object.defineProperty(exports,"normalizePackageName",{enumerable:!0,get:e(function(){return chunk3WA3M347_cjs.normalizePackageName},"get")}),Object.defineProperty(exports,"parseRequirementsLock",{enumerable:!0,get:e(function(){return chunk3WA3M347_cjs.parseRequirementsLock},"get")}),Object.defineProperty(exports,"parseTegoPluginManifest",{enumerable:!0,get:e(function(){return chunk3WA3M347_cjs.parseTegoPluginManifest},"get")}),Object.defineProperty(exports,"parseWheelFilename",{enumerable:!0,get:e(function(){return chunk3WA3M347_cjs.parseWheelFilename},"get")}),Object.defineProperty(exports,"tegoPluginManifestSchema",{enumerable:!0,get:e(function(){return chunk3WA3M347_cjs.tegoPluginManifestSchema},"get")});
1
+ "use strict";var n=Object.defineProperty;var e=(r,t)=>n(r,"name",{value:t,configurable:!0});var chunkSKNPNPJP_cjs=require("./chunk-SKNPNPJP.cjs"),chunk45Y4CR4R_cjs=require("./chunk-45Y4CR4R.cjs");require("./chunk-2P6UEAPV.cjs"),require("./chunk-N4SXZZPY.cjs"),require("./chunk-6HNG5OZI.cjs"),require("./chunk-AJY7ZHXK.cjs"),require("./chunk-OP64Y4IN.cjs"),Object.defineProperty(exports,"PYTHON_LOCK_FILENAME",{enumerable:!0,get:e(function(){return chunkSKNPNPJP_cjs.PYTHON_LOCK_FILENAME},"get")}),Object.defineProperty(exports,"PYTHON_RUNNER_VERSION",{enumerable:!0,get:e(function(){return chunkSKNPNPJP_cjs.PYTHON_RUNNER_VERSION},"get")}),Object.defineProperty(exports,"PYTHON_WHEELS_DIR",{enumerable:!0,get:e(function(){return chunkSKNPNPJP_cjs.PYTHON_WHEELS_DIR},"get")}),Object.defineProperty(exports,"checkPluginAccessCatalog",{enumerable:!0,get:e(function(){return chunkSKNPNPJP_cjs.checkPluginAccessCatalog},"get")}),Object.defineProperty(exports,"checkPythonVendoring",{enumerable:!0,get:e(function(){return chunkSKNPNPJP_cjs.checkPythonVendoring},"get")}),Object.defineProperty(exports,"normalizePackageName",{enumerable:!0,get:e(function(){return chunkSKNPNPJP_cjs.normalizePackageName},"get")}),Object.defineProperty(exports,"parseRequirementsLock",{enumerable:!0,get:e(function(){return chunkSKNPNPJP_cjs.parseRequirementsLock},"get")}),Object.defineProperty(exports,"parseTegoPluginManifest",{enumerable:!0,get:e(function(){return chunkSKNPNPJP_cjs.parseTegoPluginManifest},"get")}),Object.defineProperty(exports,"parseWheelFilename",{enumerable:!0,get:e(function(){return chunkSKNPNPJP_cjs.parseWheelFilename},"get")}),Object.defineProperty(exports,"tegoPluginManifestSchema",{enumerable:!0,get:e(function(){return chunkSKNPNPJP_cjs.tegoPluginManifestSchema},"get")}),Object.defineProperty(exports,"LOCALE_FILENAME_PATTERN",{enumerable:!0,get:e(function(){return chunk45Y4CR4R_cjs.LOCALE_FILENAME_PATTERN},"get")}),Object.defineProperty(exports,"MIGRATION_FILENAME_PATTERN",{enumerable:!0,get:e(function(){return chunk45Y4CR4R_cjs.MIGRATION_FILENAME_PATTERN},"get")}),Object.defineProperty(exports,"flattenLocaleKeys",{enumerable:!0,get:e(function(){return chunk45Y4CR4R_cjs.flattenLocaleKeys},"get")}),Object.defineProperty(exports,"verifyFrontendLocales",{enumerable:!0,get:e(function(){return chunk45Y4CR4R_cjs.verifyFrontendLocales},"get")}),Object.defineProperty(exports,"verifyMigrationNames",{enumerable:!0,get:e(function(){return chunk45Y4CR4R_cjs.verifyMigrationNames},"get")});
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { T as TegoPluginManifest, y as PluginBackendPlatform } from './manifest-types-DQARCQJt.cjs';
2
+ import { T as TegoPluginManifest, P as PluginNavEntry, y as PluginBackendPlatform } from './manifest-types-CUTNfJTa.cjs';
3
3
  import { A as AccessCatalog } from './catalog-CSSbrfEf.cjs';
4
4
 
5
5
  /**
@@ -22,6 +22,59 @@ type PluginAccessCatalogResult = {
22
22
  */
23
23
  declare function checkPluginAccessCatalog(manifest: Pick<TegoPluginManifest, 'access' | 'parts'>, raw: unknown): PluginAccessCatalogResult;
24
24
 
25
+ /** `zh-CN.json`, `en.json` — the locale file shape the host's loader expects. */
26
+ declare const LOCALE_FILENAME_PATTERN: RegExp;
27
+ /**
28
+ * `0001_initial.sql`. The numeric prefix is what makes the apply order
29
+ * deterministic; without it two migrations could be applied in either order,
30
+ * which is only discovered when a column is missing in production.
31
+ */
32
+ declare const MIGRATION_FILENAME_PATTERN: RegExp;
33
+ /** Read access to a plugin package or source tree, paths relative to its root with `/`. */
34
+ interface PluginTreeReader {
35
+ /** Names of the files directly in `dir` (not nested deeper); empty when it does not exist. */
36
+ list(dir: string): Promise<string[]>;
37
+ /** The file as UTF-8, or `null` when absent. */
38
+ readText(path: string): Promise<string | null>;
39
+ }
40
+ /** Every leaf path of a nested translation object, in `a.b.c` form. */
41
+ declare function flattenLocaleKeys(value: unknown, prefix?: string, into?: Set<string>): Set<string>;
42
+ interface LocaleVerification {
43
+ /** Locale tags found (`zh-CN`, `en`, …), in directory order. */
44
+ locales: string[];
45
+ /** Parsed bundles by tag, for checks that need the values. */
46
+ bundles: Map<string, unknown>;
47
+ errors: string[];
48
+ }
49
+ /**
50
+ * Every locale bundle exists, parses, and carries the same keys.
51
+ *
52
+ * Three separate failures, all of which used to reach a user as a sidebar row
53
+ * showing `crm.nav.deals`:
54
+ *
55
+ * - a missing language, where the platform is translated and this plugin is
56
+ * not. Falling back would hide it permanently, so it is refused where the
57
+ * author can act
58
+ * - a key present in one bundle and absent in another, which is what a
59
+ * half-finished translation pass leaves behind
60
+ * - a nav entry pointing at a key nobody wrote
61
+ *
62
+ * Keys are compared against `zh-CN`, the platform's own default, rather than
63
+ * against a union of all files — a union would let a typo in one file demand
64
+ * the same typo in the other nine.
65
+ */
66
+ declare function verifyFrontendLocales(reader: PluginTreeReader, frontend: {
67
+ i18nDirPath: string;
68
+ nav: PluginNavEntry[];
69
+ }): Promise<LocaleVerification>;
70
+ interface MigrationVerification {
71
+ /** Valid migration file names, sorted (= apply order). */
72
+ migrations: string[];
73
+ errors: string[];
74
+ }
75
+ /** Migration files are `<ordinal>_<description>.sql` with unique ordinals. */
76
+ declare function verifyMigrationNames(reader: PluginTreeReader, migrationsDirPath: string): Promise<MigrationVerification>;
77
+
25
78
  /**
26
79
  * Whether a python backend's vendored wheels will install in the runner.
27
80
  *
@@ -2821,4 +2874,4 @@ interface PluginManifestParseResult {
2821
2874
  */
2822
2875
  declare function parseTegoPluginManifest(value: unknown): PluginManifestParseResult;
2823
2876
 
2824
- export { type LockedRequirement, PYTHON_LOCK_FILENAME, PYTHON_RUNNER_VERSION, PYTHON_WHEELS_DIR, type ParsedTegoPluginManifest, type ParsedWheelFilename, type PluginAccessCatalogResult, type PluginManifestParseResult, type VendoredWheel, checkPluginAccessCatalog, checkPythonVendoring, normalizePackageName, parseRequirementsLock, parseTegoPluginManifest, parseWheelFilename, tegoPluginManifestSchema };
2877
+ export { LOCALE_FILENAME_PATTERN, type LocaleVerification, type LockedRequirement, MIGRATION_FILENAME_PATTERN, type MigrationVerification, PYTHON_LOCK_FILENAME, PYTHON_RUNNER_VERSION, PYTHON_WHEELS_DIR, type ParsedTegoPluginManifest, type ParsedWheelFilename, type PluginAccessCatalogResult, type PluginManifestParseResult, type PluginTreeReader, type VendoredWheel, checkPluginAccessCatalog, checkPythonVendoring, flattenLocaleKeys, normalizePackageName, parseRequirementsLock, parseTegoPluginManifest, parseWheelFilename, tegoPluginManifestSchema, verifyFrontendLocales, verifyMigrationNames };
@@ -1,5 +1,5 @@
1
1
  import { z } from 'zod';
2
- import { T as TegoPluginManifest, y as PluginBackendPlatform } from './manifest-types-DQARCQJt.js';
2
+ import { T as TegoPluginManifest, P as PluginNavEntry, y as PluginBackendPlatform } from './manifest-types-CUTNfJTa.js';
3
3
  import { A as AccessCatalog } from './catalog-CSSbrfEf.js';
4
4
 
5
5
  /**
@@ -22,6 +22,59 @@ type PluginAccessCatalogResult = {
22
22
  */
23
23
  declare function checkPluginAccessCatalog(manifest: Pick<TegoPluginManifest, 'access' | 'parts'>, raw: unknown): PluginAccessCatalogResult;
24
24
 
25
+ /** `zh-CN.json`, `en.json` — the locale file shape the host's loader expects. */
26
+ declare const LOCALE_FILENAME_PATTERN: RegExp;
27
+ /**
28
+ * `0001_initial.sql`. The numeric prefix is what makes the apply order
29
+ * deterministic; without it two migrations could be applied in either order,
30
+ * which is only discovered when a column is missing in production.
31
+ */
32
+ declare const MIGRATION_FILENAME_PATTERN: RegExp;
33
+ /** Read access to a plugin package or source tree, paths relative to its root with `/`. */
34
+ interface PluginTreeReader {
35
+ /** Names of the files directly in `dir` (not nested deeper); empty when it does not exist. */
36
+ list(dir: string): Promise<string[]>;
37
+ /** The file as UTF-8, or `null` when absent. */
38
+ readText(path: string): Promise<string | null>;
39
+ }
40
+ /** Every leaf path of a nested translation object, in `a.b.c` form. */
41
+ declare function flattenLocaleKeys(value: unknown, prefix?: string, into?: Set<string>): Set<string>;
42
+ interface LocaleVerification {
43
+ /** Locale tags found (`zh-CN`, `en`, …), in directory order. */
44
+ locales: string[];
45
+ /** Parsed bundles by tag, for checks that need the values. */
46
+ bundles: Map<string, unknown>;
47
+ errors: string[];
48
+ }
49
+ /**
50
+ * Every locale bundle exists, parses, and carries the same keys.
51
+ *
52
+ * Three separate failures, all of which used to reach a user as a sidebar row
53
+ * showing `crm.nav.deals`:
54
+ *
55
+ * - a missing language, where the platform is translated and this plugin is
56
+ * not. Falling back would hide it permanently, so it is refused where the
57
+ * author can act
58
+ * - a key present in one bundle and absent in another, which is what a
59
+ * half-finished translation pass leaves behind
60
+ * - a nav entry pointing at a key nobody wrote
61
+ *
62
+ * Keys are compared against `zh-CN`, the platform's own default, rather than
63
+ * against a union of all files — a union would let a typo in one file demand
64
+ * the same typo in the other nine.
65
+ */
66
+ declare function verifyFrontendLocales(reader: PluginTreeReader, frontend: {
67
+ i18nDirPath: string;
68
+ nav: PluginNavEntry[];
69
+ }): Promise<LocaleVerification>;
70
+ interface MigrationVerification {
71
+ /** Valid migration file names, sorted (= apply order). */
72
+ migrations: string[];
73
+ errors: string[];
74
+ }
75
+ /** Migration files are `<ordinal>_<description>.sql` with unique ordinals. */
76
+ declare function verifyMigrationNames(reader: PluginTreeReader, migrationsDirPath: string): Promise<MigrationVerification>;
77
+
25
78
  /**
26
79
  * Whether a python backend's vendored wheels will install in the runner.
27
80
  *
@@ -2821,4 +2874,4 @@ interface PluginManifestParseResult {
2821
2874
  */
2822
2875
  declare function parseTegoPluginManifest(value: unknown): PluginManifestParseResult;
2823
2876
 
2824
- export { type LockedRequirement, PYTHON_LOCK_FILENAME, PYTHON_RUNNER_VERSION, PYTHON_WHEELS_DIR, type ParsedTegoPluginManifest, type ParsedWheelFilename, type PluginAccessCatalogResult, type PluginManifestParseResult, type VendoredWheel, checkPluginAccessCatalog, checkPythonVendoring, normalizePackageName, parseRequirementsLock, parseTegoPluginManifest, parseWheelFilename, tegoPluginManifestSchema };
2877
+ export { LOCALE_FILENAME_PATTERN, type LocaleVerification, type LockedRequirement, MIGRATION_FILENAME_PATTERN, type MigrationVerification, PYTHON_LOCK_FILENAME, PYTHON_RUNNER_VERSION, PYTHON_WHEELS_DIR, type ParsedTegoPluginManifest, type ParsedWheelFilename, type PluginAccessCatalogResult, type PluginManifestParseResult, type PluginTreeReader, type VendoredWheel, checkPluginAccessCatalog, checkPythonVendoring, flattenLocaleKeys, normalizePackageName, parseRequirementsLock, parseTegoPluginManifest, parseWheelFilename, tegoPluginManifestSchema, verifyFrontendLocales, verifyMigrationNames };
package/dist/manifest.js CHANGED
@@ -1 +1 @@
1
- export{PYTHON_LOCK_FILENAME,PYTHON_RUNNER_VERSION,PYTHON_WHEELS_DIR,checkPluginAccessCatalog,checkPythonVendoring,normalizePackageName,parseRequirementsLock,parseTegoPluginManifest,parseWheelFilename,tegoPluginManifestSchema}from"./chunk-5CU33JVY.js";import"./chunk-BTHXQWHT.js";import"./chunk-7XHF3YSK.js";import"./chunk-VMCGDDGP.js";import"./chunk-SNUCO2MK.js";
1
+ export{PYTHON_LOCK_FILENAME,PYTHON_RUNNER_VERSION,PYTHON_WHEELS_DIR,checkPluginAccessCatalog,checkPythonVendoring,normalizePackageName,parseRequirementsLock,parseTegoPluginManifest,parseWheelFilename,tegoPluginManifestSchema}from"./chunk-EIVA5BR6.js";export{LOCALE_FILENAME_PATTERN,MIGRATION_FILENAME_PATTERN,flattenLocaleKeys,verifyFrontendLocales,verifyMigrationNames}from"./chunk-2NTAWYCG.js";import"./chunk-IVJECPGG.js";import"./chunk-INCA3GFW.js";import"./chunk-56QOZQTY.js";import"./chunk-VMCGDDGP.js";import"./chunk-SNUCO2MK.js";
@@ -0,0 +1 @@
1
+ "use strict";var f=Object.defineProperty;var o=(e,t)=>f(e,"name",{value:t,configurable:!0});var promises=require("fs/promises"),path=require("path");function parseName(e){const t=/^(\d{4,})_(.+)\.sql$/.exec(e);return t?{ordinal:Number(t[1]),tag:t[2]}:null}o(parseName,"parseName");async function sqlFiles(e){return(await promises.readdir(e).catch(()=>[])).filter(n=>n.endsWith(".sql")).sort()}o(sqlFiles,"sqlFiles");function normalizeGeneratedSql(e){return e.replaceAll('"public".',"")}o(normalizeGeneratedSql,"normalizeGeneratedSql");async function syncMigrations(e){const t=e.generatedDir??"backend/.drizzle",n=e.migrationsDir??"backend/migrations",g=await sqlFiles(path.join(e.root,t)),m=await sqlFiles(path.join(e.root,n)),l=new Map;let s=0;for(const r of m){const a=parseName(r);a&&(l.set(a.tag,r),s=Math.max(s,a.ordinal))}const i={copied:[],problems:[]};for(const r of g){const a=parseName(r);if(!a){i.problems.push(`${t}/${r}: not named <ordinal>_<tag>.sql`);continue}const u=normalizeGeneratedSql(await promises.readFile(path.join(e.root,t,r),"utf8")),d=l.get(a.tag);if(d){await promises.readFile(path.join(e.root,n,d),"utf8")!==u&&i.problems.push(`${n}/${d} differs from the regenerated ${r}. An applied migration is frozen \u2014 add a new one instead of editing it.`);continue}s+=1;const c=`${String(s).padStart(4,"0")}_${a.tag}.sql`;if(e.check){i.problems.push(`${n}/${c} is missing; run \`tego-plugin migrations sync\`.`);continue}await promises.writeFile(path.join(e.root,n,c),u,"utf8"),l.set(a.tag,c),i.copied.push({from:`${t}/${r}`,to:`${n}/${c}`})}return i}o(syncMigrations,"syncMigrations"),exports.normalizeGeneratedSql=normalizeGeneratedSql,exports.syncMigrations=syncMigrations;
@@ -0,0 +1,20 @@
1
+ interface MigrationSyncOptions {
2
+ root: string;
3
+ /** drizzle-kit's `out`. Default `backend/.drizzle`. */
4
+ generatedDir?: string;
5
+ /** The manifest's migrations dir. Default `backend/migrations`. */
6
+ migrationsDir?: string;
7
+ /** Verify only (for `build` / CI): report what is missing or edited. */
8
+ check?: boolean;
9
+ }
10
+ interface MigrationSyncResult {
11
+ copied: {
12
+ from: string;
13
+ to: string;
14
+ }[];
15
+ problems: string[];
16
+ }
17
+ declare function normalizeGeneratedSql(sql: string): string;
18
+ declare function syncMigrations(options: MigrationSyncOptions): Promise<MigrationSyncResult>;
19
+
20
+ export { type MigrationSyncOptions, type MigrationSyncResult, normalizeGeneratedSql, syncMigrations };
@@ -0,0 +1,20 @@
1
+ interface MigrationSyncOptions {
2
+ root: string;
3
+ /** drizzle-kit's `out`. Default `backend/.drizzle`. */
4
+ generatedDir?: string;
5
+ /** The manifest's migrations dir. Default `backend/migrations`. */
6
+ migrationsDir?: string;
7
+ /** Verify only (for `build` / CI): report what is missing or edited. */
8
+ check?: boolean;
9
+ }
10
+ interface MigrationSyncResult {
11
+ copied: {
12
+ from: string;
13
+ to: string;
14
+ }[];
15
+ problems: string[];
16
+ }
17
+ declare function normalizeGeneratedSql(sql: string): string;
18
+ declare function syncMigrations(options: MigrationSyncOptions): Promise<MigrationSyncResult>;
19
+
20
+ export { type MigrationSyncOptions, type MigrationSyncResult, normalizeGeneratedSql, syncMigrations };
@@ -0,0 +1 @@
1
+ var w=Object.defineProperty;var o=(t,e)=>w(t,"name",{value:e,configurable:!0});import{readFile as m,writeFile as y,readdir as q}from"fs/promises";import{join as s}from"path";function f(t){const e=/^(\d{4,})_(.+)\.sql$/.exec(t);return e?{ordinal:Number(e[1]),tag:e[2]}:null}o(f,"parseName");async function p(t){return(await q(t).catch(()=>[])).filter(n=>n.endsWith(".sql")).sort()}o(p,"sqlFiles");function $(t){return t.replaceAll('"public".',"")}o($,"normalizeGeneratedSql");async function x(t){const e=t.generatedDir??"backend/.drizzle",n=t.migrationsDir??"backend/migrations",h=await p(s(t.root,e)),b=await p(s(t.root,n)),l=new Map;let c=0;for(const r of b){const a=f(r);a&&(l.set(a.tag,r),c=Math.max(c,a.ordinal))}const i={copied:[],problems:[]};for(const r of h){const a=f(r);if(!a){i.problems.push(`${e}/${r}: not named <ordinal>_<tag>.sql`);continue}const g=$(await m(s(t.root,e,r),"utf8")),u=l.get(a.tag);if(u){await m(s(t.root,n,u),"utf8")!==g&&i.problems.push(`${n}/${u} differs from the regenerated ${r}. An applied migration is frozen \u2014 add a new one instead of editing it.`);continue}c+=1;const d=`${String(c).padStart(4,"0")}_${a.tag}.sql`;if(t.check){i.problems.push(`${n}/${d} is missing; run \`tego-plugin migrations sync\`.`);continue}await y(s(t.root,n,d),g,"utf8"),l.set(a.tag,d),i.copied.push({from:`${e}/${r}`,to:`${n}/${d}`})}return i}o(x,"syncMigrations");export{$ as normalizeGeneratedSql,x as syncMigrations};
package/dist/pack.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var u=Object.defineProperty;var e=(r,n)=>u(r,"name",{value:n,configurable:!0});var chunkTHMWRUAT_cjs=require("./chunk-THMWRUAT.cjs");require("./chunk-W7C357A6.cjs"),require("./chunk-3WA3M347.cjs"),require("./chunk-ROFUQLOZ.cjs"),require("./chunk-C2OCOPZQ.cjs"),require("./chunk-AJY7ZHXK.cjs"),require("./chunk-OP64Y4IN.cjs"),Object.defineProperty(exports,"buildAgentTemplate",{enumerable:!0,get:e(function(){return chunkTHMWRUAT_cjs.buildAgentTemplate},"get")}),Object.defineProperty(exports,"buildPluginBackend",{enumerable:!0,get:e(function(){return chunkTHMWRUAT_cjs.buildPluginBackend},"get")}),Object.defineProperty(exports,"buildPluginTemplates",{enumerable:!0,get:e(function(){return chunkTHMWRUAT_cjs.buildPluginTemplates},"get")}),Object.defineProperty(exports,"lockPlugin",{enumerable:!0,get:e(function(){return chunkTHMWRUAT_cjs.lockPlugin},"get")}),Object.defineProperty(exports,"packPlugin",{enumerable:!0,get:e(function(){return chunkTHMWRUAT_cjs.packPlugin},"get")}),Object.defineProperty(exports,"watchPluginFrontend",{enumerable:!0,get:e(function(){return chunkTHMWRUAT_cjs.watchPluginFrontend},"get")});
1
+ "use strict";var u=Object.defineProperty;var e=(r,n)=>u(r,"name",{value:n,configurable:!0});var chunk4LKRXDYY_cjs=require("./chunk-4LKRXDYY.cjs");require("./chunk-HZ4YWRYX.cjs"),require("./chunk-SKNPNPJP.cjs"),require("./chunk-45Y4CR4R.cjs"),require("./chunk-2P6UEAPV.cjs"),require("./chunk-N4SXZZPY.cjs"),require("./chunk-6HNG5OZI.cjs"),require("./chunk-AJY7ZHXK.cjs"),require("./chunk-OP64Y4IN.cjs"),Object.defineProperty(exports,"buildAgentTemplate",{enumerable:!0,get:e(function(){return chunk4LKRXDYY_cjs.buildAgentTemplate},"get")}),Object.defineProperty(exports,"buildPluginBackend",{enumerable:!0,get:e(function(){return chunk4LKRXDYY_cjs.buildPluginBackend},"get")}),Object.defineProperty(exports,"buildPluginTemplates",{enumerable:!0,get:e(function(){return chunk4LKRXDYY_cjs.buildPluginTemplates},"get")}),Object.defineProperty(exports,"lockPlugin",{enumerable:!0,get:e(function(){return chunk4LKRXDYY_cjs.lockPlugin},"get")}),Object.defineProperty(exports,"packPlugin",{enumerable:!0,get:e(function(){return chunk4LKRXDYY_cjs.packPlugin},"get")}),Object.defineProperty(exports,"readManifest",{enumerable:!0,get:e(function(){return chunk4LKRXDYY_cjs.readManifest},"get")}),Object.defineProperty(exports,"watchPluginFrontend",{enumerable:!0,get:e(function(){return chunk4LKRXDYY_cjs.watchPluginFrontend},"get")});
package/dist/pack.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { T as TegoPluginManifest, R as ResolvedBackendPart } from './manifest-types-DQARCQJt.cjs';
1
+ import { T as TegoPluginManifest, R as ResolvedBackendPart } from './manifest-types-CUTNfJTa.cjs';
2
2
 
3
3
  /**
4
4
  * Staging a `runtime: "python"` backend: source, locked wheels, and a lock
@@ -85,6 +85,11 @@ interface PackPluginOptions {
85
85
  };
86
86
  /** Where to report progress. Defaults to `console.log`. Pass `null` to mute. */
87
87
  log?: ((message: string) => void) | null;
88
+ /**
89
+ * Run `tego-plugin doctor` first (default). Packing stops on the problems
90
+ * upload would refuse anyway; the rest are summarized, not fatal.
91
+ */
92
+ doctor?: boolean;
88
93
  }
89
94
  interface PackedPlugin {
90
95
  /** Absolute path of the archive. */
@@ -144,6 +149,7 @@ interface WatchPluginOptions {
144
149
  * Never resolves — the caller is a foreground command the author interrupts.
145
150
  */
146
151
  declare function watchPluginFrontend(options: WatchPluginOptions): Promise<never>;
152
+ declare function readManifest(root: string): Promise<TegoPluginManifest>;
147
153
  interface BuildPluginBackendOptions {
148
154
  /** Package root: the directory holding `tego.plugin.json`. */
149
155
  root: string;
@@ -166,4 +172,4 @@ interface BuildPluginBackendOptions {
166
172
  */
167
173
  declare function buildPluginBackend(part: ResolvedBackendPart, options: BuildPluginBackendOptions): Promise<void>;
168
174
 
169
- export { type BuildPluginBackendOptions, type PackPluginOptions, type PackedPlugin, type WatchPluginOptions, buildAgentTemplate, buildPluginBackend, buildPluginTemplates, lockPlugin, packPlugin, watchPluginFrontend };
175
+ export { type BuildPluginBackendOptions, type PackPluginOptions, type PackedPlugin, type WatchPluginOptions, buildAgentTemplate, buildPluginBackend, buildPluginTemplates, lockPlugin, packPlugin, readManifest, watchPluginFrontend };
package/dist/pack.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { T as TegoPluginManifest, R as ResolvedBackendPart } from './manifest-types-DQARCQJt.js';
1
+ import { T as TegoPluginManifest, R as ResolvedBackendPart } from './manifest-types-CUTNfJTa.js';
2
2
 
3
3
  /**
4
4
  * Staging a `runtime: "python"` backend: source, locked wheels, and a lock
@@ -85,6 +85,11 @@ interface PackPluginOptions {
85
85
  };
86
86
  /** Where to report progress. Defaults to `console.log`. Pass `null` to mute. */
87
87
  log?: ((message: string) => void) | null;
88
+ /**
89
+ * Run `tego-plugin doctor` first (default). Packing stops on the problems
90
+ * upload would refuse anyway; the rest are summarized, not fatal.
91
+ */
92
+ doctor?: boolean;
88
93
  }
89
94
  interface PackedPlugin {
90
95
  /** Absolute path of the archive. */
@@ -144,6 +149,7 @@ interface WatchPluginOptions {
144
149
  * Never resolves — the caller is a foreground command the author interrupts.
145
150
  */
146
151
  declare function watchPluginFrontend(options: WatchPluginOptions): Promise<never>;
152
+ declare function readManifest(root: string): Promise<TegoPluginManifest>;
147
153
  interface BuildPluginBackendOptions {
148
154
  /** Package root: the directory holding `tego.plugin.json`. */
149
155
  root: string;
@@ -166,4 +172,4 @@ interface BuildPluginBackendOptions {
166
172
  */
167
173
  declare function buildPluginBackend(part: ResolvedBackendPart, options: BuildPluginBackendOptions): Promise<void>;
168
174
 
169
- export { type BuildPluginBackendOptions, type PackPluginOptions, type PackedPlugin, type WatchPluginOptions, buildAgentTemplate, buildPluginBackend, buildPluginTemplates, lockPlugin, packPlugin, watchPluginFrontend };
175
+ export { type BuildPluginBackendOptions, type PackPluginOptions, type PackedPlugin, type WatchPluginOptions, buildAgentTemplate, buildPluginBackend, buildPluginTemplates, lockPlugin, packPlugin, readManifest, watchPluginFrontend };
package/dist/pack.js CHANGED
@@ -1 +1 @@
1
- export{buildAgentTemplate,buildPluginBackend,buildPluginTemplates,lockPlugin,packPlugin,watchPluginFrontend}from"./chunk-3KUOBGJN.js";import"./chunk-7K37PVGV.js";import"./chunk-5CU33JVY.js";import"./chunk-BTHXQWHT.js";import"./chunk-7XHF3YSK.js";import"./chunk-VMCGDDGP.js";import"./chunk-SNUCO2MK.js";
1
+ export{buildAgentTemplate,buildPluginBackend,buildPluginTemplates,lockPlugin,packPlugin,readManifest,watchPluginFrontend}from"./chunk-FCYAAOHC.js";import"./chunk-I5Z3ECC4.js";import"./chunk-EIVA5BR6.js";import"./chunk-2NTAWYCG.js";import"./chunk-IVJECPGG.js";import"./chunk-INCA3GFW.js";import"./chunk-56QOZQTY.js";import"./chunk-VMCGDDGP.js";import"./chunk-SNUCO2MK.js";
package/dist/publish.cjs CHANGED
@@ -1 +1 @@
1
- "use strict";var $=Object.defineProperty;var s=(e,a)=>$(e,"name",{value:a,configurable:!0});var chunkTHMWRUAT_cjs=require("./chunk-THMWRUAT.cjs");require("./chunk-W7C357A6.cjs"),require("./chunk-3WA3M347.cjs");var chunkROFUQLOZ_cjs=require("./chunk-ROFUQLOZ.cjs");require("./chunk-C2OCOPZQ.cjs"),require("./chunk-AJY7ZHXK.cjs"),require("./chunk-OP64Y4IN.cjs");var promises=require("fs/promises"),path=require("path"),API_PREFIX="/api/v1/tego-os";async function publishPlugin(e){const a=e.log===null?()=>{}:e.log??(t=>console.log(t)),n=await chunkTHMWRUAT_cjs.packPlugin({root:e.root,log:a});return publishPackedPlugin(n,e)}s(publishPlugin,"publishPlugin");async function publishPackedPlugin(e,a){const n=a.log===null?()=>{}:a.log??(p=>console.log(p)),t=a.fetchImpl??fetch,r=createCaller(a.server,a.token,t),u=await promises.readFile(e.file),o=path.basename(e.file);n("\u203A presigning upload");const c=await r("POST","/admin/runtime-plugins/presign-stream",{filename:o,mimeType:"application/zip",size:u.byteLength});n(`\u203A uploading ${o} (${u.byteLength} bytes)`),await putArchive(a.server,c.uploadUrl,u,t),n("\u203A inspecting package");const l=await r("POST","/admin/runtime-plugins/inspect",{uploadToken:c.uploadToken});n(` ${l.pluginId}@${l.semanticVersion} \u2014 ${describeParts(l)}${l.isUpgrade?" (upgrade)":""}${l.requiresConfig?", needs config":""}`),n("\u203A finalizing release");const g=await r("POST","/admin/runtime-plugins/upload",{uploadToken:c.uploadToken,...a.meta});let i=g.release;n(` release ${i.id} (${i.status})`),(a.approve??!0)&&(n("\u203A approving release"),i=await r("POST",`/admin/runtime-plugins/releases/${encodeURIComponent(i.id)}/approve`,{}),n(` release ${i.id} (${i.status})`));let d=null;if(a.install??!0){const p=chunkROFUQLOZ_cjs.resolvePluginScope(e.manifest),f=p==="avatar"?{releaseId:i.id,avatarIds:a.avatarIds??[]}:{releaseId:i.id};n(`\u203A installing for tenant (${p}-scoped)`),d=await r("POST","/admin/runtime-plugins/installs",f),reportInstall(d,n)}return{packed:e,preview:l,catalog:g.catalog,release:i,install:d}}s(publishPackedPlugin,"publishPackedPlugin");function describeParts(e){const a=[];return e.parts.frontend&&a.push(`frontend (${e.parts.frontend.locales.length} locales)`),e.parts.backend&&a.push(`backend (${e.parts.backend.migrationCount} migrations)`),e.parts.claw&&a.push("claw"),a.join(", ")||"no parts"}s(describeParts,"describeParts");function reportInstall(e,a){const n=e.avatars;if(!n||n.attempted===0){a(` installed ${e.pluginId}@${e.version}`);return}const t=n.attempted-n.failures.length;a(` installed ${e.pluginId}@${e.version} \u2014 ${t}/${n.attempted} avatars`);for(const r of n.failures)a(` \u2717 ${r.avatarId}: ${r.message}`)}s(reportInstall,"reportInstall");function createCaller(e,a,n){const t=e.replace(/\/+$/,"");return s(async function(u,o,c){const l=await n(`${t}${API_PREFIX}${o}`,{method:u,headers:{authorization:`Bearer ${a}`,...c===void 0?{}:{"content-type":"application/json"}},...c===void 0?{}:{body:JSON.stringify(c)}});return unwrap(l,`${u} ${o}`)},"call")}s(createCaller,"createCaller");async function putArchive(e,a,n,t){const r=e.replace(/\/+$/,""),u=await t(`${r}${a}`,{method:"PUT",headers:{"content-type":"application/zip"},body:n});await unwrap(u,"PUT upload")}s(putArchive,"putArchive");async function unwrap(e,a){let n;try{n=await e.json()}catch{n=null}if(!e.ok||!n?.success){const t=n?.error?.message??`HTTP ${e.status}`;throw new Error(`${a} failed: ${t}`)}return n.data}s(unwrap,"unwrap"),exports.publishPackedPlugin=publishPackedPlugin,exports.publishPlugin=publishPlugin;
1
+ "use strict";var $=Object.defineProperty;var s=(e,a)=>$(e,"name",{value:a,configurable:!0});var chunk4LKRXDYY_cjs=require("./chunk-4LKRXDYY.cjs");require("./chunk-HZ4YWRYX.cjs"),require("./chunk-SKNPNPJP.cjs"),require("./chunk-45Y4CR4R.cjs");var chunk2P6UEAPV_cjs=require("./chunk-2P6UEAPV.cjs");require("./chunk-N4SXZZPY.cjs"),require("./chunk-6HNG5OZI.cjs"),require("./chunk-AJY7ZHXK.cjs"),require("./chunk-OP64Y4IN.cjs");var promises=require("fs/promises"),path=require("path"),API_PREFIX="/api/v1/tego-os";async function publishPlugin(e){const a=e.log===null?()=>{}:e.log??(t=>console.log(t)),n=await chunk4LKRXDYY_cjs.packPlugin({root:e.root,log:a});return publishPackedPlugin(n,e)}s(publishPlugin,"publishPlugin");async function publishPackedPlugin(e,a){const n=a.log===null?()=>{}:a.log??(p=>console.log(p)),t=a.fetchImpl??fetch,r=createCaller(a.server,a.token,t),u=await promises.readFile(e.file),o=path.basename(e.file);n("\u203A presigning upload");const l=await r("POST","/admin/runtime-plugins/presign-stream",{filename:o,mimeType:"application/zip",size:u.byteLength});n(`\u203A uploading ${o} (${u.byteLength} bytes)`),await putArchive(a.server,l.uploadUrl,u,t),n("\u203A inspecting package");const c=await r("POST","/admin/runtime-plugins/inspect",{uploadToken:l.uploadToken});n(` ${c.pluginId}@${c.semanticVersion} \u2014 ${describeParts(c)}${c.isUpgrade?" (upgrade)":""}${c.requiresConfig?", needs config":""}`),n("\u203A finalizing release");const g=await r("POST","/admin/runtime-plugins/upload",{uploadToken:l.uploadToken,...a.meta});let i=g.release;n(` release ${i.id} (${i.status})`),(a.approve??!0)&&(n("\u203A approving release"),i=await r("POST",`/admin/runtime-plugins/releases/${encodeURIComponent(i.id)}/approve`,{}),n(` release ${i.id} (${i.status})`));let d=null;if(a.install??!0){const p=chunk2P6UEAPV_cjs.resolvePluginScope(e.manifest),f=p==="avatar"?{releaseId:i.id,avatarIds:a.avatarIds??[]}:{releaseId:i.id};n(`\u203A installing for tenant (${p}-scoped)`),d=await r("POST","/admin/runtime-plugins/installs",f),reportInstall(d,n)}return{packed:e,preview:c,catalog:g.catalog,release:i,install:d}}s(publishPackedPlugin,"publishPackedPlugin");function describeParts(e){const a=[];return e.parts.frontend&&a.push(`frontend (${e.parts.frontend.locales.length} locales)`),e.parts.backend&&a.push(`backend (${e.parts.backend.migrationCount} migrations)`),e.parts.claw&&a.push("claw"),a.join(", ")||"no parts"}s(describeParts,"describeParts");function reportInstall(e,a){const n=e.avatars;if(!n||n.attempted===0){a(` installed ${e.pluginId}@${e.version}`);return}const t=n.attempted-n.failures.length;a(` installed ${e.pluginId}@${e.version} \u2014 ${t}/${n.attempted} avatars`);for(const r of n.failures)a(` \u2717 ${r.avatarId}: ${r.message}`)}s(reportInstall,"reportInstall");function createCaller(e,a,n){const t=e.replace(/\/+$/,"");return s(async function(u,o,l){const c=await n(`${t}${API_PREFIX}${o}`,{method:u,headers:{authorization:`Bearer ${a}`,...l===void 0?{}:{"content-type":"application/json"}},...l===void 0?{}:{body:JSON.stringify(l)}});return unwrap(c,`${u} ${o}`)},"call")}s(createCaller,"createCaller");async function putArchive(e,a,n,t){const r=e.replace(/\/+$/,""),u=await t(`${r}${a}`,{method:"PUT",headers:{"content-type":"application/zip"},body:n});await unwrap(u,"PUT upload")}s(putArchive,"putArchive");async function unwrap(e,a){let n;try{n=await e.json()}catch{n=null}if(!e.ok||!n?.success){const t=n?.error?.message??`HTTP ${e.status}`;throw new Error(`${a} failed: ${t}`)}return n.data}s(unwrap,"unwrap"),exports.publishPackedPlugin=publishPackedPlugin,exports.publishPlugin=publishPlugin;
@@ -1,5 +1,5 @@
1
1
  import { PackedPlugin } from './pack.cjs';
2
- import './manifest-types-DQARCQJt.cjs';
2
+ import './manifest-types-CUTNfJTa.cjs';
3
3
 
4
4
  interface PublishPluginOptions {
5
5
  /** Package root: the directory holding `tego.plugin.json`. */
package/dist/publish.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { PackedPlugin } from './pack.js';
2
- import './manifest-types-DQARCQJt.js';
2
+ import './manifest-types-CUTNfJTa.js';
3
3
 
4
4
  interface PublishPluginOptions {
5
5
  /** Package root: the directory holding `tego.plugin.json`. */
package/dist/publish.js CHANGED
@@ -1 +1 @@
1
- var h=Object.defineProperty;var o=(e,a)=>h(e,"name",{value:a,configurable:!0});import{packPlugin as v}from"./chunk-3KUOBGJN.js";import"./chunk-7K37PVGV.js";import"./chunk-5CU33JVY.js";import{resolvePluginScope as P}from"./chunk-BTHXQWHT.js";import"./chunk-7XHF3YSK.js";import"./chunk-VMCGDDGP.js";import"./chunk-SNUCO2MK.js";import{readFile as w}from"fs/promises";import{basename as y}from"path";var I="/api/v1/tego-os";async function T(e){const a=e.log===null?()=>{}:e.log??(n=>console.log(n)),t=await v({root:e.root,log:a});return f(t,e)}o(T,"publishPlugin");async function f(e,a){const t=a.log===null?()=>{}:a.log??(p=>console.log(p)),n=a.fetchImpl??fetch,r=A(a.server,a.token,n),s=await w(e.file),c=y(e.file);t("\u203A presigning upload");const u=await r("POST","/admin/runtime-plugins/presign-stream",{filename:c,mimeType:"application/zip",size:s.byteLength});t(`\u203A uploading ${c} (${s.byteLength} bytes)`),await S(a.server,u.uploadUrl,s,n),t("\u203A inspecting package");const l=await r("POST","/admin/runtime-plugins/inspect",{uploadToken:u.uploadToken});t(` ${l.pluginId}@${l.semanticVersion} \u2014 ${b(l)}${l.isUpgrade?" (upgrade)":""}${l.requiresConfig?", needs config":""}`),t("\u203A finalizing release");const g=await r("POST","/admin/runtime-plugins/upload",{uploadToken:u.uploadToken,...a.meta});let i=g.release;t(` release ${i.id} (${i.status})`),(a.approve??!0)&&(t("\u203A approving release"),i=await r("POST",`/admin/runtime-plugins/releases/${encodeURIComponent(i.id)}/approve`,{}),t(` release ${i.id} (${i.status})`));let d=null;if(a.install??!0){const p=P(e.manifest),$=p==="avatar"?{releaseId:i.id,avatarIds:a.avatarIds??[]}:{releaseId:i.id};t(`\u203A installing for tenant (${p}-scoped)`),d=await r("POST","/admin/runtime-plugins/installs",$),k(d,t)}return{packed:e,preview:l,catalog:g.catalog,release:i,install:d}}o(f,"publishPackedPlugin");function b(e){const a=[];return e.parts.frontend&&a.push(`frontend (${e.parts.frontend.locales.length} locales)`),e.parts.backend&&a.push(`backend (${e.parts.backend.migrationCount} migrations)`),e.parts.claw&&a.push("claw"),a.join(", ")||"no parts"}o(b,"describeParts");function k(e,a){const t=e.avatars;if(!t||t.attempted===0){a(` installed ${e.pluginId}@${e.version}`);return}const n=t.attempted-t.failures.length;a(` installed ${e.pluginId}@${e.version} \u2014 ${n}/${t.attempted} avatars`);for(const r of t.failures)a(` \u2717 ${r.avatarId}: ${r.message}`)}o(k,"reportInstall");function A(e,a,t){const n=e.replace(/\/+$/,"");return o(async function(s,c,u){const l=await t(`${n}${I}${c}`,{method:s,headers:{authorization:`Bearer ${a}`,...u===void 0?{}:{"content-type":"application/json"}},...u===void 0?{}:{body:JSON.stringify(u)}});return m(l,`${s} ${c}`)},"call")}o(A,"createCaller");async function S(e,a,t,n){const r=e.replace(/\/+$/,""),s=await n(`${r}${a}`,{method:"PUT",headers:{"content-type":"application/zip"},body:t});await m(s,"PUT upload")}o(S,"putArchive");async function m(e,a){let t;try{t=await e.json()}catch{t=null}if(!e.ok||!t?.success){const n=t?.error?.message??`HTTP ${e.status}`;throw new Error(`${a} failed: ${n}`)}return t.data}o(m,"unwrap");export{f as publishPackedPlugin,T as publishPlugin};
1
+ var h=Object.defineProperty;var o=(e,a)=>h(e,"name",{value:a,configurable:!0});import{packPlugin as v}from"./chunk-FCYAAOHC.js";import"./chunk-I5Z3ECC4.js";import"./chunk-EIVA5BR6.js";import"./chunk-2NTAWYCG.js";import{resolvePluginScope as P}from"./chunk-IVJECPGG.js";import"./chunk-INCA3GFW.js";import"./chunk-56QOZQTY.js";import"./chunk-VMCGDDGP.js";import"./chunk-SNUCO2MK.js";import{readFile as w}from"fs/promises";import{basename as y}from"path";var I="/api/v1/tego-os";async function T(e){const a=e.log===null?()=>{}:e.log??(n=>console.log(n)),t=await v({root:e.root,log:a});return m(t,e)}o(T,"publishPlugin");async function m(e,a){const t=a.log===null?()=>{}:a.log??(p=>console.log(p)),n=a.fetchImpl??fetch,r=A(a.server,a.token,n),s=await w(e.file),c=y(e.file);t("\u203A presigning upload");const u=await r("POST","/admin/runtime-plugins/presign-stream",{filename:c,mimeType:"application/zip",size:s.byteLength});t(`\u203A uploading ${c} (${s.byteLength} bytes)`),await S(a.server,u.uploadUrl,s,n),t("\u203A inspecting package");const l=await r("POST","/admin/runtime-plugins/inspect",{uploadToken:u.uploadToken});t(` ${l.pluginId}@${l.semanticVersion} \u2014 ${b(l)}${l.isUpgrade?" (upgrade)":""}${l.requiresConfig?", needs config":""}`),t("\u203A finalizing release");const g=await r("POST","/admin/runtime-plugins/upload",{uploadToken:u.uploadToken,...a.meta});let i=g.release;t(` release ${i.id} (${i.status})`),(a.approve??!0)&&(t("\u203A approving release"),i=await r("POST",`/admin/runtime-plugins/releases/${encodeURIComponent(i.id)}/approve`,{}),t(` release ${i.id} (${i.status})`));let d=null;if(a.install??!0){const p=P(e.manifest),$=p==="avatar"?{releaseId:i.id,avatarIds:a.avatarIds??[]}:{releaseId:i.id};t(`\u203A installing for tenant (${p}-scoped)`),d=await r("POST","/admin/runtime-plugins/installs",$),k(d,t)}return{packed:e,preview:l,catalog:g.catalog,release:i,install:d}}o(m,"publishPackedPlugin");function b(e){const a=[];return e.parts.frontend&&a.push(`frontend (${e.parts.frontend.locales.length} locales)`),e.parts.backend&&a.push(`backend (${e.parts.backend.migrationCount} migrations)`),e.parts.claw&&a.push("claw"),a.join(", ")||"no parts"}o(b,"describeParts");function k(e,a){const t=e.avatars;if(!t||t.attempted===0){a(` installed ${e.pluginId}@${e.version}`);return}const n=t.attempted-t.failures.length;a(` installed ${e.pluginId}@${e.version} \u2014 ${n}/${t.attempted} avatars`);for(const r of t.failures)a(` \u2717 ${r.avatarId}: ${r.message}`)}o(k,"reportInstall");function A(e,a,t){const n=e.replace(/\/+$/,"");return o(async function(s,c,u){const l=await t(`${n}${I}${c}`,{method:s,headers:{authorization:`Bearer ${a}`,...u===void 0?{}:{"content-type":"application/json"}},...u===void 0?{}:{body:JSON.stringify(u)}});return f(l,`${s} ${c}`)},"call")}o(A,"createCaller");async function S(e,a,t,n){const r=e.replace(/\/+$/,""),s=await n(`${r}${a}`,{method:"PUT",headers:{"content-type":"application/zip"},body:t});await f(s,"PUT upload")}o(S,"putArchive");async function f(e,a){let t;try{t=await e.json()}catch{t=null}if(!e.ok||!t?.success){const n=t?.error?.message??`HTTP ${e.status}`;throw new Error(`${a} failed: ${n}`)}return t.data}o(f,"unwrap");export{m as publishPackedPlugin,T as publishPlugin};
package/dist/react.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";var p=Object.defineProperty;var n=(e,t)=>p(e,"name",{value:t,configurable:!0});var react=require("react"),PluginApiError=class extends Error{static{n(this,"PluginApiError")}constructor(e,t,r,s={}){super(e),this.status=t,this.code=r,this.detail=s,this.name="PluginApiError"}status;code;detail};function toPluginApiError(e,t){const r=t&&typeof t=="object"?t.error:void 0;if(typeof r=="string")return new PluginApiError(r,e,void 0);if(r&&typeof r=="object"){const{code:s,message:i,detail:o}=r;return new PluginApiError(typeof i=="string"?i:`HTTP ${e}`,e,typeof s=="string"?s:void 0,o&&typeof o=="object"?o:{})}return new PluginApiError(`HTTP ${e}`,e,void 0)}n(toPluginApiError,"toPluginApiError");function createApiClient(e){const t=n(async(r,s,i)=>{const o=await e.request(s,i===void 0?{method:r}:{method:r,headers:{"content-type":"application/json"},body:JSON.stringify(i)}),u=await o.text();let c=null;try{c=u?JSON.parse(u):null}catch{c=u||null}if(!o.ok)throw toPluginApiError(o.status,c);return c},"send");return{get:n(r=>t("GET",r),"get"),post:n((r,s)=>t("POST",r,s??{}),"post"),put:n((r,s)=>t("PUT",r,s),"put"),patch:n((r,s)=>t("PATCH",r,s),"patch"),delete:n(r=>t("DELETE",r),"delete")}}n(createApiClient,"createApiClient");function withQuery(e,t){const r=new URLSearchParams;for(const[i,o]of Object.entries(t))o!==void 0&&o!==""&&r.set(i,String(o));const s=r.toString();return s?`${e}?${s}`:e}n(withQuery,"withQuery");function describeApiError(e,t){return e instanceof PluginApiError?e.code?{key:`${t}.errors.${e.code}`,message:e.message}:{message:e.message}:{message:e instanceof Error?e.message:String(e)}}n(describeApiError,"describeApiError");var HostContext=react.createContext(null);function HostProvider({value:e,children:t}){return react.createElement(HostContext.Provider,{value:e},t)}n(HostProvider,"HostProvider");function useHost(){const e=react.useContext(HostContext);if(!e)throw new Error("useHost() outside <HostProvider>");return e}n(useHost,"useHost");function usePluginRoute(){const{route:e}=useHost();return react.useSyncExternalStore(t=>e.subscribe(t),()=>e.current())}n(usePluginRoute,"usePluginRoute");function useLiveUpdates(e,t={}){const{events:r}=useHost(),s=react.useRef({onChange:e,filter:t.filter});s.current={onChange:e,filter:t.filter};const i=t.waitMs??400;react.useEffect(()=>{let o=[],u;const c=r.subscribe(a=>{s.current.filter&&!s.current.filter(a)||(o.push(a),!u&&(u=setTimeout(()=>{u=void 0;const l=o;o=[],s.current.onChange(l)},i)))});return()=>{c(),u&&clearTimeout(u)}},[r,i])}n(useLiveUpdates,"useLiveUpdates");var PluginErrorBoundary=class extends react.Component{static{n(this,"PluginErrorBoundary")}state={error:null};static getDerivedStateFromError(e){return{error:e instanceof Error?e:new Error(String(e))}}componentDidCatch(e,t){this.props.onError?.(e,t)}reset=n(()=>this.setState({error:null}),"reset");render(){return this.state.error?this.props.fallback(this.state.error,this.reset):this.props.children??null}};exports.HostProvider=HostProvider,exports.PluginApiError=PluginApiError,exports.PluginErrorBoundary=PluginErrorBoundary,exports.createApiClient=createApiClient,exports.describeApiError=describeApiError,exports.toPluginApiError=toPluginApiError,exports.useHost=useHost,exports.useLiveUpdates=useLiveUpdates,exports.usePluginRoute=usePluginRoute,exports.withQuery=withQuery;
@@ -0,0 +1,101 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, Component, ErrorInfo } from 'react';
3
+ import { PluginApiClient, PluginHostContext, PluginEvent, PluginRoute } from './frontend.cjs';
4
+
5
+ /**
6
+ * Calls to the plugin's own backend through `ctx.api`, with errors that keep
7
+ * what the backend said.
8
+ *
9
+ * `ctx.api.json` throws a bare `Error('<method> <path>: <status>')` and drops
10
+ * the body, so a screen cannot tell "that note is locked" from "the server
11
+ * fell over". This keeps the code for translation and the message for the
12
+ * fallback. Two shapes arrive here: the plugin backend's
13
+ * `{ error: { code, message, detail? } }` (what `pluginErrorHandler` writes)
14
+ * and the gateway's `{ success: false, error: string | { message } }`.
15
+ */
16
+
17
+ declare class PluginApiError extends Error {
18
+ readonly status: number;
19
+ readonly code: string | undefined;
20
+ readonly detail: Record<string, unknown>;
21
+ constructor(message: string, status: number, code: string | undefined, detail?: Record<string, unknown>);
22
+ }
23
+ /** The error a failed response carries, whichever side wrote it. */
24
+ declare function toPluginApiError(status: number, body: unknown): PluginApiError;
25
+ interface PluginApi {
26
+ get<T>(path: string): Promise<T>;
27
+ /** A POST without a body still sends `{}`: validators read JSON on every write. */
28
+ post<T>(path: string, body?: unknown): Promise<T>;
29
+ put<T>(path: string, body: unknown): Promise<T>;
30
+ patch<T>(path: string, body: unknown): Promise<T>;
31
+ delete<T>(path: string): Promise<T>;
32
+ }
33
+ declare function createApiClient(client: PluginApiClient): PluginApi;
34
+ /** `path?a=1&b=x`, leaving out empty values. */
35
+ declare function withQuery(path: string, params: Record<string, string | number | undefined>): string;
36
+ /**
37
+ * What to tell the user about a failed call: the translation key
38
+ * `<namespace>.errors.<code>` when the backend sent a code, and the backend's
39
+ * own sentence as the fallback.
40
+ *
41
+ * ```ts
42
+ * const { key, message } = describeApiError(error, 'notes');
43
+ * return key ? t(key, { defaultValue: message }) : message;
44
+ * ```
45
+ */
46
+ declare function describeApiError(error: unknown, namespace: string): {
47
+ key?: string;
48
+ message: string;
49
+ };
50
+
51
+ /** Makes `ctx` available to `useHost` / `usePluginRoute` below it. */
52
+ declare function HostProvider({ value, children, }: {
53
+ value: PluginHostContext;
54
+ children?: ReactNode;
55
+ }): react.FunctionComponentElement<react.ProviderProps<PluginHostContext | null>>;
56
+ declare function useHost(): PluginHostContext;
57
+ /**
58
+ * The in-plugin route, re-rendering on every host navigation.
59
+ *
60
+ * The shell does not remount the page when only the path below `/p/<id>`
61
+ * changes, so reading `ctx.route.current()` once is a stale page.
62
+ */
63
+ declare function usePluginRoute(): PluginRoute;
64
+ /**
65
+ * Calls `onChange` when the plugin's backend publishes events, at most once
66
+ * per `waitMs` (a burst of writes is one refresh). Pass a stable callback,
67
+ * e.g. `() => queryClient.invalidateQueries({ queryKey: ['notes'] })`.
68
+ *
69
+ * `filter` limits it to some event names. The stream may be down: keep a slow
70
+ * `refetchInterval` as a fallback (faster while `ctx.events.connected()` is false).
71
+ */
72
+ declare function useLiveUpdates(onChange: (events: PluginEvent[]) => void, options?: {
73
+ waitMs?: number;
74
+ filter?: (event: PluginEvent) => boolean;
75
+ }): void;
76
+ interface BoundaryProps {
77
+ /** What to show instead of the crashed tree; `reset` renders the children again. */
78
+ fallback: (error: Error, reset: () => void) => ReactNode;
79
+ onError?(error: Error, info: ErrorInfo): void;
80
+ children?: ReactNode;
81
+ }
82
+ /**
83
+ * The plugin's own error boundary. The shell's boundary sits outside this
84
+ * React root and never sees an error thrown inside it: without one a render
85
+ * error is a blank page that reads as the platform losing the plugin.
86
+ */
87
+ declare class PluginErrorBoundary extends Component<BoundaryProps, {
88
+ error: Error | null;
89
+ }> {
90
+ state: {
91
+ error: Error | null;
92
+ };
93
+ static getDerivedStateFromError(error: unknown): {
94
+ error: Error;
95
+ };
96
+ componentDidCatch(error: Error, info: ErrorInfo): void;
97
+ private readonly reset;
98
+ render(): ReactNode;
99
+ }
100
+
101
+ export { HostProvider, type PluginApi, PluginApiError, PluginErrorBoundary, createApiClient, describeApiError, toPluginApiError, useHost, useLiveUpdates, usePluginRoute, withQuery };
@@ -0,0 +1,101 @@
1
+ import * as react from 'react';
2
+ import { ReactNode, Component, ErrorInfo } from 'react';
3
+ import { PluginApiClient, PluginHostContext, PluginEvent, PluginRoute } from './frontend.js';
4
+
5
+ /**
6
+ * Calls to the plugin's own backend through `ctx.api`, with errors that keep
7
+ * what the backend said.
8
+ *
9
+ * `ctx.api.json` throws a bare `Error('<method> <path>: <status>')` and drops
10
+ * the body, so a screen cannot tell "that note is locked" from "the server
11
+ * fell over". This keeps the code for translation and the message for the
12
+ * fallback. Two shapes arrive here: the plugin backend's
13
+ * `{ error: { code, message, detail? } }` (what `pluginErrorHandler` writes)
14
+ * and the gateway's `{ success: false, error: string | { message } }`.
15
+ */
16
+
17
+ declare class PluginApiError extends Error {
18
+ readonly status: number;
19
+ readonly code: string | undefined;
20
+ readonly detail: Record<string, unknown>;
21
+ constructor(message: string, status: number, code: string | undefined, detail?: Record<string, unknown>);
22
+ }
23
+ /** The error a failed response carries, whichever side wrote it. */
24
+ declare function toPluginApiError(status: number, body: unknown): PluginApiError;
25
+ interface PluginApi {
26
+ get<T>(path: string): Promise<T>;
27
+ /** A POST without a body still sends `{}`: validators read JSON on every write. */
28
+ post<T>(path: string, body?: unknown): Promise<T>;
29
+ put<T>(path: string, body: unknown): Promise<T>;
30
+ patch<T>(path: string, body: unknown): Promise<T>;
31
+ delete<T>(path: string): Promise<T>;
32
+ }
33
+ declare function createApiClient(client: PluginApiClient): PluginApi;
34
+ /** `path?a=1&b=x`, leaving out empty values. */
35
+ declare function withQuery(path: string, params: Record<string, string | number | undefined>): string;
36
+ /**
37
+ * What to tell the user about a failed call: the translation key
38
+ * `<namespace>.errors.<code>` when the backend sent a code, and the backend's
39
+ * own sentence as the fallback.
40
+ *
41
+ * ```ts
42
+ * const { key, message } = describeApiError(error, 'notes');
43
+ * return key ? t(key, { defaultValue: message }) : message;
44
+ * ```
45
+ */
46
+ declare function describeApiError(error: unknown, namespace: string): {
47
+ key?: string;
48
+ message: string;
49
+ };
50
+
51
+ /** Makes `ctx` available to `useHost` / `usePluginRoute` below it. */
52
+ declare function HostProvider({ value, children, }: {
53
+ value: PluginHostContext;
54
+ children?: ReactNode;
55
+ }): react.FunctionComponentElement<react.ProviderProps<PluginHostContext | null>>;
56
+ declare function useHost(): PluginHostContext;
57
+ /**
58
+ * The in-plugin route, re-rendering on every host navigation.
59
+ *
60
+ * The shell does not remount the page when only the path below `/p/<id>`
61
+ * changes, so reading `ctx.route.current()` once is a stale page.
62
+ */
63
+ declare function usePluginRoute(): PluginRoute;
64
+ /**
65
+ * Calls `onChange` when the plugin's backend publishes events, at most once
66
+ * per `waitMs` (a burst of writes is one refresh). Pass a stable callback,
67
+ * e.g. `() => queryClient.invalidateQueries({ queryKey: ['notes'] })`.
68
+ *
69
+ * `filter` limits it to some event names. The stream may be down: keep a slow
70
+ * `refetchInterval` as a fallback (faster while `ctx.events.connected()` is false).
71
+ */
72
+ declare function useLiveUpdates(onChange: (events: PluginEvent[]) => void, options?: {
73
+ waitMs?: number;
74
+ filter?: (event: PluginEvent) => boolean;
75
+ }): void;
76
+ interface BoundaryProps {
77
+ /** What to show instead of the crashed tree; `reset` renders the children again. */
78
+ fallback: (error: Error, reset: () => void) => ReactNode;
79
+ onError?(error: Error, info: ErrorInfo): void;
80
+ children?: ReactNode;
81
+ }
82
+ /**
83
+ * The plugin's own error boundary. The shell's boundary sits outside this
84
+ * React root and never sees an error thrown inside it: without one a render
85
+ * error is a blank page that reads as the platform losing the plugin.
86
+ */
87
+ declare class PluginErrorBoundary extends Component<BoundaryProps, {
88
+ error: Error | null;
89
+ }> {
90
+ state: {
91
+ error: Error | null;
92
+ };
93
+ static getDerivedStateFromError(error: unknown): {
94
+ error: Error;
95
+ };
96
+ componentDidCatch(error: Error, info: ErrorInfo): void;
97
+ private readonly reset;
98
+ render(): ReactNode;
99
+ }
100
+
101
+ export { HostProvider, type PluginApi, PluginApiError, PluginErrorBoundary, createApiClient, describeApiError, toPluginApiError, useHost, useLiveUpdates, usePluginRoute, withQuery };
package/dist/react.js ADDED
@@ -0,0 +1 @@
1
+ var h=Object.defineProperty;var s=(e,t)=>h(e,"name",{value:t,configurable:!0});import{createContext as E,createElement as m,useContext as v,useSyncExternalStore as P,useRef as w,useEffect as y,Component as S}from"react";var a=class extends Error{static{s(this,"PluginApiError")}constructor(e,t,r,n={}){super(e),this.status=t,this.code=r,this.detail=n,this.name="PluginApiError"}status;code;detail};function d(e,t){const r=t&&typeof t=="object"?t.error:void 0;if(typeof r=="string")return new a(r,e,void 0);if(r&&typeof r=="object"){const{code:n,message:i,detail:o}=r;return new a(typeof i=="string"?i:`HTTP ${e}`,e,typeof n=="string"?n:void 0,o&&typeof o=="object"?o:{})}return new a(`HTTP ${e}`,e,void 0)}s(d,"toPluginApiError");function b(e){const t=s(async(r,n,i)=>{const o=await e.request(n,i===void 0?{method:r}:{method:r,headers:{"content-type":"application/json"},body:JSON.stringify(i)}),c=await o.text();let u=null;try{u=c?JSON.parse(c):null}catch{u=c||null}if(!o.ok)throw d(o.status,u);return u},"send");return{get:s(r=>t("GET",r),"get"),post:s((r,n)=>t("POST",r,n??{}),"post"),put:s((r,n)=>t("PUT",r,n),"put"),patch:s((r,n)=>t("PATCH",r,n),"patch"),delete:s(r=>t("DELETE",r),"delete")}}s(b,"createApiClient");function T(e,t){const r=new URLSearchParams;for(const[i,o]of Object.entries(t))o!==void 0&&o!==""&&r.set(i,String(o));const n=r.toString();return n?`${e}?${n}`:e}s(T,"withQuery");function x(e,t){return e instanceof a?e.code?{key:`${t}.errors.${e.code}`,message:e.message}:{message:e.message}:{message:e instanceof Error?e.message:String(e)}}s(x,"describeApiError");var p=E(null);function H({value:e,children:t}){return m(p.Provider,{value:e},t)}s(H,"HostProvider");function l(){const e=v(p);if(!e)throw new Error("useHost() outside <HostProvider>");return e}s(l,"useHost");function C(){const{route:e}=l();return P(t=>e.subscribe(t),()=>e.current())}s(C,"usePluginRoute");function A(e,t={}){const{events:r}=l(),n=w({onChange:e,filter:t.filter});n.current={onChange:e,filter:t.filter};const i=t.waitMs??400;y(()=>{let o=[],c;const u=r.subscribe(f=>{n.current.filter&&!n.current.filter(f)||(o.push(f),!c&&(c=setTimeout(()=>{c=void 0;const g=o;o=[],n.current.onChange(g)},i)))});return()=>{u(),c&&clearTimeout(c)}},[r,i])}s(A,"useLiveUpdates");var $=class extends S{static{s(this,"PluginErrorBoundary")}state={error:null};static getDerivedStateFromError(e){return{error:e instanceof Error?e:new Error(String(e))}}componentDidCatch(e,t){this.props.onError?.(e,t)}reset=s(()=>this.setState({error:null}),"reset");render(){return this.state.error?this.props.fallback(this.state.error,this.reset):this.props.children??null}};export{H as HostProvider,a as PluginApiError,$ as PluginErrorBoundary,b as createApiClient,x as describeApiError,d as toPluginApiError,l as useHost,A as useLiveUpdates,C as usePluginRoute,T as withQuery};