@zhama/tego-plugin-sdk 1.36.2 → 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 (93) hide show
  1. package/agent/RULES.md +123 -0
  2. package/agent/guides/access.md +39 -0
  3. package/agent/guides/backend.md +76 -0
  4. package/agent/guides/claw.md +18 -0
  5. package/agent/guides/desktop-web.md +48 -0
  6. package/agent/guides/frontend.md +83 -0
  7. package/agent/guides/host-api-versions.md +34 -0
  8. package/agent/guides/i18n.md +17 -0
  9. package/agent/guides/manifest.md +26 -0
  10. package/agent/guides/migrations.md +17 -0
  11. package/agent/guides/storage.md +18 -0
  12. package/agent/guides/testing.md +17 -0
  13. package/agent/guides/ui.md +22 -0
  14. package/agent/reference/plugin-sdk-api.md +561 -0
  15. package/bin/tego-plugin-pack.mjs +1 -1
  16. package/bin/tego-plugin.mjs +4 -2
  17. package/dist/chunk-2NTAWYCG.js +1 -0
  18. package/dist/chunk-2P6UEAPV.cjs +1 -0
  19. package/dist/chunk-45Y4CR4R.cjs +1 -0
  20. package/dist/{chunk-PWSTCQRN.cjs → chunk-4LKRXDYY.cjs} +13 -12
  21. package/dist/{chunk-7XHF3YSK.js → chunk-56QOZQTY.js} +1 -1
  22. package/dist/{chunk-C2OCOPZQ.cjs → chunk-6HNG5OZI.cjs} +1 -1
  23. package/dist/{chunk-2ORO36BY.js → chunk-DEGQDJF6.js} +1 -1
  24. package/dist/chunk-EIVA5BR6.js +1 -0
  25. package/dist/{chunk-P2Q2AKWP.js → chunk-FCYAAOHC.js} +17 -16
  26. package/dist/chunk-HZ4YWRYX.cjs +5 -0
  27. package/dist/chunk-I5Z3ECC4.js +5 -0
  28. package/dist/chunk-INCA3GFW.js +1 -0
  29. package/dist/chunk-IVJECPGG.js +1 -0
  30. package/dist/{chunk-KMN5RHLX.cjs → chunk-MWMGJJ74.cjs} +1 -1
  31. package/dist/chunk-N4SXZZPY.cjs +1 -0
  32. package/dist/chunk-SKNPNPJP.cjs +1 -0
  33. package/dist/dev.cjs +88 -5
  34. package/dist/dev.d.cts +37 -1
  35. package/dist/dev.d.ts +37 -1
  36. package/dist/dev.js +91 -8
  37. package/dist/doctor.cjs +8 -0
  38. package/dist/doctor.d.cts +62 -0
  39. package/dist/doctor.d.ts +62 -0
  40. package/dist/doctor.js +8 -0
  41. package/dist/frontend.d.cts +33 -1
  42. package/dist/frontend.d.ts +33 -1
  43. package/dist/hono.cjs +1 -0
  44. package/dist/hono.d.cts +84 -0
  45. package/dist/hono.d.ts +84 -0
  46. package/dist/hono.js +1 -0
  47. package/dist/index.cjs +1 -1
  48. package/dist/index.d.cts +6 -1
  49. package/dist/index.d.ts +6 -1
  50. package/dist/index.js +1 -1
  51. package/dist/manifest.cjs +1 -1
  52. package/dist/manifest.d.cts +55 -2
  53. package/dist/manifest.d.ts +55 -2
  54. package/dist/manifest.js +1 -1
  55. package/dist/migrations-sync.cjs +1 -0
  56. package/dist/migrations-sync.d.cts +20 -0
  57. package/dist/migrations-sync.d.ts +20 -0
  58. package/dist/migrations-sync.js +1 -0
  59. package/dist/pack.cjs +1 -1
  60. package/dist/pack.d.cts +5 -0
  61. package/dist/pack.d.ts +5 -0
  62. package/dist/pack.js +1 -1
  63. package/dist/publish.cjs +1 -1
  64. package/dist/publish.js +1 -1
  65. package/dist/react.cjs +1 -0
  66. package/dist/react.d.cts +101 -0
  67. package/dist/react.d.ts +101 -0
  68. package/dist/react.js +1 -0
  69. package/dist/scaffold.cjs +671 -17
  70. package/dist/scaffold.d.cts +42 -18
  71. package/dist/scaffold.d.ts +42 -18
  72. package/dist/scaffold.js +673 -19
  73. package/dist/server-runtime.cjs +1 -1
  74. package/dist/server-runtime.d.cts +27 -2
  75. package/dist/server-runtime.d.ts +27 -2
  76. package/dist/server-runtime.js +1 -1
  77. package/dist/testing.cjs +6 -2
  78. package/dist/testing.d.cts +19 -1
  79. package/dist/testing.d.ts +19 -1
  80. package/dist/testing.js +6 -2
  81. package/dist/verify-FF2RD2MV.cjs +1 -0
  82. package/dist/verify-W7ZTUCO4.js +1 -0
  83. package/dist/vite.cjs +1 -1
  84. package/dist/vite.d.cts +27 -1
  85. package/dist/vite.d.ts +27 -1
  86. package/dist/vite.js +1 -1
  87. package/package.json +31 -3
  88. package/dist/chunk-3WA3M347.cjs +0 -1
  89. package/dist/chunk-5CU33JVY.js +0 -1
  90. package/dist/chunk-7K37PVGV.js +0 -5
  91. package/dist/chunk-BTHXQWHT.js +0 -1
  92. package/dist/chunk-ROFUQLOZ.cjs +0 -1
  93. package/dist/chunk-W7C357A6.cjs +0 -5
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};