@mirrorstack-ai/app-module-client 0.5.1

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 (70) hide show
  1. package/CHANGELOG.md +152 -0
  2. package/LICENSE +202 -0
  3. package/README.md +488 -0
  4. package/dist/base-url.d.ts +39 -0
  5. package/dist/base-url.js +51 -0
  6. package/dist/base-url.js.map +1 -0
  7. package/dist/client.d.ts +56 -0
  8. package/dist/client.js +85 -0
  9. package/dist/client.js.map +1 -0
  10. package/dist/error.d.ts +29 -0
  11. package/dist/error.js +100 -0
  12. package/dist/error.js.map +1 -0
  13. package/dist/index.d.ts +6 -0
  14. package/dist/index.js +6 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/next/auth-routes.d.ts +82 -0
  17. package/dist/next/auth-routes.js +164 -0
  18. package/dist/next/auth-routes.js.map +1 -0
  19. package/dist/next/index.d.ts +12 -0
  20. package/dist/next/index.js +13 -0
  21. package/dist/next/index.js.map +1 -0
  22. package/dist/next/module-proxy-routes.d.ts +81 -0
  23. package/dist/next/module-proxy-routes.js +77 -0
  24. package/dist/next/module-proxy-routes.js.map +1 -0
  25. package/dist/plugin.d.ts +39 -0
  26. package/dist/plugin.js +39 -0
  27. package/dist/plugin.js.map +1 -0
  28. package/dist/response.d.ts +13 -0
  29. package/dist/response.js +80 -0
  30. package/dist/response.js.map +1 -0
  31. package/dist/server/index.d.ts +7 -0
  32. package/dist/server/index.js +8 -0
  33. package/dist/server/index.js.map +1 -0
  34. package/dist/server/member-sessions.d.ts +80 -0
  35. package/dist/server/member-sessions.js +162 -0
  36. package/dist/server/member-sessions.js.map +1 -0
  37. package/dist/transport.d.ts +98 -0
  38. package/dist/transport.js +299 -0
  39. package/dist/transport.js.map +1 -0
  40. package/dist/web/cache.d.ts +33 -0
  41. package/dist/web/cache.js +130 -0
  42. package/dist/web/cache.js.map +1 -0
  43. package/dist/web/component-mount.d.ts +23 -0
  44. package/dist/web/component-mount.js +111 -0
  45. package/dist/web/component-mount.js.map +1 -0
  46. package/dist/web/index.d.ts +6 -0
  47. package/dist/web/index.js +7 -0
  48. package/dist/web/index.js.map +1 -0
  49. package/dist/web/localized-text.d.ts +10 -0
  50. package/dist/web/localized-text.js +36 -0
  51. package/dist/web/localized-text.js.map +1 -0
  52. package/dist/web/react.d.ts +18 -0
  53. package/dist/web/react.js +111 -0
  54. package/dist/web/react.js.map +1 -0
  55. package/dist/web/runtime.d.ts +60 -0
  56. package/dist/web/runtime.js +73 -0
  57. package/dist/web/runtime.js.map +1 -0
  58. package/dist/web/subpath.d.ts +22 -0
  59. package/dist/web/subpath.js +50 -0
  60. package/dist/web/subpath.js.map +1 -0
  61. package/dist/web/types.d.ts +129 -0
  62. package/dist/web/types.js +2 -0
  63. package/dist/web/types.js.map +1 -0
  64. package/dist/web/use-now.d.ts +7 -0
  65. package/dist/web/use-now.js +22 -0
  66. package/dist/web/use-now.js.map +1 -0
  67. package/dist/web/use-platform-unsaved-state.d.ts +10 -0
  68. package/dist/web/use-platform-unsaved-state.js +35 -0
  69. package/dist/web/use-platform-unsaved-state.js.map +1 -0
  70. package/package.json +88 -0
@@ -0,0 +1,73 @@
1
+ import { assertModuleRef } from "../plugin.js";
2
+ import { resolveMaxResponseBytes } from "../response.js";
3
+ import { createScopedTransports } from "../transport.js";
4
+ /**
5
+ * Creates full public and platform transports for one mounted module.
6
+ *
7
+ * The host-provided fetch owns authentication and application identity.
8
+ * Browser module code cannot select trusted `X-MS-*` identity headers.
9
+ */
10
+ function composeWebTransports(options, scopePaths) {
11
+ assertModuleRef(options.moduleRef);
12
+ let apiBase = options.apiBase ?? "";
13
+ while (apiBase.endsWith("/"))
14
+ apiBase = apiBase.slice(0, -1);
15
+ const hostFetch = options.fetch;
16
+ const contextualFetch = async (input, init) => {
17
+ if (!hostFetch) {
18
+ throw new Error("Module " + options.moduleRef + " cannot request data before the host supplies fetch.");
19
+ }
20
+ return hostFetch(input, init);
21
+ };
22
+ return createScopedTransports({
23
+ baseUrl: apiBase,
24
+ fetch: contextualFetch,
25
+ credentials: "include",
26
+ maxResponseBytes: resolveMaxResponseBytes(options.maxResponseBytes),
27
+ }, options.moduleRef, apiBase, scopePaths);
28
+ }
29
+ export function createModuleWebTransports(options) {
30
+ // The Go SDK mounts each scope under its own name — ms.Public routes live at
31
+ // /public/ — so the public scope owns that segment exactly as platform owns
32
+ // /platform/. A module must never have to spell it in the path it requests.
33
+ return composeWebTransports(options, { public: "public", platform: "platform" });
34
+ }
35
+ function routeTarget(route, transports) {
36
+ if (route === "/platform") {
37
+ return { path: "/", transport: transports.platform };
38
+ }
39
+ if (route.startsWith("/platform/")) {
40
+ return { path: route.slice("/platform".length), transport: transports.platform };
41
+ }
42
+ return { path: route, transport: transports.public };
43
+ }
44
+ /**
45
+ * Creates the direct route-dispatching transport released in v0.1.0.
46
+ *
47
+ * @deprecated Prefer {@link createModuleWebTransports}, which keeps public and
48
+ * platform routes structurally separate and exposes the full scoped contract.
49
+ */
50
+ export function createModuleWebTransport(options) {
51
+ // This transport is documented as serving "public-root and platform-scoped
52
+ // routes": its callers pass whole paths, including the /public segment and
53
+ // root-level routes. It therefore keeps addressing the module ROOT, and does
54
+ // not inherit the /public segment createModuleWebTransports now owns.
55
+ const transports = composeWebTransports({
56
+ moduleRef: options.moduleRef,
57
+ ...(options.apiBase === undefined ? {} : { apiBase: options.apiBase }),
58
+ ...(options.fetch === undefined ? {} : { fetch: options.fetch }),
59
+ }, { public: "", platform: "platform" });
60
+ const invoke = async (method, route, requestOptions, responseType) => {
61
+ const target = routeTarget(route, transports);
62
+ const request = target.transport.request;
63
+ return request(method, target.path, {
64
+ ...requestOptions,
65
+ responseType,
66
+ });
67
+ };
68
+ return Object.freeze({
69
+ request: (method, route, requestOptions) => invoke(method, route, requestOptions, "json"),
70
+ text: (method, route, requestOptions) => invoke(method, route, requestOptions, "text"),
71
+ });
72
+ }
73
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../src/web/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["import { assertModuleRef, type ModuleClientContext } from \"../plugin.js\";\nimport { resolveMaxResponseBytes } from \"../response.js\";\nimport { createScopedTransports } from \"../transport.js\";\nimport type { PlatformFetch } from \"./types.js\";\n\n/** Configuration owned by the host at module mount time. */\nexport interface CreateModuleWebTransportsOptions {\n /** Canonical 1-16 character catalog slug or UUID used for routing and diagnostics. */\n readonly moduleRef: string;\n /** Dispatch root for this mounted module. Empty means same-origin. */\n readonly apiBase?: string;\n /** Authenticated fetch capability supplied by the platform host. */\n readonly fetch?: PlatformFetch;\n /** Maximum bytes parsed from response bodies. Defaults to one mebibyte. */\n readonly maxResponseBytes?: number;\n}\n\n/** Public-root and platform-scoped transports for one mounted module. */\nexport type ModuleWebTransports = ModuleClientContext;\n\n/**\n * Creates full public and platform transports for one mounted module.\n *\n * The host-provided fetch owns authentication and application identity.\n * Browser module code cannot select trusted `X-MS-*` identity headers.\n */\nfunction composeWebTransports(\n options: CreateModuleWebTransportsOptions,\n scopePaths: Partial<Record<\"public\" | \"platform\", string>>,\n): ModuleWebTransports {\n assertModuleRef(options.moduleRef);\n let apiBase = options.apiBase ?? \"\";\n while (apiBase.endsWith(\"/\")) apiBase = apiBase.slice(0, -1);\n const hostFetch = options.fetch;\n\n const contextualFetch: typeof globalThis.fetch = async (input, init) => {\n if (!hostFetch) {\n throw new Error(\n \"Module \" + options.moduleRef + \" cannot request data before the host supplies fetch.\",\n );\n }\n return hostFetch(input, init);\n };\n\n return createScopedTransports(\n {\n baseUrl: apiBase,\n fetch: contextualFetch,\n credentials: \"include\",\n maxResponseBytes: resolveMaxResponseBytes(options.maxResponseBytes),\n },\n options.moduleRef,\n apiBase,\n scopePaths,\n );\n}\n\nexport function createModuleWebTransports(\n options: CreateModuleWebTransportsOptions,\n): ModuleWebTransports {\n // The Go SDK mounts each scope under its own name — ms.Public routes live at\n // /public/ — so the public scope owns that segment exactly as platform owns\n // /platform/. A module must never have to spell it in the path it requests.\n return composeWebTransports(options, { public: \"public\", platform: \"platform\" });\n}\n\n/**\n * Options for an API request made through the v0.1.0 compatibility transport.\n *\n * @deprecated Prefer the complete scoped request options exposed by\n * `createModuleWebTransports()`.\n */\nexport interface ModuleWebRequestOptions {\n body?: unknown;\n headers?: HeadersInit;\n query?: Record<string, boolean | number | string | null | undefined>;\n signal?: AbortSignal;\n}\n\n/**\n * Configuration for the v0.1.0 compatibility transport.\n *\n * `appId` remains accepted for source compatibility but is informational.\n * It is never converted into a trusted `X-MS-App-ID` browser header.\n *\n * @deprecated Prefer {@link CreateModuleWebTransportsOptions}.\n */\nexport interface CreateModuleWebTransportOptions {\n moduleRef: string;\n apiBase?: string;\n appId?: string;\n fetch?: PlatformFetch;\n}\n\n/**\n * Direct v0.1.0 module transport for public-root and platform-scoped routes.\n *\n * @deprecated Prefer {@link ModuleWebTransports}.\n */\nexport interface ModuleWebTransport {\n /** Sends a request and parses a successful JSON body. */\n request<T>(method: string, route: string, options?: ModuleWebRequestOptions): Promise<T>;\n\n /** Sends a request and returns its successful body as text. */\n text(method: string, route: string, options?: ModuleWebRequestOptions): Promise<string>;\n}\n\ntype RequestInvoker = (\n method: string,\n path: string,\n options?: Record<string, unknown>,\n) => Promise<unknown>;\n\nfunction routeTarget(\n route: string,\n transports: ModuleWebTransports,\n) {\n if (route === \"/platform\") {\n return { path: \"/\", transport: transports.platform };\n }\n if (route.startsWith(\"/platform/\")) {\n return { path: route.slice(\"/platform\".length), transport: transports.platform };\n }\n return { path: route, transport: transports.public };\n}\n\n/**\n * Creates the direct route-dispatching transport released in v0.1.0.\n *\n * @deprecated Prefer {@link createModuleWebTransports}, which keeps public and\n * platform routes structurally separate and exposes the full scoped contract.\n */\nexport function createModuleWebTransport(\n options: CreateModuleWebTransportOptions,\n): ModuleWebTransport {\n // This transport is documented as serving \"public-root and platform-scoped\n // routes\": its callers pass whole paths, including the /public segment and\n // root-level routes. It therefore keeps addressing the module ROOT, and does\n // not inherit the /public segment createModuleWebTransports now owns.\n const transports = composeWebTransports(\n {\n moduleRef: options.moduleRef,\n ...(options.apiBase === undefined ? {} : { apiBase: options.apiBase }),\n ...(options.fetch === undefined ? {} : { fetch: options.fetch }),\n },\n { public: \"\", platform: \"platform\" },\n );\n\n const invoke = async <T>(\n method: string,\n route: string,\n requestOptions: ModuleWebRequestOptions | undefined,\n responseType: \"json\" | \"text\",\n ): Promise<T> => {\n const target = routeTarget(route, transports);\n const request = target.transport.request as unknown as RequestInvoker;\n return request(method, target.path, {\n ...requestOptions,\n responseType,\n }) as Promise<T>;\n };\n\n return Object.freeze({\n request: <T>(\n method: string,\n route: string,\n requestOptions?: ModuleWebRequestOptions,\n ) => invoke<T>(method, route, requestOptions, \"json\"),\n text: (\n method: string,\n route: string,\n requestOptions?: ModuleWebRequestOptions,\n ) => invoke<string>(method, route, requestOptions, \"text\"),\n });\n}\n"]}
@@ -0,0 +1,22 @@
1
+ import type { ModuleSubpath, SubpathCrumb } from "./types.js";
2
+ /** Mount-local observable state synchronized with the platform subpath bridge. */
3
+ export interface ModuleSubpathStore {
4
+ /** Returns the stable current segment snapshot. */
5
+ getSnapshot(): readonly string[];
6
+ /** Subscribes to snapshot changes. */
7
+ subscribe(listener: () => void): () => void;
8
+ /** Publishes breadcrumbs to the host and updates the local snapshot. */
9
+ publish(crumbs: readonly SubpathCrumb[], opts?: {
10
+ replace?: boolean;
11
+ }): void;
12
+ /** Releases the host subscription and local listeners. */
13
+ dispose(): void;
14
+ }
15
+ /**
16
+ * Creates subpath state isolated to one mounted module surface.
17
+ *
18
+ * Publishing updates local state even when the host does not echo its own
19
+ * navigation event. Without a host bridge, the store remains useful as an
20
+ * in-memory navigation source.
21
+ */
22
+ export declare function createModuleSubpathStore(bridge?: ModuleSubpath): ModuleSubpathStore;
@@ -0,0 +1,50 @@
1
+ function snapshot(segments) {
2
+ return Object.freeze([...segments]);
3
+ }
4
+ function equal(first, second) {
5
+ return first.length === second.length
6
+ && first.every((segment, index) => segment === second[index]);
7
+ }
8
+ /**
9
+ * Creates subpath state isolated to one mounted module surface.
10
+ *
11
+ * Publishing updates local state even when the host does not echo its own
12
+ * navigation event. Without a host bridge, the store remains useful as an
13
+ * in-memory navigation source.
14
+ */
15
+ export function createModuleSubpathStore(bridge) {
16
+ let current = snapshot(bridge?.get() ?? []);
17
+ let disposed = false;
18
+ const listeners = new Set();
19
+ const update = (segments) => {
20
+ if (disposed || equal(current, segments))
21
+ return;
22
+ current = snapshot(segments);
23
+ for (const listener of listeners)
24
+ listener();
25
+ };
26
+ const unsubscribe = bridge?.subscribe(update);
27
+ return Object.freeze({
28
+ getSnapshot: () => current,
29
+ subscribe(listener) {
30
+ if (disposed)
31
+ return () => { };
32
+ listeners.add(listener);
33
+ return () => listeners.delete(listener);
34
+ },
35
+ publish(crumbs, opts) {
36
+ if (disposed)
37
+ return;
38
+ bridge?.set([...crumbs], opts);
39
+ update(crumbs.map(({ segment }) => segment));
40
+ },
41
+ dispose() {
42
+ if (disposed)
43
+ return;
44
+ disposed = true;
45
+ unsubscribe?.();
46
+ listeners.clear();
47
+ },
48
+ });
49
+ }
50
+ //# sourceMappingURL=subpath.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subpath.js","sourceRoot":"","sources":["../../src/web/subpath.ts"],"names":[],"mappings":"AAcA,SAAS,QAAQ,CAAC,QAA2B;IAC3C,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC;AACtC,CAAC;AAED,SAAS,KAAK,CAAC,KAAwB,EAAE,MAAyB;IAChE,OAAO,KAAK,CAAC,MAAM,KAAK,MAAM,CAAC,MAAM;WAChC,KAAK,CAAC,KAAK,CAAC,CAAC,OAAO,EAAE,KAAK,EAAE,EAAE,CAAC,OAAO,KAAK,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AAClE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,wBAAwB,CACtC,MAAsB;IAEtB,IAAI,OAAO,GAAG,QAAQ,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,IAAI,QAAQ,GAAG,KAAK,CAAC;IACrB,MAAM,SAAS,GAAG,IAAI,GAAG,EAAc,CAAC;IAExC,MAAM,MAAM,GAAG,CAAC,QAA2B,EAAE,EAAE;QAC7C,IAAI,QAAQ,IAAI,KAAK,CAAC,OAAO,EAAE,QAAQ,CAAC;YAAE,OAAO;QACjD,OAAO,GAAG,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAC7B,KAAK,MAAM,QAAQ,IAAI,SAAS;YAAE,QAAQ,EAAE,CAAC;IAC/C,CAAC,CAAC;IACF,MAAM,WAAW,GAAG,MAAM,EAAE,SAAS,CAAC,MAAM,CAAC,CAAC;IAE9C,OAAO,MAAM,CAAC,MAAM,CAAC;QACnB,WAAW,EAAE,GAAG,EAAE,CAAC,OAAO;QAC1B,SAAS,CAAC,QAAoB;YAC5B,IAAI,QAAQ;gBAAE,OAAO,GAAG,EAAE,GAAE,CAAC,CAAC;YAC9B,SAAS,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACxB,OAAO,GAAG,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;QAC1C,CAAC;QACD,OAAO,CAAC,MAA+B,EAAE,IAA4B;YACnE,IAAI,QAAQ;gBAAE,OAAO;YACrB,MAAM,EAAE,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,EAAE,IAAI,CAAC,CAAC;YAC/B,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;QAC/C,CAAC;QACD,OAAO;YACL,IAAI,QAAQ;gBAAE,OAAO;YACrB,QAAQ,GAAG,IAAI,CAAC;YAChB,WAAW,EAAE,EAAE,CAAC;YAChB,SAAS,CAAC,KAAK,EAAE,CAAC;QACpB,CAAC;KACF,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type { ModuleSubpath, SubpathCrumb } from \"./types.js\";\n\n/** Mount-local observable state synchronized with the platform subpath bridge. */\nexport interface ModuleSubpathStore {\n /** Returns the stable current segment snapshot. */\n getSnapshot(): readonly string[];\n /** Subscribes to snapshot changes. */\n subscribe(listener: () => void): () => void;\n /** Publishes breadcrumbs to the host and updates the local snapshot. */\n publish(crumbs: readonly SubpathCrumb[], opts?: { replace?: boolean }): void;\n /** Releases the host subscription and local listeners. */\n dispose(): void;\n}\n\nfunction snapshot(segments: readonly string[]): readonly string[] {\n return Object.freeze([...segments]);\n}\n\nfunction equal(first: readonly string[], second: readonly string[]): boolean {\n return first.length === second.length\n && first.every((segment, index) => segment === second[index]);\n}\n\n/**\n * Creates subpath state isolated to one mounted module surface.\n *\n * Publishing updates local state even when the host does not echo its own\n * navigation event. Without a host bridge, the store remains useful as an\n * in-memory navigation source.\n */\nexport function createModuleSubpathStore(\n bridge?: ModuleSubpath,\n): ModuleSubpathStore {\n let current = snapshot(bridge?.get() ?? []);\n let disposed = false;\n const listeners = new Set<() => void>();\n\n const update = (segments: readonly string[]) => {\n if (disposed || equal(current, segments)) return;\n current = snapshot(segments);\n for (const listener of listeners) listener();\n };\n const unsubscribe = bridge?.subscribe(update);\n\n return Object.freeze({\n getSnapshot: () => current,\n subscribe(listener: () => void) {\n if (disposed) return () => {};\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n publish(crumbs: readonly SubpathCrumb[], opts?: { replace?: boolean }) {\n if (disposed) return;\n bridge?.set([...crumbs], opts);\n update(crumbs.map(({ segment }) => segment));\n },\n dispose() {\n if (disposed) return;\n disposed = true;\n unsubscribe?.();\n listeners.clear();\n },\n });\n}\n"]}
@@ -0,0 +1,129 @@
1
+ /** Fetch implementation supplied by the authenticated platform host. */
2
+ export type PlatformFetch = (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
3
+ /** Resolves platform actor identifiers for audit and attribution UI. */
4
+ export interface PlatformIdentity<TIdentity = unknown> {
5
+ resolve: (ids: string[]) => Promise<Record<string, TIdentity>>;
6
+ }
7
+ /** Navigation bridge supplied by the platform shell. */
8
+ export interface PlatformNavigate {
9
+ settings: () => void;
10
+ /** Platform-resolved settings slot: this module first, followed by installed
11
+ * settings-capable modules that contribute to it. Optional for old hosts. */
12
+ settingsItems?: PlatformSettingsItem[];
13
+ openSettings?: (moduleSlug: string) => void;
14
+ /** Opens one user in the platform-owned User Core detail surface. */
15
+ userDetail?: (userId: string) => void;
16
+ }
17
+ /** Settings destination accepted by the platform navigation bridge. */
18
+ export interface PlatformSettingsItem {
19
+ moduleSlug: string;
20
+ label: string;
21
+ icon: string;
22
+ }
23
+ /** State rendered by the platform unsaved-changes bar. */
24
+ export interface UnsavedBarState {
25
+ message: string;
26
+ saveLabel: string;
27
+ resetLabel: string;
28
+ /** Disables the save action while retaining reset and warning behavior. */
29
+ canSave?: boolean;
30
+ onSave: () => void;
31
+ onReset: () => void;
32
+ }
33
+ /** Bridge used by a mounted module to control unsaved-change UI. */
34
+ export interface PlatformUnsaved {
35
+ set: (state: UnsavedBarState | null) => void;
36
+ }
37
+ /** Event emitted from one mounted module component to another. */
38
+ export interface PlatformModuleComponentEvent {
39
+ type: string;
40
+ payload?: unknown;
41
+ }
42
+ /** Request to mount one component owned by an installed module. */
43
+ export type PlatformModuleMountRequest = {
44
+ /** Manifest UI component name; the host resolves its declared bundle export. */
45
+ component: string;
46
+ target: HTMLElement;
47
+ props?: Record<string, unknown>;
48
+ onEvent?: (event: PlatformModuleComponentEvent) => void;
49
+ } & ({
50
+ /** Authoritative installed-module identity resolved by the platform. */
51
+ moduleId: string;
52
+ /** Optional display/routing hint; never authoritative when moduleId exists. */
53
+ moduleSlug?: string;
54
+ } | {
55
+ moduleId?: undefined;
56
+ /** @deprecated Supply the platform-resolved moduleId when the host supports it. */
57
+ moduleSlug: string;
58
+ });
59
+ /** Cross-module component mount bridge supplied by the platform. */
60
+ export interface PlatformModules {
61
+ mount: (request: PlatformModuleMountRequest) => Promise<() => void>;
62
+ }
63
+ /** Breadcrumb metadata for a module-owned subpath. */
64
+ export interface SubpathCrumb {
65
+ /** URL-safe route segment owned by the module. */
66
+ segment: string;
67
+ /** Human-readable label rendered by the platform breadcrumb. */
68
+ label: string;
69
+ }
70
+ /** Router state passed to a mounted module surface. */
71
+ export interface ModuleSubpath {
72
+ /** Returns the currently requested module-relative path segments. */
73
+ get: () => string[];
74
+ /** Publishes a new module-relative path and its breadcrumb labels. */
75
+ set: (crumbs: SubpathCrumb[], opts?: {
76
+ replace?: boolean;
77
+ }) => void;
78
+ /** Subscribes to navigation initiated by the platform. */
79
+ subscribe: (listener: (segments: string[]) => void) => () => void;
80
+ }
81
+ /** Complete framework-neutral contract supplied when a module web surface mounts. */
82
+ export interface ModuleMountContext<TIdentity = unknown> {
83
+ /** Prefix for this module's HTTP routes; empty means same-origin. */
84
+ apiBase?: string;
85
+ /** Authenticated host transport, including platform token refresh. */
86
+ fetch?: PlatformFetch;
87
+ /**
88
+ * Host-supplied application identifier for mount-local state and links.
89
+ * Informational in browser code; never trusted as request identity.
90
+ */
91
+ appId?: string;
92
+ /** Active BCP-47 locale, such as `en-US` or `zh-TW`. */
93
+ locale?: string;
94
+ /** Platform-owned cross-surface navigation helpers. */
95
+ navigate?: PlatformNavigate;
96
+ /** Bridge to the platform-owned unsaved-changes interface. */
97
+ unsaved?: PlatformUnsaved;
98
+ /** Bridge for mounting contributed components from installed modules. */
99
+ modules?: PlatformModules;
100
+ /** Platform-owned principal identity resolver. */
101
+ identity?: PlatformIdentity<TIdentity>;
102
+ /** Bridge for module-relative navigation and breadcrumbs. */
103
+ subpath?: ModuleSubpath;
104
+ }
105
+ /** Backward-compatible concise name for the module mount contract. */
106
+ export type MountContext<TIdentity = unknown> = ModuleMountContext<TIdentity>;
107
+ /** Props and lifecycle events supplied to a manifest-declared component. */
108
+ export interface ModuleComponentBridge<TProps extends object = Record<string, unknown>, TEventPayload = unknown> {
109
+ /** Props validated by the host against the component manifest. */
110
+ readonly props: TProps;
111
+ /** Emits a component lifecycle event to the surface that requested it. */
112
+ emit: (type: string, payload?: TEventPayload) => void;
113
+ }
114
+ /**
115
+ * Host context for one manifest-declared component mount.
116
+ *
117
+ * Component mounts always have resolved installation, transport, locale, and
118
+ * settings navigation context. Optional page bridges remain inherited from
119
+ * ModuleMountContext so a host can add them without changing this contract.
120
+ */
121
+ export interface ModuleComponentMountContext<TProps extends object = Record<string, unknown>, TEventPayload = unknown, TIdentity = unknown> extends ModuleMountContext<TIdentity> {
122
+ apiBase: string;
123
+ fetch: PlatformFetch;
124
+ /** Informational host application value; never trusted request identity. */
125
+ appId: string;
126
+ locale: string;
127
+ navigate: Pick<PlatformNavigate, "settings">;
128
+ component: ModuleComponentBridge<TProps, TEventPayload>;
129
+ }
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/web/types.ts"],"names":[],"mappings":"","sourcesContent":["/** Fetch implementation supplied by the authenticated platform host. */\nexport type PlatformFetch = (\n input: RequestInfo | URL,\n init?: RequestInit,\n) => Promise<Response>;\n\n/** Resolves platform actor identifiers for audit and attribution UI. */\nexport interface PlatformIdentity<TIdentity = unknown> {\n resolve: (ids: string[]) => Promise<Record<string, TIdentity>>;\n}\n\n/** Navigation bridge supplied by the platform shell. */\nexport interface PlatformNavigate {\n settings: () => void;\n /** Platform-resolved settings slot: this module first, followed by installed\n * settings-capable modules that contribute to it. Optional for old hosts. */\n settingsItems?: PlatformSettingsItem[];\n openSettings?: (moduleSlug: string) => void;\n /** Opens one user in the platform-owned User Core detail surface. */\n userDetail?: (userId: string) => void;\n}\n\n/** Settings destination accepted by the platform navigation bridge. */\nexport interface PlatformSettingsItem {\n moduleSlug: string;\n label: string;\n icon: string;\n}\n\n/** State rendered by the platform unsaved-changes bar. */\nexport interface UnsavedBarState {\n message: string;\n saveLabel: string;\n resetLabel: string;\n /** Disables the save action while retaining reset and warning behavior. */\n canSave?: boolean;\n onSave: () => void;\n onReset: () => void;\n}\n\n/** Bridge used by a mounted module to control unsaved-change UI. */\nexport interface PlatformUnsaved {\n set: (state: UnsavedBarState | null) => void;\n}\n\n/** Event emitted from one mounted module component to another. */\nexport interface PlatformModuleComponentEvent {\n type: string;\n payload?: unknown;\n}\n\n/** Request to mount one component owned by an installed module. */\nexport type PlatformModuleMountRequest = {\n /** Manifest UI component name; the host resolves its declared bundle export. */\n component: string;\n target: HTMLElement;\n props?: Record<string, unknown>;\n onEvent?: (event: PlatformModuleComponentEvent) => void;\n} & (\n | {\n /** Authoritative installed-module identity resolved by the platform. */\n moduleId: string;\n /** Optional display/routing hint; never authoritative when moduleId exists. */\n moduleSlug?: string;\n }\n | {\n moduleId?: undefined;\n /** @deprecated Supply the platform-resolved moduleId when the host supports it. */\n moduleSlug: string;\n }\n);\n\n/** Cross-module component mount bridge supplied by the platform. */\nexport interface PlatformModules {\n mount: (request: PlatformModuleMountRequest) => Promise<() => void>;\n}\n\n/** Breadcrumb metadata for a module-owned subpath. */\nexport interface SubpathCrumb {\n /** URL-safe route segment owned by the module. */\n segment: string;\n /** Human-readable label rendered by the platform breadcrumb. */\n label: string;\n}\n\n/** Router state passed to a mounted module surface. */\nexport interface ModuleSubpath {\n /** Returns the currently requested module-relative path segments. */\n get: () => string[];\n /** Publishes a new module-relative path and its breadcrumb labels. */\n set: (crumbs: SubpathCrumb[], opts?: { replace?: boolean }) => void;\n /** Subscribes to navigation initiated by the platform. */\n subscribe: (listener: (segments: string[]) => void) => () => void;\n}\n\n/** Complete framework-neutral contract supplied when a module web surface mounts. */\nexport interface ModuleMountContext<TIdentity = unknown> {\n /** Prefix for this module's HTTP routes; empty means same-origin. */\n apiBase?: string;\n /** Authenticated host transport, including platform token refresh. */\n fetch?: PlatformFetch;\n /**\n * Host-supplied application identifier for mount-local state and links.\n * Informational in browser code; never trusted as request identity.\n */\n appId?: string;\n /** Active BCP-47 locale, such as `en-US` or `zh-TW`. */\n locale?: string;\n /** Platform-owned cross-surface navigation helpers. */\n navigate?: PlatformNavigate;\n /** Bridge to the platform-owned unsaved-changes interface. */\n unsaved?: PlatformUnsaved;\n /** Bridge for mounting contributed components from installed modules. */\n modules?: PlatformModules;\n /** Platform-owned principal identity resolver. */\n identity?: PlatformIdentity<TIdentity>;\n /** Bridge for module-relative navigation and breadcrumbs. */\n subpath?: ModuleSubpath;\n}\n\n/** Backward-compatible concise name for the module mount contract. */\nexport type MountContext<TIdentity = unknown> = ModuleMountContext<TIdentity>;\n\n/** Props and lifecycle events supplied to a manifest-declared component. */\nexport interface ModuleComponentBridge<\n TProps extends object = Record<string, unknown>,\n TEventPayload = unknown,\n> {\n /** Props validated by the host against the component manifest. */\n readonly props: TProps;\n /** Emits a component lifecycle event to the surface that requested it. */\n emit: (type: string, payload?: TEventPayload) => void;\n}\n\n/**\n * Host context for one manifest-declared component mount.\n *\n * Component mounts always have resolved installation, transport, locale, and\n * settings navigation context. Optional page bridges remain inherited from\n * ModuleMountContext so a host can add them without changing this contract.\n */\nexport interface ModuleComponentMountContext<\n TProps extends object = Record<string, unknown>,\n TEventPayload = unknown,\n TIdentity = unknown,\n> extends ModuleMountContext<TIdentity> {\n apiBase: string;\n fetch: PlatformFetch;\n /** Informational host application value; never trusted request identity. */\n appId: string;\n locale: string;\n navigate: Pick<PlatformNavigate, \"settings\">;\n component: ModuleComponentBridge<TProps, TEventPayload>;\n}\n"]}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Returns wall-clock time refreshed on a configurable, document-independent cadence.
3
+ *
4
+ * The default is suitable for relative timestamps and expiry labels that do
5
+ * not need second-level precision.
6
+ */
7
+ export declare function useNow(intervalMs?: number): number;
@@ -0,0 +1,22 @@
1
+ import { useEffect, useState } from "react";
2
+ const MAX_TIMER_DELAY_MS = 2_147_483_647;
3
+ /**
4
+ * Returns wall-clock time refreshed on a configurable, document-independent cadence.
5
+ *
6
+ * The default is suitable for relative timestamps and expiry labels that do
7
+ * not need second-level precision.
8
+ */
9
+ export function useNow(intervalMs = 30_000) {
10
+ if (!Number.isSafeInteger(intervalMs)
11
+ || intervalMs <= 0
12
+ || intervalMs > MAX_TIMER_DELAY_MS) {
13
+ throw new TypeError(`useNow intervalMs must be an integer from 1 to ${MAX_TIMER_DELAY_MS}`);
14
+ }
15
+ const [now, setNow] = useState(Date.now);
16
+ useEffect(() => {
17
+ const timer = globalThis.setInterval(() => setNow(Date.now()), intervalMs);
18
+ return () => globalThis.clearInterval(timer);
19
+ }, [intervalMs]);
20
+ return now;
21
+ }
22
+ //# sourceMappingURL=use-now.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-now.js","sourceRoot":"","sources":["../../src/web/use-now.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAE5C,MAAM,kBAAkB,GAAG,aAAa,CAAC;AAEzC;;;;;GAKG;AACH,MAAM,UAAU,MAAM,CAAC,UAAU,GAAG,MAAM;IACxC,IACE,CAAC,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC;WAC9B,UAAU,IAAI,CAAC;WACf,UAAU,GAAG,kBAAkB,EAClC,CAAC;QACD,MAAM,IAAI,SAAS,CACjB,kDAAkD,kBAAkB,EAAE,CACvE,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACzC,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,KAAK,GAAG,UAAU,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,EAAE,UAAU,CAAC,CAAC;QAC3E,OAAO,GAAG,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;IAC/C,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IACjB,OAAO,GAAG,CAAC;AACb,CAAC","sourcesContent":["import { useEffect, useState } from \"react\";\n\nconst MAX_TIMER_DELAY_MS = 2_147_483_647;\n\n/**\n * Returns wall-clock time refreshed on a configurable, document-independent cadence.\n *\n * The default is suitable for relative timestamps and expiry labels that do\n * not need second-level precision.\n */\nexport function useNow(intervalMs = 30_000): number {\n if (\n !Number.isSafeInteger(intervalMs)\n || intervalMs <= 0\n || intervalMs > MAX_TIMER_DELAY_MS\n ) {\n throw new TypeError(\n `useNow intervalMs must be an integer from 1 to ${MAX_TIMER_DELAY_MS}`,\n );\n }\n\n const [now, setNow] = useState(Date.now);\n useEffect(() => {\n const timer = globalThis.setInterval(() => setNow(Date.now()), intervalMs);\n return () => globalThis.clearInterval(timer);\n }, [intervalMs]);\n return now;\n}\n"]}
@@ -0,0 +1,10 @@
1
+ import type { PlatformUnsaved, UnsavedBarState } from "./types.js";
2
+ /**
3
+ * Synchronizes one React surface with its platform-owned unsaved-state bridge.
4
+ *
5
+ * State updates replace the current value directly. The hook sends `null` only
6
+ * when the caller supplies it, the bridge changes, or the final owner unmounts.
7
+ * Cleanup is deferred by one microtask so React Strict Mode's effect replay
8
+ * cannot emit a false transient clear between identical registrations.
9
+ */
10
+ export declare function usePlatformUnsavedState(bridge: PlatformUnsaved | undefined, state: UnsavedBarState | null): void;
@@ -0,0 +1,35 @@
1
+ import { useEffect } from "react";
2
+ const latestRegistration = new WeakMap();
3
+ /**
4
+ * Synchronizes one React surface with its platform-owned unsaved-state bridge.
5
+ *
6
+ * State updates replace the current value directly. The hook sends `null` only
7
+ * when the caller supplies it, the bridge changes, or the final owner unmounts.
8
+ * Cleanup is deferred by one microtask so React Strict Mode's effect replay
9
+ * cannot emit a false transient clear between identical registrations.
10
+ */
11
+ export function usePlatformUnsavedState(bridge, state) {
12
+ useEffect(() => {
13
+ if (bridge === undefined)
14
+ return;
15
+ const registration = Symbol("platform-unsaved-state");
16
+ latestRegistration.set(bridge, registration);
17
+ return () => {
18
+ queueMicrotask(() => {
19
+ if (latestRegistration.get(bridge) !== registration)
20
+ return;
21
+ latestRegistration.delete(bridge);
22
+ try {
23
+ bridge.set(null);
24
+ }
25
+ catch {
26
+ // A cleanup failure cannot be reported safely from a deferred effect.
27
+ }
28
+ });
29
+ };
30
+ }, [bridge]);
31
+ useEffect(() => {
32
+ bridge?.set(state);
33
+ }, [bridge, state]);
34
+ }
35
+ //# sourceMappingURL=use-platform-unsaved-state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-platform-unsaved-state.js","sourceRoot":"","sources":["../../src/web/use-platform-unsaved-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIlC,MAAM,kBAAkB,GAAG,IAAI,OAAO,EAA2B,CAAC;AAElE;;;;;;;GAOG;AACH,MAAM,UAAU,uBAAuB,CACrC,MAAmC,EACnC,KAA6B;IAE7B,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO;QACjC,MAAM,YAAY,GAAG,MAAM,CAAC,wBAAwB,CAAC,CAAC;QACtD,kBAAkB,CAAC,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QAE7C,OAAO,GAAG,EAAE;YACV,cAAc,CAAC,GAAG,EAAE;gBAClB,IAAI,kBAAkB,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,YAAY;oBAAE,OAAO;gBAC5D,kBAAkB,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;gBAClC,IAAI,CAAC;oBACH,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;gBACnB,CAAC;gBAAC,MAAM,CAAC;oBACP,sEAAsE;gBACxE,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAEb,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,EAAE,GAAG,CAAC,KAAK,CAAC,CAAC;IACrB,CAAC,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;AACtB,CAAC","sourcesContent":["import { useEffect } from \"react\";\n\nimport type { PlatformUnsaved, UnsavedBarState } from \"./types.js\";\n\nconst latestRegistration = new WeakMap<PlatformUnsaved, symbol>();\n\n/**\n * Synchronizes one React surface with its platform-owned unsaved-state bridge.\n *\n * State updates replace the current value directly. The hook sends `null` only\n * when the caller supplies it, the bridge changes, or the final owner unmounts.\n * Cleanup is deferred by one microtask so React Strict Mode's effect replay\n * cannot emit a false transient clear between identical registrations.\n */\nexport function usePlatformUnsavedState(\n bridge: PlatformUnsaved | undefined,\n state: UnsavedBarState | null,\n): void {\n useEffect(() => {\n if (bridge === undefined) return;\n const registration = Symbol(\"platform-unsaved-state\");\n latestRegistration.set(bridge, registration);\n\n return () => {\n queueMicrotask(() => {\n if (latestRegistration.get(bridge) !== registration) return;\n latestRegistration.delete(bridge);\n try {\n bridge.set(null);\n } catch {\n // A cleanup failure cannot be reported safely from a deferred effect.\n }\n });\n };\n }, [bridge]);\n\n useEffect(() => {\n bridge?.set(state);\n }, [bridge, state]);\n}\n"]}
package/package.json ADDED
@@ -0,0 +1,88 @@
1
+ {
2
+ "name": "@mirrorstack-ai/app-module-client",
3
+ "version": "0.5.1",
4
+ "description": "Framework-neutral client composition for MirrorStack application modules",
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "sideEffects": false,
8
+ "engines": {
9
+ "node": ">=20",
10
+ "pnpm": ">=10.29.3 <11"
11
+ },
12
+ "repository": {
13
+ "type": "git",
14
+ "url": "git+https://github.com/mirrorstack-ai/app-module-client.git"
15
+ },
16
+ "bugs": {
17
+ "url": "https://github.com/mirrorstack-ai/app-module-client/issues"
18
+ },
19
+ "homepage": "https://github.com/mirrorstack-ai/app-module-client#readme",
20
+ "main": "./dist/index.js",
21
+ "types": "./dist/index.d.ts",
22
+ "exports": {
23
+ ".": {
24
+ "types": "./dist/index.d.ts",
25
+ "import": "./dist/index.js",
26
+ "default": "./dist/index.js"
27
+ },
28
+ "./web": {
29
+ "types": "./dist/web/index.d.ts",
30
+ "import": "./dist/web/index.js"
31
+ },
32
+ "./web/react": {
33
+ "types": "./dist/web/react.d.ts",
34
+ "import": "./dist/web/react.js"
35
+ },
36
+ "./server": {
37
+ "types": "./dist/server/index.d.ts",
38
+ "import": "./dist/server/index.js"
39
+ },
40
+ "./next": {
41
+ "types": "./dist/next/index.d.ts",
42
+ "import": "./dist/next/index.js"
43
+ }
44
+ },
45
+ "files": [
46
+ "dist",
47
+ "README.md",
48
+ "CHANGELOG.md"
49
+ ],
50
+ "publishConfig": {
51
+ "registry": "https://registry.npmjs.org/",
52
+ "access": "public"
53
+ },
54
+ "devDependencies": {
55
+ "@types/node": "^22.20.1",
56
+ "@types/react": "^19.0.0",
57
+ "@types/react-dom": "^19.0.0",
58
+ "next": "^16.3.4",
59
+ "react": "^19.0.0",
60
+ "react-dom": "^19.0.0",
61
+ "typescript": "^7.0.2",
62
+ "vitest": "^4.1.11"
63
+ },
64
+ "peerDependencies": {
65
+ "next": ">=15.0.0",
66
+ "react": "^18.2.0 || ^19.0.0",
67
+ "react-dom": "^18.2.0 || ^19.0.0"
68
+ },
69
+ "peerDependenciesMeta": {
70
+ "react": {
71
+ "optional": true
72
+ },
73
+ "react-dom": {
74
+ "optional": true
75
+ },
76
+ "next": {
77
+ "optional": true
78
+ }
79
+ },
80
+ "scripts": {
81
+ "clean": "node --eval \"require('node:fs').rmSync('dist', { recursive: true, force: true })\"",
82
+ "build": "pnpm clean && tsc -p tsconfig.build.json",
83
+ "typecheck": "tsc -p tsconfig.json --noEmit",
84
+ "test": "vitest run",
85
+ "test:watch": "vitest",
86
+ "pack:check": "pnpm pack --dry-run"
87
+ }
88
+ }