@immediately-run/sdk 0.43.1 → 0.45.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/ambient.d.ts +47 -0
- package/dist/auth.cjs +3 -2
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.js +3 -2
- package/dist/auth.js.map +1 -1
- package/dist/boot.cjs +3 -2
- package/dist/boot.cjs.map +1 -1
- package/dist/boot.js +3 -2
- package/dist/boot.js.map +1 -1
- package/dist/catalog.cjs +3 -2
- package/dist/catalog.cjs.map +1 -1
- package/dist/catalog.js +3 -2
- package/dist/catalog.js.map +1 -1
- package/dist/components/MDXComponents.cjs +14 -1
- package/dist/components/MDXComponents.cjs.map +1 -1
- package/dist/components/MDXComponents.js +14 -1
- package/dist/components/MDXComponents.js.map +1 -1
- package/dist/components/WikiLink.cjs +19 -17
- package/dist/components/WikiLink.cjs.map +1 -1
- package/dist/components/WikiLink.js +19 -17
- package/dist/components/WikiLink.js.map +1 -1
- package/dist/contribute.cjs +2 -1
- package/dist/contribute.cjs.map +1 -1
- package/dist/contribute.js +2 -1
- package/dist/contribute.js.map +1 -1
- package/dist/debug.cjs +6 -5
- package/dist/debug.cjs.map +1 -1
- package/dist/debug.js +12 -5
- package/dist/debug.js.map +1 -1
- package/dist/diagnostics.cjs +3 -2
- package/dist/diagnostics.cjs.map +1 -1
- package/dist/diagnostics.js +3 -2
- package/dist/diagnostics.js.map +1 -1
- package/dist/dnd.cjs +5 -3
- package/dist/dnd.cjs.map +1 -1
- package/dist/dnd.js +5 -3
- package/dist/dnd.js.map +1 -1
- package/dist/editor.cjs +3 -1
- package/dist/editor.cjs.map +1 -1
- package/dist/editor.js +3 -1
- package/dist/editor.js.map +1 -1
- package/dist/editorContext.cjs +3 -2
- package/dist/editorContext.cjs.map +1 -1
- package/dist/editorContext.js +3 -2
- package/dist/editorContext.js.map +1 -1
- package/dist/formFactor.cjs +3 -2
- package/dist/formFactor.cjs.map +1 -1
- package/dist/formFactor.js +3 -2
- package/dist/formFactor.js.map +1 -1
- package/dist/generated/protocol.cjs +23 -0
- package/dist/generated/protocol.cjs.map +1 -0
- package/dist/generated/protocol.d.cts +1 -0
- package/dist/generated/protocol.d.ts +1 -0
- package/dist/generated/protocol.js +2 -0
- package/dist/generated/protocol.js.map +1 -0
- package/dist/hooks.cjs +22 -14
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.cts +26 -4
- package/dist/hooks.d.ts +26 -4
- package/dist/hooks.js +23 -15
- package/dist/hooks.js.map +1 -1
- package/dist/index.cjs +4 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/ipc.cjs +5 -3
- package/dist/ipc.cjs.map +1 -1
- package/dist/ipc.js +5 -3
- package/dist/ipc.js.map +1 -1
- package/dist/launch.cjs +5 -3
- package/dist/launch.cjs.map +1 -1
- package/dist/launch.js +5 -3
- package/dist/launch.js.map +1 -1
- package/dist/linkSpace.cjs +63 -0
- package/dist/linkSpace.cjs.map +1 -0
- package/dist/linkSpace.d.cts +44 -0
- package/dist/linkSpace.d.ts +44 -0
- package/dist/linkSpace.js +37 -0
- package/dist/linkSpace.js.map +1 -0
- package/dist/llm.cjs +3 -2
- package/dist/llm.cjs.map +1 -1
- package/dist/llm.js +3 -2
- package/dist/llm.js.map +1 -1
- package/dist/metadataSource.cjs +53 -0
- package/dist/metadataSource.cjs.map +1 -0
- package/dist/metadataSource.d.cts +51 -0
- package/dist/metadataSource.d.ts +51 -0
- package/dist/metadataSource.js +29 -0
- package/dist/metadataSource.js.map +1 -0
- package/dist/moduleCache.cjs +2 -1
- package/dist/moduleCache.cjs.map +1 -1
- package/dist/moduleCache.js +2 -1
- package/dist/moduleCache.js.map +1 -1
- package/dist/mounts.cjs +13 -10
- package/dist/mounts.cjs.map +1 -1
- package/dist/mounts.js +23 -10
- package/dist/mounts.js.map +1 -1
- package/dist/netFetch.cjs +4 -2
- package/dist/netFetch.cjs.map +1 -1
- package/dist/netFetch.js +4 -2
- package/dist/netFetch.js.map +1 -1
- package/dist/onFsChange.cjs +2 -1
- package/dist/onFsChange.cjs.map +1 -1
- package/dist/onFsChange.js +2 -1
- package/dist/onFsChange.js.map +1 -1
- package/dist/protocolSchemes.cjs +46 -0
- package/dist/protocolSchemes.cjs.map +1 -0
- package/dist/protocolSchemes.d.cts +18 -0
- package/dist/protocolSchemes.d.ts +18 -0
- package/dist/protocolSchemes.js +37 -0
- package/dist/protocolSchemes.js.map +1 -0
- package/dist/routing.cjs +2 -1
- package/dist/routing.cjs.map +1 -1
- package/dist/routing.js +2 -1
- package/dist/routing.js.map +1 -1
- package/dist/runtime.cjs +4 -2
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +4 -2
- package/dist/runtime.js.map +1 -1
- package/dist/safeContent/renderMdast.cjs +2 -0
- package/dist/safeContent/renderMdast.cjs.map +1 -1
- package/dist/safeContent/renderMdast.js +2 -0
- package/dist/safeContent/renderMdast.js.map +1 -1
- package/dist/sandboxTypes.cjs.map +1 -1
- package/dist/sandboxTypes.d.cts +40 -6
- package/dist/sandboxTypes.d.ts +40 -6
- package/dist/secrets.cjs +5 -3
- package/dist/secrets.cjs.map +1 -1
- package/dist/secrets.js +5 -3
- package/dist/secrets.js.map +1 -1
- package/dist/tasks.cjs +6 -4
- package/dist/tasks.cjs.map +1 -1
- package/dist/tasks.js +6 -4
- package/dist/tasks.js.map +1 -1
- package/dist/theme.cjs +5 -3
- package/dist/theme.cjs.map +1 -1
- package/dist/theme.js +5 -3
- package/dist/theme.js.map +1 -1
- package/dist/urlUtils.cjs +3 -4
- package/dist/urlUtils.cjs.map +1 -1
- package/dist/urlUtils.d.cts +3 -10
- package/dist/urlUtils.d.ts +3 -10
- package/dist/urlUtils.js +1 -2
- package/dist/urlUtils.js.map +1 -1
- package/dist/vcs.cjs +5 -3
- package/dist/vcs.cjs.map +1 -1
- package/dist/vcs.js +5 -3
- package/dist/vcs.js.map +1 -1
- package/dist/version.cjs +1 -1
- package/dist/version.cjs.map +1 -1
- package/dist/version.d.cts +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +11 -4
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// Ambient declarations for the globals the immediately.run SANDBOX provides to app
|
|
2
|
+
// code (PLATFORM_LAYERING_SPEC §4 / S3, R3-276).
|
|
3
|
+
//
|
|
4
|
+
// Activate them in a consuming app with a one-line reference in any `.d.ts` your
|
|
5
|
+
// tsconfig includes:
|
|
6
|
+
//
|
|
7
|
+
// /// <reference types="@immediately-run/sdk/ambient" />
|
|
8
|
+
//
|
|
9
|
+
// This is types-only: nothing here is imported at runtime, so referencing it does
|
|
10
|
+
// not pull the SDK's sandbox-adapter tier into the app's bundle graph.
|
|
11
|
+
//
|
|
12
|
+
// ── What is NOT here: the `fs` module ────────────────────────────────────────
|
|
13
|
+
// The async-only `fs` surface the sandbox exposes is declared by
|
|
14
|
+
// `@immediately-run/dev-fs/fs` (`/// <reference types="@immediately-run/dev-fs/fs" />`),
|
|
15
|
+
// the package that also bridges it to real disk under `vite dev`. It is not
|
|
16
|
+
// re-declared here: a second copy of a type declaration is the drift this whole
|
|
17
|
+
// project is removing, and the copy would be the one apps hit first. Moving that
|
|
18
|
+
// declaration's OWNERSHIP to the SDK (with dev-fs re-referencing it) is the right
|
|
19
|
+
// long-run shape and needs a change in that repo — filed as R3-276b.
|
|
20
|
+
//
|
|
21
|
+
// ── Host obligation: mount before boot ───────────────────────────────────────
|
|
22
|
+
// The corpus a viewer reads must be MOUNTED before the app boots. The SDK offers no
|
|
23
|
+
// "wait for the mount" affordance for it, and none of the types below imply one:
|
|
24
|
+
// app code that reads `fs` at module scope is entitled to assume the filesystem is
|
|
25
|
+
// already there, because the host guarantees the mount precedes evaluation. A host
|
|
26
|
+
// that boots an app first and mounts second is breaking the contract, not exposing
|
|
27
|
+
// a race the app should defend against.
|
|
28
|
+
|
|
29
|
+
import type { EvaluationContext } from './sandboxTypes';
|
|
30
|
+
|
|
31
|
+
declare global {
|
|
32
|
+
/**
|
|
33
|
+
* The module currently being evaluated — the sandbox's evaluation context for
|
|
34
|
+
* THIS file, injected as a global by the runtime.
|
|
35
|
+
*
|
|
36
|
+
* Its use in app code is as `<Include baseModule={module} …>`: the anchor a
|
|
37
|
+
* relative include resolves against, so an included file's own relative imports
|
|
38
|
+
* resolve from where it lives rather than from the app root.
|
|
39
|
+
*
|
|
40
|
+
* TypeScript otherwise types a bare `module` from `@types/node` (a CommonJS
|
|
41
|
+
* `NodeModule`, which this is not) or fails to resolve it at all — the reason app
|
|
42
|
+
* repos ended up asserting `module as any` at every call site.
|
|
43
|
+
*/
|
|
44
|
+
const module: EvaluationContext;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export {};
|
package/dist/auth.cjs
CHANGED
|
@@ -24,13 +24,14 @@ __export(auth_exports, {
|
|
|
24
24
|
});
|
|
25
25
|
module.exports = __toCommonJS(auth_exports);
|
|
26
26
|
var import_pushChannel = require("./pushChannel");
|
|
27
|
+
var import_protocol = require("./generated/protocol");
|
|
27
28
|
const isAuthState = (v) => {
|
|
28
29
|
const s = v;
|
|
29
30
|
return !!s && (s.status === "unknown" || s.status === "signed-in" || s.status === "signed-out") && (s.user === null || typeof s.user === "object" && typeof s.user.login === "string");
|
|
30
31
|
};
|
|
31
32
|
const channel = (0, import_pushChannel.createPushChannel)({
|
|
32
|
-
pushType:
|
|
33
|
-
requestType:
|
|
33
|
+
pushType: import_protocol.AUTH_STATE,
|
|
34
|
+
requestType: import_protocol.REQUEST_AUTH_STATE,
|
|
34
35
|
initial: { status: "unknown", user: null },
|
|
35
36
|
parse: (msg) => isAuthState(msg.state) ? msg.state : void 0
|
|
36
37
|
});
|
package/dist/auth.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/auth.ts"],"sourcesContent":["import { createPushChannel } from './pushChannel';\n\n/**\n * Login / account state of the immediately.run user, mirrored from the host\n * window into the sandbox.\n *\n * `status` is `'unknown'` until the host has reported a value (use it to\n * distinguish \"still loading\" from a confirmed signed-out session).\n */\nexport type AuthStatus = 'unknown' | 'signed-in' | 'signed-out';\n\n/** The signed-in immediately.run user, as seen by the sandbox (no token, ever). */\nexport interface SandboxUser {\n /** GitHub login (handle) of the signed-in user. */\n login: string;\n}\n\n/** The user's login / account state: a `status` plus the `user` when signed in. */\nexport interface AuthState {\n status: AuthStatus;\n user: SandboxUser | null;\n}\n\nconst isAuthState = (v: unknown): v is AuthState => {\n const s = v as Partial<AuthState> | null;\n return (\n !!s &&\n (s.status === 'unknown' || s.status === 'signed-in' || s.status === 'signed-out') &&\n (s.user === null || (typeof s.user === 'object' && typeof (s.user as SandboxUser).login === 'string'))\n );\n};\n\n// Read over the transport (SDK_PACKAGING_SPEC §4): the host pushes `auth-state`\n// and answers `request-auth-state` (wire format: site-main channelBridge.ts).\nconst channel = createPushChannel<AuthState>({\n pushType:
|
|
1
|
+
{"version":3,"sources":["../src/auth.ts"],"sourcesContent":["import { createPushChannel } from './pushChannel';\nimport { AUTH_STATE, REQUEST_AUTH_STATE } from './generated/protocol';\n\n/**\n * Login / account state of the immediately.run user, mirrored from the host\n * window into the sandbox.\n *\n * `status` is `'unknown'` until the host has reported a value (use it to\n * distinguish \"still loading\" from a confirmed signed-out session).\n */\nexport type AuthStatus = 'unknown' | 'signed-in' | 'signed-out';\n\n/** The signed-in immediately.run user, as seen by the sandbox (no token, ever). */\nexport interface SandboxUser {\n /** GitHub login (handle) of the signed-in user. */\n login: string;\n}\n\n/** The user's login / account state: a `status` plus the `user` when signed in. */\nexport interface AuthState {\n status: AuthStatus;\n user: SandboxUser | null;\n}\n\nconst isAuthState = (v: unknown): v is AuthState => {\n const s = v as Partial<AuthState> | null;\n return (\n !!s &&\n (s.status === 'unknown' || s.status === 'signed-in' || s.status === 'signed-out') &&\n (s.user === null || (typeof s.user === 'object' && typeof (s.user as SandboxUser).login === 'string'))\n );\n};\n\n// Read over the transport (SDK_PACKAGING_SPEC §4): the host pushes `auth-state`\n// and answers `request-auth-state` (wire format: site-main channelBridge.ts).\nconst channel = createPushChannel<AuthState>({\n pushType: AUTH_STATE,\n requestType: REQUEST_AUTH_STATE,\n initial: { status: 'unknown', user: null },\n parse: (msg) => (isAuthState(msg.state) ? (msg.state as AuthState) : undefined),\n});\n\n/**\n * Returns the current login / account state. Poll this whenever you need a\n * one-off read; use {@link onAuthChange} or {@link useAuth} to react to changes.\n */\nexport const getAuthState = (): AuthState => channel.get();\n\n/**\n * Subscribe to login / logout changes. The listener is invoked immediately with\n * the current state, then again on every change. Returns an unsubscribe fn.\n */\nexport const onAuthChange = (listener: (state: AuthState) => void): (() => void) =>\n channel.onChange(listener);\n\n/**\n * React hook returning the current login / account state, re-rendering on\n * login / logout.\n */\nexport const useAuth = (): AuthState => channel.use();\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yBAAkC;AAClC,sBAA+C;AAuB/C,MAAM,cAAc,CAAC,MAA+B;AAClD,QAAM,IAAI;AACV,SACE,CAAC,CAAC,MACD,EAAE,WAAW,aAAa,EAAE,WAAW,eAAe,EAAE,WAAW,kBACnE,EAAE,SAAS,QAAS,OAAO,EAAE,SAAS,YAAY,OAAQ,EAAE,KAAqB,UAAU;AAEhG;AAIA,MAAM,cAAU,sCAA6B;AAAA,EAC3C,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS,EAAE,QAAQ,WAAW,MAAM,KAAK;AAAA,EACzC,OAAO,CAAC,QAAS,YAAY,IAAI,KAAK,IAAK,IAAI,QAAsB;AACvE,CAAC;AAMM,MAAM,eAAe,MAAiB,QAAQ,IAAI;AAMlD,MAAM,eAAe,CAAC,aAC3B,QAAQ,SAAS,QAAQ;AAMpB,MAAM,UAAU,MAAiB,QAAQ,IAAI;","names":[]}
|
package/dist/auth.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import "./chunk-VHAA22YE.js";
|
|
2
2
|
import { createPushChannel } from "./pushChannel";
|
|
3
|
+
import { AUTH_STATE, REQUEST_AUTH_STATE } from "./generated/protocol";
|
|
3
4
|
const isAuthState = (v) => {
|
|
4
5
|
const s = v;
|
|
5
6
|
return !!s && (s.status === "unknown" || s.status === "signed-in" || s.status === "signed-out") && (s.user === null || typeof s.user === "object" && typeof s.user.login === "string");
|
|
6
7
|
};
|
|
7
8
|
const channel = createPushChannel({
|
|
8
|
-
pushType:
|
|
9
|
-
requestType:
|
|
9
|
+
pushType: AUTH_STATE,
|
|
10
|
+
requestType: REQUEST_AUTH_STATE,
|
|
10
11
|
initial: { status: "unknown", user: null },
|
|
11
12
|
parse: (msg) => isAuthState(msg.state) ? msg.state : void 0
|
|
12
13
|
});
|
package/dist/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/auth.ts"],"sourcesContent":["import { createPushChannel } from './pushChannel';\n\n/**\n * Login / account state of the immediately.run user, mirrored from the host\n * window into the sandbox.\n *\n * `status` is `'unknown'` until the host has reported a value (use it to\n * distinguish \"still loading\" from a confirmed signed-out session).\n */\nexport type AuthStatus = 'unknown' | 'signed-in' | 'signed-out';\n\n/** The signed-in immediately.run user, as seen by the sandbox (no token, ever). */\nexport interface SandboxUser {\n /** GitHub login (handle) of the signed-in user. */\n login: string;\n}\n\n/** The user's login / account state: a `status` plus the `user` when signed in. */\nexport interface AuthState {\n status: AuthStatus;\n user: SandboxUser | null;\n}\n\nconst isAuthState = (v: unknown): v is AuthState => {\n const s = v as Partial<AuthState> | null;\n return (\n !!s &&\n (s.status === 'unknown' || s.status === 'signed-in' || s.status === 'signed-out') &&\n (s.user === null || (typeof s.user === 'object' && typeof (s.user as SandboxUser).login === 'string'))\n );\n};\n\n// Read over the transport (SDK_PACKAGING_SPEC §4): the host pushes `auth-state`\n// and answers `request-auth-state` (wire format: site-main channelBridge.ts).\nconst channel = createPushChannel<AuthState>({\n pushType:
|
|
1
|
+
{"version":3,"sources":["../src/auth.ts"],"sourcesContent":["import { createPushChannel } from './pushChannel';\nimport { AUTH_STATE, REQUEST_AUTH_STATE } from './generated/protocol';\n\n/**\n * Login / account state of the immediately.run user, mirrored from the host\n * window into the sandbox.\n *\n * `status` is `'unknown'` until the host has reported a value (use it to\n * distinguish \"still loading\" from a confirmed signed-out session).\n */\nexport type AuthStatus = 'unknown' | 'signed-in' | 'signed-out';\n\n/** The signed-in immediately.run user, as seen by the sandbox (no token, ever). */\nexport interface SandboxUser {\n /** GitHub login (handle) of the signed-in user. */\n login: string;\n}\n\n/** The user's login / account state: a `status` plus the `user` when signed in. */\nexport interface AuthState {\n status: AuthStatus;\n user: SandboxUser | null;\n}\n\nconst isAuthState = (v: unknown): v is AuthState => {\n const s = v as Partial<AuthState> | null;\n return (\n !!s &&\n (s.status === 'unknown' || s.status === 'signed-in' || s.status === 'signed-out') &&\n (s.user === null || (typeof s.user === 'object' && typeof (s.user as SandboxUser).login === 'string'))\n );\n};\n\n// Read over the transport (SDK_PACKAGING_SPEC §4): the host pushes `auth-state`\n// and answers `request-auth-state` (wire format: site-main channelBridge.ts).\nconst channel = createPushChannel<AuthState>({\n pushType: AUTH_STATE,\n requestType: REQUEST_AUTH_STATE,\n initial: { status: 'unknown', user: null },\n parse: (msg) => (isAuthState(msg.state) ? (msg.state as AuthState) : undefined),\n});\n\n/**\n * Returns the current login / account state. Poll this whenever you need a\n * one-off read; use {@link onAuthChange} or {@link useAuth} to react to changes.\n */\nexport const getAuthState = (): AuthState => channel.get();\n\n/**\n * Subscribe to login / logout changes. The listener is invoked immediately with\n * the current state, then again on every change. Returns an unsubscribe fn.\n */\nexport const onAuthChange = (listener: (state: AuthState) => void): (() => void) =>\n channel.onChange(listener);\n\n/**\n * React hook returning the current login / account state, re-rendering on\n * login / logout.\n */\nexport const useAuth = (): AuthState => channel.use();\n"],"mappings":";AAAA,SAAS,yBAAyB;AAClC,SAAS,YAAY,0BAA0B;AAuB/C,MAAM,cAAc,CAAC,MAA+B;AAClD,QAAM,IAAI;AACV,SACE,CAAC,CAAC,MACD,EAAE,WAAW,aAAa,EAAE,WAAW,eAAe,EAAE,WAAW,kBACnE,EAAE,SAAS,QAAS,OAAO,EAAE,SAAS,YAAY,OAAQ,EAAE,KAAqB,UAAU;AAEhG;AAIA,MAAM,UAAU,kBAA6B;AAAA,EAC3C,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS,EAAE,QAAQ,WAAW,MAAM,KAAK;AAAA,EACzC,OAAO,CAAC,QAAS,YAAY,IAAI,KAAK,IAAK,IAAI,QAAsB;AACvE,CAAC;AAMM,MAAM,eAAe,MAAiB,QAAQ,IAAI;AAMlD,MAAM,eAAe,CAAC,aAC3B,QAAQ,SAAS,QAAQ;AAMpB,MAAM,UAAU,MAAiB,QAAQ,IAAI;","names":[]}
|
package/dist/boot.cjs
CHANGED
|
@@ -42,6 +42,7 @@ var import_routing = require("./routing");
|
|
|
42
42
|
var import_sandboxUtils = require("./sandboxUtils");
|
|
43
43
|
var import_TinkerableContext = require("./TinkerableContext");
|
|
44
44
|
var import_urlUtils = require("./urlUtils");
|
|
45
|
+
var import_protocol = require("./generated/protocol");
|
|
45
46
|
const resolveMdxComponents = (mdxComponents) => mdxComponents === void 0 ? import_MDXComponents.DEFAULT_MDX_COMPONENTS : typeof mdxComponents === "function" ? mdxComponents(import_MDXComponents.DEFAULT_MDX_COMPONENTS) : { ...import_MDXComponents.DEFAULT_MDX_COMPONENTS, ...mdxComponents };
|
|
46
47
|
const updateAlreadyApplied = (filesMetadata, update) => {
|
|
47
48
|
for (let [key, value] of Object.entries(update)) {
|
|
@@ -57,7 +58,7 @@ const TinkerableApp = ({
|
|
|
57
58
|
}) => {
|
|
58
59
|
const [context, setContext] = (0, import_react.useState)((0, import_contextUtils.getInitialContext)(routingSpec));
|
|
59
60
|
(0, import_react.useEffect)(() => {
|
|
60
|
-
const removeListener = (0, import_sandboxUtils.addListener)(
|
|
61
|
+
const removeListener = (0, import_sandboxUtils.addListener)(import_protocol.URLCHANGE, ({ url }) => {
|
|
61
62
|
setContext((context2) => {
|
|
62
63
|
const updatedContext = (0, import_contextUtils.updateContext)(context2, url);
|
|
63
64
|
if (updatedContext !== context2) {
|
|
@@ -73,7 +74,7 @@ const TinkerableApp = ({
|
|
|
73
74
|
(0, import_react.useEffect)(() => {
|
|
74
75
|
const source = (0, import_injectedBundler.resolveMetadataSource)((0, import_injectedBundler.getInjectedMetadataEmitter)());
|
|
75
76
|
const dispose = (0, import_sandboxUtils.addListener)(
|
|
76
|
-
|
|
77
|
+
import_protocol.METADATA_UPDATE,
|
|
77
78
|
({ update }) => {
|
|
78
79
|
setContext(
|
|
79
80
|
(prevContext) => updateAlreadyApplied(prevContext.filesMetadata, update) ? prevContext : {
|
package/dist/boot.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/boot.tsx"],"sourcesContent":["import { FC, ReactNode, StrictMode, useEffect, useLayoutEffect, useState } from 'react';\nimport { createRoot } from 'react-dom/client';\n\nimport { emitMarkerOnce } from './markers';\n\nimport { ErrorNotFound } from './components/errors';\nimport { FileRouter } from './components/FileRouter';\nimport { MainContent } from './components/MainContent';\nimport { DEFAULT_MDX_COMPONENTS } from './components/MDXComponents';\nimport { ScrollAfterNavigation } from './components/ScrollAfterNavigation';\nimport { getInitialContext, updateContext } from './contextUtils';\nimport { getInjectedMetadataEmitter, resolveMetadataSource } from './injectedBundler';\nimport { MDXProvider } from './MDXProvider';\nimport { ModuleCache, ModuleCacheContextProvider } from './moduleCache';\nimport { Router } from './routing';\nimport type { RoutingSpec } from './RoutingSpec';\nimport { FilesMetadata } from './sandboxTypes';\nimport { addListener } from './sandboxUtils';\nimport { TinkerableContext, TinkerableState } from './TinkerableContext';\nimport { FILES_PREFIX } from './urlUtils';\n\n/** A map of MDX component overrides, or a function that receives the platform\n * {@link DEFAULT_MDX_COMPONENTS} and returns the full map to use. */\nexport type MdxComponents =\n | Record<string, FC>\n | ((defaults: Record<string, FC>) => Record<string, FC>);\n\n/**\n * Resolve the effective MDX component map from a {@link BootProps.mdxComponents}\n * value (MARKDOWN_SYNTAX_SPEC §11.3):\n * - `undefined` → the platform {@link DEFAULT_MDX_COMPONENTS} (same reference).\n * - a **function** → the full-replace escape hatch, handed the defaults.\n * - a **map** → merged *over* the defaults (`{ ...defaults, ...map }`), so\n * overriding one component keeps the rest — the phantom-defaults invariant\n * (§11.2) that stops the MDX missing-reference guard from firing.\n */\nexport const resolveMdxComponents = (mdxComponents?: MdxComponents): Record<string, FC> =>\n mdxComponents === undefined\n ? (DEFAULT_MDX_COMPONENTS as Record<string, FC>)\n : typeof mdxComponents === 'function'\n ? mdxComponents(DEFAULT_MDX_COMPONENTS as Record<string, FC>)\n : { ...(DEFAULT_MDX_COMPONENTS as Record<string, FC>), ...mdxComponents };\n\n/** Options for {@link boot}: MDX overrides, a route table, or an app root. */\nexport type BootProps = {\n /**\n * MDX component overrides. A **map** is *merged over* the platform defaults\n * ({@link DEFAULT_MDX_COMPONENTS}) — so overriding `WikiLink` alone keeps the\n * default `a` and `Admonition` (MARKDOWN_SYNTAX_SPEC §11.3). Pass a **function**\n * `(defaults) => map` as the full-replace escape hatch when you want complete\n * control over the set.\n */\n mdxComponents?: MdxComponents;\n routingSpec?: RoutingSpec;\n /**\n * App root rendered directly inside the providers (with full navigation\n * context), instead of dispatching through a `routingSpec`. Render\n * `<Routes>`/`<Route>` here for fully dynamic routing — no catch-all rule\n * boilerplate. Takes precedence over `routingSpec` for what is rendered.\n */\n children?: ReactNode;\n};\n\nconst updateAlreadyApplied = (filesMetadata: FilesMetadata, update: FilesMetadata) => {\n for (let [key, value] of Object.entries(update)) {\n if (filesMetadata[key] !== value) {\n return false;\n }\n }\n return true;\n};\n\n/** The app shell {@link boot} renders: holds navigation state, subscribes to host\n * URL + metadata pushes, and renders `children` or the route `<Router />`. */\nexport const TinkerableApp = ({\n routingSpec,\n children,\n}: {\n routingSpec: RoutingSpec;\n children?: ReactNode;\n}) => {\n const [context, setContext] = useState<TinkerableState>(getInitialContext(routingSpec));\n useEffect(() => {\n const removeListener = addListener('urlchange', ({ url }) => {\n setContext((context) => {\n const updatedContext = updateContext(context, url);\n if (updatedContext !== context) {\n console.log(\n `[Sandbox] Updating path from ${context.navigationState.sandboxPath} to ${updatedContext.navigationState.sandboxPath}`\n );\n }\n return updatedContext;\n });\n });\n return removeListener;\n }, [setContext]);\n useEffect(() => {\n // Phase 5 dual-mode (SDK_PACKAGING_SPEC §4/§8): prefer the injected bundler's\n // metadata emitter (the live path, byte-identical); when the SDK is npm-fetched\n // with no injection, `event` is undefined so `addListener` receives\n // 'metadata-update' over the §4 transport instead, and `enable` is a no-op.\n const source = resolveMetadataSource(getInjectedMetadataEmitter());\n const dispose = addListener(\n 'metadata-update',\n ({ update }: Record<string, any>) => {\n setContext((prevContext) =>\n updateAlreadyApplied(prevContext.filesMetadata, update)\n ? prevContext\n : {\n ...prevContext,\n filesMetadata: {\n // TODO: file deletion!\n ...prevContext.filesMetadata,\n ...update,\n },\n }\n );\n },\n source.event\n );\n source.enable();\n return dispose;\n }, [setContext]);\n\n return (\n <TinkerableContext value={context}>\n {/* Capability C: lands cross-page `#fragment` deep-links on their section\n after the destination tree mounts, uniform for every MDX app (§13.5). */}\n <ScrollAfterNavigation />\n {children ?? <Router />}\n </TinkerableContext>\n );\n};\n\n// Boot marker emitter (LOAD_PROFILING_SPEC §3, R3-46). Rendered at the top of the\n// app tree so its layout effect fires on the FIRST root-render commit: that instant\n// is `ir.fmp` (the content is in the DOM, about to paint) and the baseline for\n// `ir.interactive` (the host treats a forwarded `ir.interactive` as the root-commit\n// signal and resolves `max(commit, reportReady)` — LP2-3 — so this can only ever be\n// delayed by an app's `reportReady()`, never advanced). Emitted in canonical stream\n// order (fmp then interactive); idempotent per name (StrictMode-safe). Renders null.\nconst BootMarkers = (): null => {\n useLayoutEffect(() => {\n emitMarkerOnce('ir.fmp');\n emitMarkerOnce('ir.interactive');\n }, []);\n return null;\n};\n\n/** The default route table when `boot` is called with no `routingSpec`/`children`:\n * `/` → main content, `/files/<path>` → the file router, else not-found.\n * Re-expressed with path templates (SANDBOX_ROUTING_SPEC §7); the file path\n * surfaces under the `*` wildcard. The catch-all stays a raw RegExp — the\n * escape hatch — so it anchors `.+` (a non-empty path) exactly as before. */\nexport const DEFAULT_ROUTING_SPEC: RoutingSpec = {\n routes: [\n { name: 'MainContent', pattern: '/', element: <MainContent /> },\n { name: 'FileRouter', pattern: `${FILES_PREFIX}/*`, element: <FileRouter /> },\n { name: 'ErrorNotFound', pattern: /^(?<path>.+)$/, element: <ErrorNotFound /> },\n ],\n};\n\n/**\n * Matches any `sandboxPath` so navigation context can be built without a route\n * table. Used when {@link boot} is given `children` (the app owns dispatch via\n * `<Routes>`); the catch-all's `element` is never rendered (children are).\n */\nexport const CATCH_ALL_ROUTING_SPEC: RoutingSpec = {\n routes: [{ name: 'AppRoot', pattern: /^.*$/, element: null }],\n};\n\n/**\n * Mount an immediately.run app into the sandbox `#root`. The entry point every\n * app calls from `index.tsx`: wires the MDX, module-cache, and navigation\n * providers, then renders the route table (`routingSpec`) or your `children`.\n */\nexport const boot = ({\n mdxComponents,\n routingSpec,\n children,\n}: BootProps = {}) => {\n const rootElement = document.getElementById('root');\n if (!rootElement) {\n throw new Error('boot requires root HTML element to exist');\n }\n const resolvedComponents = resolveMdxComponents(mdxComponents);\n // `children` own dispatch, so a catch-all keeps navigation working without a\n // table; otherwise fall back to the default file/main-content routes.\n const spec = routingSpec ?? (children ? CATCH_ALL_ROUTING_SPEC : DEFAULT_ROUTING_SPEC);\n const moduleCache = new ModuleCache();\n const root = createRoot(rootElement);\n root.render(\n <StrictMode>\n <ModuleCacheContextProvider moduleCache={moduleCache}>\n <MDXProvider components={resolvedComponents}>\n <BootMarkers />\n <TinkerableApp routingSpec={spec}>{children}</TinkerableApp>\n </MDXProvider>\n </ModuleCacheContextProvider>\n </StrictMode>\n );\n};\n"],"mappings":"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","names":["context"]}
|
|
1
|
+
{"version":3,"sources":["../src/boot.tsx"],"sourcesContent":["import { FC, ReactNode, StrictMode, useEffect, useLayoutEffect, useState } from 'react';\nimport { createRoot } from 'react-dom/client';\n\nimport { emitMarkerOnce } from './markers';\n\nimport { ErrorNotFound } from './components/errors';\nimport { FileRouter } from './components/FileRouter';\nimport { MainContent } from './components/MainContent';\nimport { DEFAULT_MDX_COMPONENTS } from './components/MDXComponents';\nimport { ScrollAfterNavigation } from './components/ScrollAfterNavigation';\nimport { getInitialContext, updateContext } from './contextUtils';\nimport { getInjectedMetadataEmitter, resolveMetadataSource } from './injectedBundler';\nimport { MDXProvider } from './MDXProvider';\nimport { ModuleCache, ModuleCacheContextProvider } from './moduleCache';\nimport { Router } from './routing';\nimport type { RoutingSpec } from './RoutingSpec';\nimport { FilesMetadata } from './sandboxTypes';\nimport { addListener } from './sandboxUtils';\nimport { TinkerableContext, TinkerableState } from './TinkerableContext';\nimport { FILES_PREFIX } from './urlUtils';\nimport { METADATA_UPDATE, URLCHANGE } from './generated/protocol';\n\n/** A map of MDX component overrides, or a function that receives the platform\n * {@link DEFAULT_MDX_COMPONENTS} and returns the full map to use. */\nexport type MdxComponents =\n | Record<string, FC>\n | ((defaults: Record<string, FC>) => Record<string, FC>);\n\n/**\n * Resolve the effective MDX component map from a {@link BootProps.mdxComponents}\n * value (MARKDOWN_SYNTAX_SPEC §11.3):\n * - `undefined` → the platform {@link DEFAULT_MDX_COMPONENTS} (same reference).\n * - a **function** → the full-replace escape hatch, handed the defaults.\n * - a **map** → merged *over* the defaults (`{ ...defaults, ...map }`), so\n * overriding one component keeps the rest — the phantom-defaults invariant\n * (§11.2) that stops the MDX missing-reference guard from firing.\n */\nexport const resolveMdxComponents = (mdxComponents?: MdxComponents): Record<string, FC> =>\n mdxComponents === undefined\n ? (DEFAULT_MDX_COMPONENTS as Record<string, FC>)\n : typeof mdxComponents === 'function'\n ? mdxComponents(DEFAULT_MDX_COMPONENTS as Record<string, FC>)\n : { ...(DEFAULT_MDX_COMPONENTS as Record<string, FC>), ...mdxComponents };\n\n/** Options for {@link boot}: MDX overrides, a route table, or an app root. */\nexport type BootProps = {\n /**\n * MDX component overrides. A **map** is *merged over* the platform defaults\n * ({@link DEFAULT_MDX_COMPONENTS}) — so overriding `WikiLink` alone keeps the\n * default `a` and `Admonition` (MARKDOWN_SYNTAX_SPEC §11.3). Pass a **function**\n * `(defaults) => map` as the full-replace escape hatch when you want complete\n * control over the set.\n */\n mdxComponents?: MdxComponents;\n routingSpec?: RoutingSpec;\n /**\n * App root rendered directly inside the providers (with full navigation\n * context), instead of dispatching through a `routingSpec`. Render\n * `<Routes>`/`<Route>` here for fully dynamic routing — no catch-all rule\n * boilerplate. Takes precedence over `routingSpec` for what is rendered.\n */\n children?: ReactNode;\n};\n\nconst updateAlreadyApplied = (filesMetadata: FilesMetadata, update: FilesMetadata) => {\n for (let [key, value] of Object.entries(update)) {\n if (filesMetadata[key] !== value) {\n return false;\n }\n }\n return true;\n};\n\n/** The app shell {@link boot} renders: holds navigation state, subscribes to host\n * URL + metadata pushes, and renders `children` or the route `<Router />`. */\nexport const TinkerableApp = ({\n routingSpec,\n children,\n}: {\n routingSpec: RoutingSpec;\n children?: ReactNode;\n}) => {\n const [context, setContext] = useState<TinkerableState>(getInitialContext(routingSpec));\n useEffect(() => {\n const removeListener = addListener(URLCHANGE, ({ url }) => {\n setContext((context) => {\n const updatedContext = updateContext(context, url);\n if (updatedContext !== context) {\n console.log(\n `[Sandbox] Updating path from ${context.navigationState.sandboxPath} to ${updatedContext.navigationState.sandboxPath}`\n );\n }\n return updatedContext;\n });\n });\n return removeListener;\n }, [setContext]);\n useEffect(() => {\n // Phase 5 dual-mode (SDK_PACKAGING_SPEC §4/§8): prefer the injected bundler's\n // metadata emitter (the live path, byte-identical); when the SDK is npm-fetched\n // with no injection, `event` is undefined so `addListener` receives\n // 'metadata-update' over the §4 transport instead, and `enable` is a no-op.\n const source = resolveMetadataSource(getInjectedMetadataEmitter());\n const dispose = addListener(\n METADATA_UPDATE,\n ({ update }: Record<string, any>) => {\n setContext((prevContext) =>\n updateAlreadyApplied(prevContext.filesMetadata, update)\n ? prevContext\n : {\n ...prevContext,\n filesMetadata: {\n // TODO: file deletion!\n ...prevContext.filesMetadata,\n ...update,\n },\n }\n );\n },\n source.event\n );\n source.enable();\n return dispose;\n }, [setContext]);\n\n return (\n <TinkerableContext value={context}>\n {/* Capability C: lands cross-page `#fragment` deep-links on their section\n after the destination tree mounts, uniform for every MDX app (§13.5). */}\n <ScrollAfterNavigation />\n {children ?? <Router />}\n </TinkerableContext>\n );\n};\n\n// Boot marker emitter (LOAD_PROFILING_SPEC §3, R3-46). Rendered at the top of the\n// app tree so its layout effect fires on the FIRST root-render commit: that instant\n// is `ir.fmp` (the content is in the DOM, about to paint) and the baseline for\n// `ir.interactive` (the host treats a forwarded `ir.interactive` as the root-commit\n// signal and resolves `max(commit, reportReady)` — LP2-3 — so this can only ever be\n// delayed by an app's `reportReady()`, never advanced). Emitted in canonical stream\n// order (fmp then interactive); idempotent per name (StrictMode-safe). Renders null.\nconst BootMarkers = (): null => {\n useLayoutEffect(() => {\n emitMarkerOnce('ir.fmp');\n emitMarkerOnce('ir.interactive');\n }, []);\n return null;\n};\n\n/** The default route table when `boot` is called with no `routingSpec`/`children`:\n * `/` → main content, `/files/<path>` → the file router, else not-found.\n * Re-expressed with path templates (SANDBOX_ROUTING_SPEC §7); the file path\n * surfaces under the `*` wildcard. The catch-all stays a raw RegExp — the\n * escape hatch — so it anchors `.+` (a non-empty path) exactly as before. */\nexport const DEFAULT_ROUTING_SPEC: RoutingSpec = {\n routes: [\n { name: 'MainContent', pattern: '/', element: <MainContent /> },\n { name: 'FileRouter', pattern: `${FILES_PREFIX}/*`, element: <FileRouter /> },\n { name: 'ErrorNotFound', pattern: /^(?<path>.+)$/, element: <ErrorNotFound /> },\n ],\n};\n\n/**\n * Matches any `sandboxPath` so navigation context can be built without a route\n * table. Used when {@link boot} is given `children` (the app owns dispatch via\n * `<Routes>`); the catch-all's `element` is never rendered (children are).\n */\nexport const CATCH_ALL_ROUTING_SPEC: RoutingSpec = {\n routes: [{ name: 'AppRoot', pattern: /^.*$/, element: null }],\n};\n\n/**\n * Mount an immediately.run app into the sandbox `#root`. The entry point every\n * app calls from `index.tsx`: wires the MDX, module-cache, and navigation\n * providers, then renders the route table (`routingSpec`) or your `children`.\n */\nexport const boot = ({\n mdxComponents,\n routingSpec,\n children,\n}: BootProps = {}) => {\n const rootElement = document.getElementById('root');\n if (!rootElement) {\n throw new Error('boot requires root HTML element to exist');\n }\n const resolvedComponents = resolveMdxComponents(mdxComponents);\n // `children` own dispatch, so a catch-all keeps navigation working without a\n // table; otherwise fall back to the default file/main-content routes.\n const spec = routingSpec ?? (children ? CATCH_ALL_ROUTING_SPEC : DEFAULT_ROUTING_SPEC);\n const moduleCache = new ModuleCache();\n const root = createRoot(rootElement);\n root.render(\n <StrictMode>\n <ModuleCacheContextProvider moduleCache={moduleCache}>\n <MDXProvider components={resolvedComponents}>\n <BootMarkers />\n <TinkerableApp routingSpec={spec}>{children}</TinkerableApp>\n </MDXProvider>\n </ModuleCacheContextProvider>\n </StrictMode>\n );\n};\n"],"mappings":"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","names":["context"]}
|
package/dist/boot.js
CHANGED
|
@@ -16,6 +16,7 @@ import { Router } from "./routing";
|
|
|
16
16
|
import { addListener } from "./sandboxUtils";
|
|
17
17
|
import { TinkerableContext } from "./TinkerableContext";
|
|
18
18
|
import { FILES_PREFIX } from "./urlUtils";
|
|
19
|
+
import { METADATA_UPDATE, URLCHANGE } from "./generated/protocol";
|
|
19
20
|
const resolveMdxComponents = (mdxComponents) => mdxComponents === void 0 ? DEFAULT_MDX_COMPONENTS : typeof mdxComponents === "function" ? mdxComponents(DEFAULT_MDX_COMPONENTS) : { ...DEFAULT_MDX_COMPONENTS, ...mdxComponents };
|
|
20
21
|
const updateAlreadyApplied = (filesMetadata, update) => {
|
|
21
22
|
for (let [key, value] of Object.entries(update)) {
|
|
@@ -31,7 +32,7 @@ const TinkerableApp = ({
|
|
|
31
32
|
}) => {
|
|
32
33
|
const [context, setContext] = useState(getInitialContext(routingSpec));
|
|
33
34
|
useEffect(() => {
|
|
34
|
-
const removeListener = addListener(
|
|
35
|
+
const removeListener = addListener(URLCHANGE, ({ url }) => {
|
|
35
36
|
setContext((context2) => {
|
|
36
37
|
const updatedContext = updateContext(context2, url);
|
|
37
38
|
if (updatedContext !== context2) {
|
|
@@ -47,7 +48,7 @@ const TinkerableApp = ({
|
|
|
47
48
|
useEffect(() => {
|
|
48
49
|
const source = resolveMetadataSource(getInjectedMetadataEmitter());
|
|
49
50
|
const dispose = addListener(
|
|
50
|
-
|
|
51
|
+
METADATA_UPDATE,
|
|
51
52
|
({ update }) => {
|
|
52
53
|
setContext(
|
|
53
54
|
(prevContext) => updateAlreadyApplied(prevContext.filesMetadata, update) ? prevContext : {
|
package/dist/boot.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/boot.tsx"],"sourcesContent":["import { FC, ReactNode, StrictMode, useEffect, useLayoutEffect, useState } from 'react';\nimport { createRoot } from 'react-dom/client';\n\nimport { emitMarkerOnce } from './markers';\n\nimport { ErrorNotFound } from './components/errors';\nimport { FileRouter } from './components/FileRouter';\nimport { MainContent } from './components/MainContent';\nimport { DEFAULT_MDX_COMPONENTS } from './components/MDXComponents';\nimport { ScrollAfterNavigation } from './components/ScrollAfterNavigation';\nimport { getInitialContext, updateContext } from './contextUtils';\nimport { getInjectedMetadataEmitter, resolveMetadataSource } from './injectedBundler';\nimport { MDXProvider } from './MDXProvider';\nimport { ModuleCache, ModuleCacheContextProvider } from './moduleCache';\nimport { Router } from './routing';\nimport type { RoutingSpec } from './RoutingSpec';\nimport { FilesMetadata } from './sandboxTypes';\nimport { addListener } from './sandboxUtils';\nimport { TinkerableContext, TinkerableState } from './TinkerableContext';\nimport { FILES_PREFIX } from './urlUtils';\n\n/** A map of MDX component overrides, or a function that receives the platform\n * {@link DEFAULT_MDX_COMPONENTS} and returns the full map to use. */\nexport type MdxComponents =\n | Record<string, FC>\n | ((defaults: Record<string, FC>) => Record<string, FC>);\n\n/**\n * Resolve the effective MDX component map from a {@link BootProps.mdxComponents}\n * value (MARKDOWN_SYNTAX_SPEC §11.3):\n * - `undefined` → the platform {@link DEFAULT_MDX_COMPONENTS} (same reference).\n * - a **function** → the full-replace escape hatch, handed the defaults.\n * - a **map** → merged *over* the defaults (`{ ...defaults, ...map }`), so\n * overriding one component keeps the rest — the phantom-defaults invariant\n * (§11.2) that stops the MDX missing-reference guard from firing.\n */\nexport const resolveMdxComponents = (mdxComponents?: MdxComponents): Record<string, FC> =>\n mdxComponents === undefined\n ? (DEFAULT_MDX_COMPONENTS as Record<string, FC>)\n : typeof mdxComponents === 'function'\n ? mdxComponents(DEFAULT_MDX_COMPONENTS as Record<string, FC>)\n : { ...(DEFAULT_MDX_COMPONENTS as Record<string, FC>), ...mdxComponents };\n\n/** Options for {@link boot}: MDX overrides, a route table, or an app root. */\nexport type BootProps = {\n /**\n * MDX component overrides. A **map** is *merged over* the platform defaults\n * ({@link DEFAULT_MDX_COMPONENTS}) — so overriding `WikiLink` alone keeps the\n * default `a` and `Admonition` (MARKDOWN_SYNTAX_SPEC §11.3). Pass a **function**\n * `(defaults) => map` as the full-replace escape hatch when you want complete\n * control over the set.\n */\n mdxComponents?: MdxComponents;\n routingSpec?: RoutingSpec;\n /**\n * App root rendered directly inside the providers (with full navigation\n * context), instead of dispatching through a `routingSpec`. Render\n * `<Routes>`/`<Route>` here for fully dynamic routing — no catch-all rule\n * boilerplate. Takes precedence over `routingSpec` for what is rendered.\n */\n children?: ReactNode;\n};\n\nconst updateAlreadyApplied = (filesMetadata: FilesMetadata, update: FilesMetadata) => {\n for (let [key, value] of Object.entries(update)) {\n if (filesMetadata[key] !== value) {\n return false;\n }\n }\n return true;\n};\n\n/** The app shell {@link boot} renders: holds navigation state, subscribes to host\n * URL + metadata pushes, and renders `children` or the route `<Router />`. */\nexport const TinkerableApp = ({\n routingSpec,\n children,\n}: {\n routingSpec: RoutingSpec;\n children?: ReactNode;\n}) => {\n const [context, setContext] = useState<TinkerableState>(getInitialContext(routingSpec));\n useEffect(() => {\n const removeListener = addListener('urlchange', ({ url }) => {\n setContext((context) => {\n const updatedContext = updateContext(context, url);\n if (updatedContext !== context) {\n console.log(\n `[Sandbox] Updating path from ${context.navigationState.sandboxPath} to ${updatedContext.navigationState.sandboxPath}`\n );\n }\n return updatedContext;\n });\n });\n return removeListener;\n }, [setContext]);\n useEffect(() => {\n // Phase 5 dual-mode (SDK_PACKAGING_SPEC §4/§8): prefer the injected bundler's\n // metadata emitter (the live path, byte-identical); when the SDK is npm-fetched\n // with no injection, `event` is undefined so `addListener` receives\n // 'metadata-update' over the §4 transport instead, and `enable` is a no-op.\n const source = resolveMetadataSource(getInjectedMetadataEmitter());\n const dispose = addListener(\n 'metadata-update',\n ({ update }: Record<string, any>) => {\n setContext((prevContext) =>\n updateAlreadyApplied(prevContext.filesMetadata, update)\n ? prevContext\n : {\n ...prevContext,\n filesMetadata: {\n // TODO: file deletion!\n ...prevContext.filesMetadata,\n ...update,\n },\n }\n );\n },\n source.event\n );\n source.enable();\n return dispose;\n }, [setContext]);\n\n return (\n <TinkerableContext value={context}>\n {/* Capability C: lands cross-page `#fragment` deep-links on their section\n after the destination tree mounts, uniform for every MDX app (§13.5). */}\n <ScrollAfterNavigation />\n {children ?? <Router />}\n </TinkerableContext>\n );\n};\n\n// Boot marker emitter (LOAD_PROFILING_SPEC §3, R3-46). Rendered at the top of the\n// app tree so its layout effect fires on the FIRST root-render commit: that instant\n// is `ir.fmp` (the content is in the DOM, about to paint) and the baseline for\n// `ir.interactive` (the host treats a forwarded `ir.interactive` as the root-commit\n// signal and resolves `max(commit, reportReady)` — LP2-3 — so this can only ever be\n// delayed by an app's `reportReady()`, never advanced). Emitted in canonical stream\n// order (fmp then interactive); idempotent per name (StrictMode-safe). Renders null.\nconst BootMarkers = (): null => {\n useLayoutEffect(() => {\n emitMarkerOnce('ir.fmp');\n emitMarkerOnce('ir.interactive');\n }, []);\n return null;\n};\n\n/** The default route table when `boot` is called with no `routingSpec`/`children`:\n * `/` → main content, `/files/<path>` → the file router, else not-found.\n * Re-expressed with path templates (SANDBOX_ROUTING_SPEC §7); the file path\n * surfaces under the `*` wildcard. The catch-all stays a raw RegExp — the\n * escape hatch — so it anchors `.+` (a non-empty path) exactly as before. */\nexport const DEFAULT_ROUTING_SPEC: RoutingSpec = {\n routes: [\n { name: 'MainContent', pattern: '/', element: <MainContent /> },\n { name: 'FileRouter', pattern: `${FILES_PREFIX}/*`, element: <FileRouter /> },\n { name: 'ErrorNotFound', pattern: /^(?<path>.+)$/, element: <ErrorNotFound /> },\n ],\n};\n\n/**\n * Matches any `sandboxPath` so navigation context can be built without a route\n * table. Used when {@link boot} is given `children` (the app owns dispatch via\n * `<Routes>`); the catch-all's `element` is never rendered (children are).\n */\nexport const CATCH_ALL_ROUTING_SPEC: RoutingSpec = {\n routes: [{ name: 'AppRoot', pattern: /^.*$/, element: null }],\n};\n\n/**\n * Mount an immediately.run app into the sandbox `#root`. The entry point every\n * app calls from `index.tsx`: wires the MDX, module-cache, and navigation\n * providers, then renders the route table (`routingSpec`) or your `children`.\n */\nexport const boot = ({\n mdxComponents,\n routingSpec,\n children,\n}: BootProps = {}) => {\n const rootElement = document.getElementById('root');\n if (!rootElement) {\n throw new Error('boot requires root HTML element to exist');\n }\n const resolvedComponents = resolveMdxComponents(mdxComponents);\n // `children` own dispatch, so a catch-all keeps navigation working without a\n // table; otherwise fall back to the default file/main-content routes.\n const spec = routingSpec ?? (children ? CATCH_ALL_ROUTING_SPEC : DEFAULT_ROUTING_SPEC);\n const moduleCache = new ModuleCache();\n const root = createRoot(rootElement);\n root.render(\n <StrictMode>\n <ModuleCacheContextProvider moduleCache={moduleCache}>\n <MDXProvider components={resolvedComponents}>\n <BootMarkers />\n <TinkerableApp routingSpec={spec}>{children}</TinkerableApp>\n </MDXProvider>\n </ModuleCacheContextProvider>\n </StrictMode>\n );\n};\n"],"mappings":"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","names":["context"]}
|
|
1
|
+
{"version":3,"sources":["../src/boot.tsx"],"sourcesContent":["import { FC, ReactNode, StrictMode, useEffect, useLayoutEffect, useState } from 'react';\nimport { createRoot } from 'react-dom/client';\n\nimport { emitMarkerOnce } from './markers';\n\nimport { ErrorNotFound } from './components/errors';\nimport { FileRouter } from './components/FileRouter';\nimport { MainContent } from './components/MainContent';\nimport { DEFAULT_MDX_COMPONENTS } from './components/MDXComponents';\nimport { ScrollAfterNavigation } from './components/ScrollAfterNavigation';\nimport { getInitialContext, updateContext } from './contextUtils';\nimport { getInjectedMetadataEmitter, resolveMetadataSource } from './injectedBundler';\nimport { MDXProvider } from './MDXProvider';\nimport { ModuleCache, ModuleCacheContextProvider } from './moduleCache';\nimport { Router } from './routing';\nimport type { RoutingSpec } from './RoutingSpec';\nimport { FilesMetadata } from './sandboxTypes';\nimport { addListener } from './sandboxUtils';\nimport { TinkerableContext, TinkerableState } from './TinkerableContext';\nimport { FILES_PREFIX } from './urlUtils';\nimport { METADATA_UPDATE, URLCHANGE } from './generated/protocol';\n\n/** A map of MDX component overrides, or a function that receives the platform\n * {@link DEFAULT_MDX_COMPONENTS} and returns the full map to use. */\nexport type MdxComponents =\n | Record<string, FC>\n | ((defaults: Record<string, FC>) => Record<string, FC>);\n\n/**\n * Resolve the effective MDX component map from a {@link BootProps.mdxComponents}\n * value (MARKDOWN_SYNTAX_SPEC §11.3):\n * - `undefined` → the platform {@link DEFAULT_MDX_COMPONENTS} (same reference).\n * - a **function** → the full-replace escape hatch, handed the defaults.\n * - a **map** → merged *over* the defaults (`{ ...defaults, ...map }`), so\n * overriding one component keeps the rest — the phantom-defaults invariant\n * (§11.2) that stops the MDX missing-reference guard from firing.\n */\nexport const resolveMdxComponents = (mdxComponents?: MdxComponents): Record<string, FC> =>\n mdxComponents === undefined\n ? (DEFAULT_MDX_COMPONENTS as Record<string, FC>)\n : typeof mdxComponents === 'function'\n ? mdxComponents(DEFAULT_MDX_COMPONENTS as Record<string, FC>)\n : { ...(DEFAULT_MDX_COMPONENTS as Record<string, FC>), ...mdxComponents };\n\n/** Options for {@link boot}: MDX overrides, a route table, or an app root. */\nexport type BootProps = {\n /**\n * MDX component overrides. A **map** is *merged over* the platform defaults\n * ({@link DEFAULT_MDX_COMPONENTS}) — so overriding `WikiLink` alone keeps the\n * default `a` and `Admonition` (MARKDOWN_SYNTAX_SPEC §11.3). Pass a **function**\n * `(defaults) => map` as the full-replace escape hatch when you want complete\n * control over the set.\n */\n mdxComponents?: MdxComponents;\n routingSpec?: RoutingSpec;\n /**\n * App root rendered directly inside the providers (with full navigation\n * context), instead of dispatching through a `routingSpec`. Render\n * `<Routes>`/`<Route>` here for fully dynamic routing — no catch-all rule\n * boilerplate. Takes precedence over `routingSpec` for what is rendered.\n */\n children?: ReactNode;\n};\n\nconst updateAlreadyApplied = (filesMetadata: FilesMetadata, update: FilesMetadata) => {\n for (let [key, value] of Object.entries(update)) {\n if (filesMetadata[key] !== value) {\n return false;\n }\n }\n return true;\n};\n\n/** The app shell {@link boot} renders: holds navigation state, subscribes to host\n * URL + metadata pushes, and renders `children` or the route `<Router />`. */\nexport const TinkerableApp = ({\n routingSpec,\n children,\n}: {\n routingSpec: RoutingSpec;\n children?: ReactNode;\n}) => {\n const [context, setContext] = useState<TinkerableState>(getInitialContext(routingSpec));\n useEffect(() => {\n const removeListener = addListener(URLCHANGE, ({ url }) => {\n setContext((context) => {\n const updatedContext = updateContext(context, url);\n if (updatedContext !== context) {\n console.log(\n `[Sandbox] Updating path from ${context.navigationState.sandboxPath} to ${updatedContext.navigationState.sandboxPath}`\n );\n }\n return updatedContext;\n });\n });\n return removeListener;\n }, [setContext]);\n useEffect(() => {\n // Phase 5 dual-mode (SDK_PACKAGING_SPEC §4/§8): prefer the injected bundler's\n // metadata emitter (the live path, byte-identical); when the SDK is npm-fetched\n // with no injection, `event` is undefined so `addListener` receives\n // 'metadata-update' over the §4 transport instead, and `enable` is a no-op.\n const source = resolveMetadataSource(getInjectedMetadataEmitter());\n const dispose = addListener(\n METADATA_UPDATE,\n ({ update }: Record<string, any>) => {\n setContext((prevContext) =>\n updateAlreadyApplied(prevContext.filesMetadata, update)\n ? prevContext\n : {\n ...prevContext,\n filesMetadata: {\n // TODO: file deletion!\n ...prevContext.filesMetadata,\n ...update,\n },\n }\n );\n },\n source.event\n );\n source.enable();\n return dispose;\n }, [setContext]);\n\n return (\n <TinkerableContext value={context}>\n {/* Capability C: lands cross-page `#fragment` deep-links on their section\n after the destination tree mounts, uniform for every MDX app (§13.5). */}\n <ScrollAfterNavigation />\n {children ?? <Router />}\n </TinkerableContext>\n );\n};\n\n// Boot marker emitter (LOAD_PROFILING_SPEC §3, R3-46). Rendered at the top of the\n// app tree so its layout effect fires on the FIRST root-render commit: that instant\n// is `ir.fmp` (the content is in the DOM, about to paint) and the baseline for\n// `ir.interactive` (the host treats a forwarded `ir.interactive` as the root-commit\n// signal and resolves `max(commit, reportReady)` — LP2-3 — so this can only ever be\n// delayed by an app's `reportReady()`, never advanced). Emitted in canonical stream\n// order (fmp then interactive); idempotent per name (StrictMode-safe). Renders null.\nconst BootMarkers = (): null => {\n useLayoutEffect(() => {\n emitMarkerOnce('ir.fmp');\n emitMarkerOnce('ir.interactive');\n }, []);\n return null;\n};\n\n/** The default route table when `boot` is called with no `routingSpec`/`children`:\n * `/` → main content, `/files/<path>` → the file router, else not-found.\n * Re-expressed with path templates (SANDBOX_ROUTING_SPEC §7); the file path\n * surfaces under the `*` wildcard. The catch-all stays a raw RegExp — the\n * escape hatch — so it anchors `.+` (a non-empty path) exactly as before. */\nexport const DEFAULT_ROUTING_SPEC: RoutingSpec = {\n routes: [\n { name: 'MainContent', pattern: '/', element: <MainContent /> },\n { name: 'FileRouter', pattern: `${FILES_PREFIX}/*`, element: <FileRouter /> },\n { name: 'ErrorNotFound', pattern: /^(?<path>.+)$/, element: <ErrorNotFound /> },\n ],\n};\n\n/**\n * Matches any `sandboxPath` so navigation context can be built without a route\n * table. Used when {@link boot} is given `children` (the app owns dispatch via\n * `<Routes>`); the catch-all's `element` is never rendered (children are).\n */\nexport const CATCH_ALL_ROUTING_SPEC: RoutingSpec = {\n routes: [{ name: 'AppRoot', pattern: /^.*$/, element: null }],\n};\n\n/**\n * Mount an immediately.run app into the sandbox `#root`. The entry point every\n * app calls from `index.tsx`: wires the MDX, module-cache, and navigation\n * providers, then renders the route table (`routingSpec`) or your `children`.\n */\nexport const boot = ({\n mdxComponents,\n routingSpec,\n children,\n}: BootProps = {}) => {\n const rootElement = document.getElementById('root');\n if (!rootElement) {\n throw new Error('boot requires root HTML element to exist');\n }\n const resolvedComponents = resolveMdxComponents(mdxComponents);\n // `children` own dispatch, so a catch-all keeps navigation working without a\n // table; otherwise fall back to the default file/main-content routes.\n const spec = routingSpec ?? (children ? CATCH_ALL_ROUTING_SPEC : DEFAULT_ROUTING_SPEC);\n const moduleCache = new ModuleCache();\n const root = createRoot(rootElement);\n root.render(\n <StrictMode>\n <ModuleCacheContextProvider moduleCache={moduleCache}>\n <MDXProvider components={resolvedComponents}>\n <BootMarkers />\n <TinkerableApp routingSpec={spec}>{children}</TinkerableApp>\n </MDXProvider>\n </ModuleCacheContextProvider>\n </StrictMode>\n );\n};\n"],"mappings":"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","names":["context"]}
|
package/dist/catalog.cjs
CHANGED
|
@@ -28,6 +28,7 @@ module.exports = __toCommonJS(catalog_exports);
|
|
|
28
28
|
var import_sandboxUtils = require("./sandboxUtils");
|
|
29
29
|
var import_protocolStream = require("./protocolStream");
|
|
30
30
|
var import_pushChannel = require("./pushChannel");
|
|
31
|
+
var import_protocol = require("./generated/protocol");
|
|
31
32
|
const split = (name) => {
|
|
32
33
|
const i = name.indexOf(":");
|
|
33
34
|
if (i <= 0) throw new Error(`invalid catalog method name: ${name}`);
|
|
@@ -55,8 +56,8 @@ function invokeStream(name, params = {}, signal) {
|
|
|
55
56
|
return (0, import_protocolStream.consumeStream)(streamTransport, `protocol-${scheme}`, method, [params], void 0, signal);
|
|
56
57
|
}
|
|
57
58
|
const channel = (0, import_pushChannel.createPushChannel)({
|
|
58
|
-
pushType:
|
|
59
|
-
requestType:
|
|
59
|
+
pushType: import_protocol.API_CATALOG,
|
|
60
|
+
requestType: import_protocol.REQUEST_API_CATALOG,
|
|
60
61
|
initial: [],
|
|
61
62
|
parse: (msg) => Array.isArray(msg.methods) ? msg.methods : void 0
|
|
62
63
|
});
|
package/dist/catalog.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/catalog.ts"],"sourcesContent":["// The method catalog (UI_AS_APPS_SPEC §5.5) — the app's own grant-filtered RPC\n// surface, and a generic way to call it. The host advertises exactly the methods\n// this app may invoke (MCP-tool-shaped); `invoke()` calls one by its catalog name.\n// Handing the catalog to an embedded agent as its tool list confines the agent to\n// the app's authority (agent sandboxing falls out of the capability model, §5.9).\nimport { protocolRequest, sendMessage, addListener } from './sandboxUtils';\nimport type { StreamFrame, StreamTransport } from './protocolStream';\nimport { consumeStream } from './protocolStream';\nimport { createPushChannel } from './pushChannel';\n\n/** One advertised method, as the host generated it from its gate table. */\nexport interface ApiMethod {\n /** Catalog name, `protocol-` stripped — e.g. `spaces:invite`, `contribute:run`. */\n name: string;\n /** The capability this method requires (already held — it's in your catalog). */\n capability: string;\n /** True when the method STREAMS (use {@link invokeStream}) vs. single-reply. */\n stream?: boolean;\n /**\n * JSON Schema for the method's single object argument, when the host declares one.\n * Self-describes the call so a catalog-as-tools bridge (an embedded agent) can\n * advertise the real param shape — e.g. that `authoring:typecheck` takes a nested\n * `{ files: [{ path, content }] }` array — instead of a permissive \"any object\".\n * Absent for methods the host advertises without a schema.\n */\n paramsSchema?: Record<string, unknown>;\n}\n\n// `scheme:method` → ['scheme', 'method'] (the wire protocol is `protocol-scheme`).\nconst split = (name: string): [string, string] => {\n const i = name.indexOf(':');\n if (i <= 0) throw new Error(`invalid catalog method name: ${name}`);\n return [name.slice(0, i), name.slice(i + 1)];\n};\n\n/**\n * Call a catalog method by name — `invoke('spaces:invite', { spaceId, login, role })`.\n * A thin generic over the host protocol: the host validates params and gates the\n * call (an un-granted method → `forbidden`, even if you name it directly). For a\n * STREAMING method (`ApiMethod.stream`), use {@link invokeStream}.\n */\nexport const invoke = async <T = unknown>(\n name: string,\n params: Record<string, unknown> = {},\n): Promise<T> => {\n const [scheme, method] = split(name);\n // The host replies with an `{ ok, data } | { ok:false, code }` envelope; unwrap\n // it and THROW on refusal (a `.code` like `forbidden` for an off-catalog call)\n // so callers — and any agent driving `invoke` — see the gate's verdict.\n const res = (await protocolRequest(scheme, method, [params])) as\n | { ok: true; data: unknown }\n | { ok: false; code?: string; message?: string }\n | undefined;\n if (!res || res.ok !== true) {\n const err = new Error(res?.message ?? `${name} failed`) as Error & { code?: string };\n err.code = res?.code ?? 'unknown';\n throw err;\n }\n return res.data as T;\n};\n\n// Stream transport over the resolver (SDK_PACKAGING_SPEC §4) — sendMessage /\n// addListener route through `transport()` (injected bundler messageBus or the §4\n// global), never `bundler.messageBus` directly.\nconst streamTransport: StreamTransport = {\n send: (msg) => sendMessage(msg.type, msg as unknown as Record<string, unknown>),\n subscribe: (type, handler) =>\n addListener(type, (msg) => handler(msg as { msgId?: number; stream?: StreamFrame })),\n // Early-cancel: route a `{type, msgId, cancel:true}` frame back to the host so it\n // aborts the in-flight generation (and, for `llm:chat`, stops billing) — §3.3.\n cancel: (msg) => sendMessage(msg.type, msg as unknown as Record<string, unknown>),\n};\n\n/** Call a STREAMING catalog method by name, yielding its events. Pass `signal` to\n * abort mid-stream: the host stops generating and (for `llm:chat`) stops billing. */\nexport function invokeStream<T = unknown, R = unknown>(\n name: string,\n params: Record<string, unknown> = {},\n signal?: AbortSignal,\n): AsyncGenerator<T, R, void> {\n const [scheme, method] = split(name);\n return consumeStream<T, R>(streamTransport, `protocol-${scheme}`, method, [params], undefined, signal);\n}\n\n// The catalog list is read over the transport (§4): the host pushes `api-catalog`\n// and answers `request-api-catalog` with this app's grant-filtered methods (wire\n// format: site-main channelBridge.ts).\nconst channel = createPushChannel<ApiMethod[]>({\n pushType:
|
|
1
|
+
{"version":3,"sources":["../src/catalog.ts"],"sourcesContent":["// The method catalog (UI_AS_APPS_SPEC §5.5) — the app's own grant-filtered RPC\n// surface, and a generic way to call it. The host advertises exactly the methods\n// this app may invoke (MCP-tool-shaped); `invoke()` calls one by its catalog name.\n// Handing the catalog to an embedded agent as its tool list confines the agent to\n// the app's authority (agent sandboxing falls out of the capability model, §5.9).\nimport { protocolRequest, sendMessage, addListener } from './sandboxUtils';\nimport type { StreamFrame, StreamTransport } from './protocolStream';\nimport { consumeStream } from './protocolStream';\nimport { createPushChannel } from './pushChannel';\nimport { API_CATALOG, REQUEST_API_CATALOG } from './generated/protocol';\n\n/** One advertised method, as the host generated it from its gate table. */\nexport interface ApiMethod {\n /** Catalog name, `protocol-` stripped — e.g. `spaces:invite`, `contribute:run`. */\n name: string;\n /** The capability this method requires (already held — it's in your catalog). */\n capability: string;\n /** True when the method STREAMS (use {@link invokeStream}) vs. single-reply. */\n stream?: boolean;\n /**\n * JSON Schema for the method's single object argument, when the host declares one.\n * Self-describes the call so a catalog-as-tools bridge (an embedded agent) can\n * advertise the real param shape — e.g. that `authoring:typecheck` takes a nested\n * `{ files: [{ path, content }] }` array — instead of a permissive \"any object\".\n * Absent for methods the host advertises without a schema.\n */\n paramsSchema?: Record<string, unknown>;\n}\n\n// `scheme:method` → ['scheme', 'method'] (the wire protocol is `protocol-scheme`).\nconst split = (name: string): [string, string] => {\n const i = name.indexOf(':');\n if (i <= 0) throw new Error(`invalid catalog method name: ${name}`);\n return [name.slice(0, i), name.slice(i + 1)];\n};\n\n/**\n * Call a catalog method by name — `invoke('spaces:invite', { spaceId, login, role })`.\n * A thin generic over the host protocol: the host validates params and gates the\n * call (an un-granted method → `forbidden`, even if you name it directly). For a\n * STREAMING method (`ApiMethod.stream`), use {@link invokeStream}.\n */\nexport const invoke = async <T = unknown>(\n name: string,\n params: Record<string, unknown> = {},\n): Promise<T> => {\n const [scheme, method] = split(name);\n // The host replies with an `{ ok, data } | { ok:false, code }` envelope; unwrap\n // it and THROW on refusal (a `.code` like `forbidden` for an off-catalog call)\n // so callers — and any agent driving `invoke` — see the gate's verdict.\n const res = (await protocolRequest(scheme, method, [params])) as\n | { ok: true; data: unknown }\n | { ok: false; code?: string; message?: string }\n | undefined;\n if (!res || res.ok !== true) {\n const err = new Error(res?.message ?? `${name} failed`) as Error & { code?: string };\n err.code = res?.code ?? 'unknown';\n throw err;\n }\n return res.data as T;\n};\n\n// Stream transport over the resolver (SDK_PACKAGING_SPEC §4) — sendMessage /\n// addListener route through `transport()` (injected bundler messageBus or the §4\n// global), never `bundler.messageBus` directly.\nconst streamTransport: StreamTransport = {\n send: (msg) => sendMessage(msg.type, msg as unknown as Record<string, unknown>),\n subscribe: (type, handler) =>\n addListener(type, (msg) => handler(msg as { msgId?: number; stream?: StreamFrame })),\n // Early-cancel: route a `{type, msgId, cancel:true}` frame back to the host so it\n // aborts the in-flight generation (and, for `llm:chat`, stops billing) — §3.3.\n cancel: (msg) => sendMessage(msg.type, msg as unknown as Record<string, unknown>),\n};\n\n/** Call a STREAMING catalog method by name, yielding its events. Pass `signal` to\n * abort mid-stream: the host stops generating and (for `llm:chat`) stops billing. */\nexport function invokeStream<T = unknown, R = unknown>(\n name: string,\n params: Record<string, unknown> = {},\n signal?: AbortSignal,\n): AsyncGenerator<T, R, void> {\n const [scheme, method] = split(name);\n return consumeStream<T, R>(streamTransport, `protocol-${scheme}`, method, [params], undefined, signal);\n}\n\n// The catalog list is read over the transport (§4): the host pushes `api-catalog`\n// and answers `request-api-catalog` with this app's grant-filtered methods (wire\n// format: site-main channelBridge.ts).\nconst channel = createPushChannel<ApiMethod[]>({\n pushType: API_CATALOG,\n requestType: REQUEST_API_CATALOG,\n initial: [],\n parse: (msg) => (Array.isArray(msg.methods) ? (msg.methods as ApiMethod[]) : undefined),\n});\n\n/** The methods this app may call (grant-filtered, §5.5). Poll for a one-off read;\n * use {@link onCatalogChange} / {@link useCatalog} to react. */\nexport const getCatalog = (): ApiMethod[] => channel.get();\n\n/** Subscribe to catalog changes (e.g. a grant added/revoked). Invoked immediately\n * with the current catalog, then on every change. Returns an unsubscribe fn. */\nexport const onCatalogChange = (listener: (catalog: ApiMethod[]) => void): (() => void) =>\n channel.onChange(listener);\n\n/** React hook returning this app's method catalog, re-rendering on change. Hand\n * it to an embedded agent as its tool list to confine the agent to the app's\n * authority (§5.9). */\nexport const useCatalog = (): ApiMethod[] => channel.use();\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAKA,0BAA0D;AAE1D,4BAA8B;AAC9B,yBAAkC;AAClC,sBAAiD;AAqBjD,MAAM,QAAQ,CAAC,SAAmC;AAChD,QAAM,IAAI,KAAK,QAAQ,GAAG;AAC1B,MAAI,KAAK,EAAG,OAAM,IAAI,MAAM,gCAAgC,IAAI,EAAE;AAClE,SAAO,CAAC,KAAK,MAAM,GAAG,CAAC,GAAG,KAAK,MAAM,IAAI,CAAC,CAAC;AAC7C;AAQO,MAAM,SAAS,OACpB,MACA,SAAkC,CAAC,MACpB;AACf,QAAM,CAAC,QAAQ,MAAM,IAAI,MAAM,IAAI;AAInC,QAAM,MAAO,UAAM,qCAAgB,QAAQ,QAAQ,CAAC,MAAM,CAAC;AAI3D,MAAI,CAAC,OAAO,IAAI,OAAO,MAAM;AAC3B,UAAM,MAAM,IAAI,MAAM,KAAK,WAAW,GAAG,IAAI,SAAS;AACtD,QAAI,OAAO,KAAK,QAAQ;AACxB,UAAM;AAAA,EACR;AACA,SAAO,IAAI;AACb;AAKA,MAAM,kBAAmC;AAAA,EACvC,MAAM,CAAC,YAAQ,iCAAY,IAAI,MAAM,GAAyC;AAAA,EAC9E,WAAW,CAAC,MAAM,gBAChB,iCAAY,MAAM,CAAC,QAAQ,QAAQ,GAA+C,CAAC;AAAA;AAAA;AAAA,EAGrF,QAAQ,CAAC,YAAQ,iCAAY,IAAI,MAAM,GAAyC;AAClF;AAIO,SAAS,aACd,MACA,SAAkC,CAAC,GACnC,QAC4B;AAC5B,QAAM,CAAC,QAAQ,MAAM,IAAI,MAAM,IAAI;AACnC,aAAO,qCAAoB,iBAAiB,YAAY,MAAM,IAAI,QAAQ,CAAC,MAAM,GAAG,QAAW,MAAM;AACvG;AAKA,MAAM,cAAU,sCAA+B;AAAA,EAC7C,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS,CAAC;AAAA,EACV,OAAO,CAAC,QAAS,MAAM,QAAQ,IAAI,OAAO,IAAK,IAAI,UAA0B;AAC/E,CAAC;AAIM,MAAM,aAAa,MAAmB,QAAQ,IAAI;AAIlD,MAAM,kBAAkB,CAAC,aAC9B,QAAQ,SAAS,QAAQ;AAKpB,MAAM,aAAa,MAAmB,QAAQ,IAAI;","names":[]}
|
package/dist/catalog.js
CHANGED
|
@@ -2,6 +2,7 @@ import "./chunk-VHAA22YE.js";
|
|
|
2
2
|
import { protocolRequest, sendMessage, addListener } from "./sandboxUtils";
|
|
3
3
|
import { consumeStream } from "./protocolStream";
|
|
4
4
|
import { createPushChannel } from "./pushChannel";
|
|
5
|
+
import { API_CATALOG, REQUEST_API_CATALOG } from "./generated/protocol";
|
|
5
6
|
const split = (name) => {
|
|
6
7
|
const i = name.indexOf(":");
|
|
7
8
|
if (i <= 0) throw new Error(`invalid catalog method name: ${name}`);
|
|
@@ -29,8 +30,8 @@ function invokeStream(name, params = {}, signal) {
|
|
|
29
30
|
return consumeStream(streamTransport, `protocol-${scheme}`, method, [params], void 0, signal);
|
|
30
31
|
}
|
|
31
32
|
const channel = createPushChannel({
|
|
32
|
-
pushType:
|
|
33
|
-
requestType:
|
|
33
|
+
pushType: API_CATALOG,
|
|
34
|
+
requestType: REQUEST_API_CATALOG,
|
|
34
35
|
initial: [],
|
|
35
36
|
parse: (msg) => Array.isArray(msg.methods) ? msg.methods : void 0
|
|
36
37
|
});
|
package/dist/catalog.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/catalog.ts"],"sourcesContent":["// The method catalog (UI_AS_APPS_SPEC §5.5) — the app's own grant-filtered RPC\n// surface, and a generic way to call it. The host advertises exactly the methods\n// this app may invoke (MCP-tool-shaped); `invoke()` calls one by its catalog name.\n// Handing the catalog to an embedded agent as its tool list confines the agent to\n// the app's authority (agent sandboxing falls out of the capability model, §5.9).\nimport { protocolRequest, sendMessage, addListener } from './sandboxUtils';\nimport type { StreamFrame, StreamTransport } from './protocolStream';\nimport { consumeStream } from './protocolStream';\nimport { createPushChannel } from './pushChannel';\n\n/** One advertised method, as the host generated it from its gate table. */\nexport interface ApiMethod {\n /** Catalog name, `protocol-` stripped — e.g. `spaces:invite`, `contribute:run`. */\n name: string;\n /** The capability this method requires (already held — it's in your catalog). */\n capability: string;\n /** True when the method STREAMS (use {@link invokeStream}) vs. single-reply. */\n stream?: boolean;\n /**\n * JSON Schema for the method's single object argument, when the host declares one.\n * Self-describes the call so a catalog-as-tools bridge (an embedded agent) can\n * advertise the real param shape — e.g. that `authoring:typecheck` takes a nested\n * `{ files: [{ path, content }] }` array — instead of a permissive \"any object\".\n * Absent for methods the host advertises without a schema.\n */\n paramsSchema?: Record<string, unknown>;\n}\n\n// `scheme:method` → ['scheme', 'method'] (the wire protocol is `protocol-scheme`).\nconst split = (name: string): [string, string] => {\n const i = name.indexOf(':');\n if (i <= 0) throw new Error(`invalid catalog method name: ${name}`);\n return [name.slice(0, i), name.slice(i + 1)];\n};\n\n/**\n * Call a catalog method by name — `invoke('spaces:invite', { spaceId, login, role })`.\n * A thin generic over the host protocol: the host validates params and gates the\n * call (an un-granted method → `forbidden`, even if you name it directly). For a\n * STREAMING method (`ApiMethod.stream`), use {@link invokeStream}.\n */\nexport const invoke = async <T = unknown>(\n name: string,\n params: Record<string, unknown> = {},\n): Promise<T> => {\n const [scheme, method] = split(name);\n // The host replies with an `{ ok, data } | { ok:false, code }` envelope; unwrap\n // it and THROW on refusal (a `.code` like `forbidden` for an off-catalog call)\n // so callers — and any agent driving `invoke` — see the gate's verdict.\n const res = (await protocolRequest(scheme, method, [params])) as\n | { ok: true; data: unknown }\n | { ok: false; code?: string; message?: string }\n | undefined;\n if (!res || res.ok !== true) {\n const err = new Error(res?.message ?? `${name} failed`) as Error & { code?: string };\n err.code = res?.code ?? 'unknown';\n throw err;\n }\n return res.data as T;\n};\n\n// Stream transport over the resolver (SDK_PACKAGING_SPEC §4) — sendMessage /\n// addListener route through `transport()` (injected bundler messageBus or the §4\n// global), never `bundler.messageBus` directly.\nconst streamTransport: StreamTransport = {\n send: (msg) => sendMessage(msg.type, msg as unknown as Record<string, unknown>),\n subscribe: (type, handler) =>\n addListener(type, (msg) => handler(msg as { msgId?: number; stream?: StreamFrame })),\n // Early-cancel: route a `{type, msgId, cancel:true}` frame back to the host so it\n // aborts the in-flight generation (and, for `llm:chat`, stops billing) — §3.3.\n cancel: (msg) => sendMessage(msg.type, msg as unknown as Record<string, unknown>),\n};\n\n/** Call a STREAMING catalog method by name, yielding its events. Pass `signal` to\n * abort mid-stream: the host stops generating and (for `llm:chat`) stops billing. */\nexport function invokeStream<T = unknown, R = unknown>(\n name: string,\n params: Record<string, unknown> = {},\n signal?: AbortSignal,\n): AsyncGenerator<T, R, void> {\n const [scheme, method] = split(name);\n return consumeStream<T, R>(streamTransport, `protocol-${scheme}`, method, [params], undefined, signal);\n}\n\n// The catalog list is read over the transport (§4): the host pushes `api-catalog`\n// and answers `request-api-catalog` with this app's grant-filtered methods (wire\n// format: site-main channelBridge.ts).\nconst channel = createPushChannel<ApiMethod[]>({\n pushType:
|
|
1
|
+
{"version":3,"sources":["../src/catalog.ts"],"sourcesContent":["// The method catalog (UI_AS_APPS_SPEC §5.5) — the app's own grant-filtered RPC\n// surface, and a generic way to call it. The host advertises exactly the methods\n// this app may invoke (MCP-tool-shaped); `invoke()` calls one by its catalog name.\n// Handing the catalog to an embedded agent as its tool list confines the agent to\n// the app's authority (agent sandboxing falls out of the capability model, §5.9).\nimport { protocolRequest, sendMessage, addListener } from './sandboxUtils';\nimport type { StreamFrame, StreamTransport } from './protocolStream';\nimport { consumeStream } from './protocolStream';\nimport { createPushChannel } from './pushChannel';\nimport { API_CATALOG, REQUEST_API_CATALOG } from './generated/protocol';\n\n/** One advertised method, as the host generated it from its gate table. */\nexport interface ApiMethod {\n /** Catalog name, `protocol-` stripped — e.g. `spaces:invite`, `contribute:run`. */\n name: string;\n /** The capability this method requires (already held — it's in your catalog). */\n capability: string;\n /** True when the method STREAMS (use {@link invokeStream}) vs. single-reply. */\n stream?: boolean;\n /**\n * JSON Schema for the method's single object argument, when the host declares one.\n * Self-describes the call so a catalog-as-tools bridge (an embedded agent) can\n * advertise the real param shape — e.g. that `authoring:typecheck` takes a nested\n * `{ files: [{ path, content }] }` array — instead of a permissive \"any object\".\n * Absent for methods the host advertises without a schema.\n */\n paramsSchema?: Record<string, unknown>;\n}\n\n// `scheme:method` → ['scheme', 'method'] (the wire protocol is `protocol-scheme`).\nconst split = (name: string): [string, string] => {\n const i = name.indexOf(':');\n if (i <= 0) throw new Error(`invalid catalog method name: ${name}`);\n return [name.slice(0, i), name.slice(i + 1)];\n};\n\n/**\n * Call a catalog method by name — `invoke('spaces:invite', { spaceId, login, role })`.\n * A thin generic over the host protocol: the host validates params and gates the\n * call (an un-granted method → `forbidden`, even if you name it directly). For a\n * STREAMING method (`ApiMethod.stream`), use {@link invokeStream}.\n */\nexport const invoke = async <T = unknown>(\n name: string,\n params: Record<string, unknown> = {},\n): Promise<T> => {\n const [scheme, method] = split(name);\n // The host replies with an `{ ok, data } | { ok:false, code }` envelope; unwrap\n // it and THROW on refusal (a `.code` like `forbidden` for an off-catalog call)\n // so callers — and any agent driving `invoke` — see the gate's verdict.\n const res = (await protocolRequest(scheme, method, [params])) as\n | { ok: true; data: unknown }\n | { ok: false; code?: string; message?: string }\n | undefined;\n if (!res || res.ok !== true) {\n const err = new Error(res?.message ?? `${name} failed`) as Error & { code?: string };\n err.code = res?.code ?? 'unknown';\n throw err;\n }\n return res.data as T;\n};\n\n// Stream transport over the resolver (SDK_PACKAGING_SPEC §4) — sendMessage /\n// addListener route through `transport()` (injected bundler messageBus or the §4\n// global), never `bundler.messageBus` directly.\nconst streamTransport: StreamTransport = {\n send: (msg) => sendMessage(msg.type, msg as unknown as Record<string, unknown>),\n subscribe: (type, handler) =>\n addListener(type, (msg) => handler(msg as { msgId?: number; stream?: StreamFrame })),\n // Early-cancel: route a `{type, msgId, cancel:true}` frame back to the host so it\n // aborts the in-flight generation (and, for `llm:chat`, stops billing) — §3.3.\n cancel: (msg) => sendMessage(msg.type, msg as unknown as Record<string, unknown>),\n};\n\n/** Call a STREAMING catalog method by name, yielding its events. Pass `signal` to\n * abort mid-stream: the host stops generating and (for `llm:chat`) stops billing. */\nexport function invokeStream<T = unknown, R = unknown>(\n name: string,\n params: Record<string, unknown> = {},\n signal?: AbortSignal,\n): AsyncGenerator<T, R, void> {\n const [scheme, method] = split(name);\n return consumeStream<T, R>(streamTransport, `protocol-${scheme}`, method, [params], undefined, signal);\n}\n\n// The catalog list is read over the transport (§4): the host pushes `api-catalog`\n// and answers `request-api-catalog` with this app's grant-filtered methods (wire\n// format: site-main channelBridge.ts).\nconst channel = createPushChannel<ApiMethod[]>({\n pushType: API_CATALOG,\n requestType: REQUEST_API_CATALOG,\n initial: [],\n parse: (msg) => (Array.isArray(msg.methods) ? (msg.methods as ApiMethod[]) : undefined),\n});\n\n/** The methods this app may call (grant-filtered, §5.5). Poll for a one-off read;\n * use {@link onCatalogChange} / {@link useCatalog} to react. */\nexport const getCatalog = (): ApiMethod[] => channel.get();\n\n/** Subscribe to catalog changes (e.g. a grant added/revoked). Invoked immediately\n * with the current catalog, then on every change. Returns an unsubscribe fn. */\nexport const onCatalogChange = (listener: (catalog: ApiMethod[]) => void): (() => void) =>\n channel.onChange(listener);\n\n/** React hook returning this app's method catalog, re-rendering on change. Hand\n * it to an embedded agent as its tool list to confine the agent to the app's\n * authority (§5.9). */\nexport const useCatalog = (): ApiMethod[] => channel.use();\n"],"mappings":";AAKA,SAAS,iBAAiB,aAAa,mBAAmB;AAE1D,SAAS,qBAAqB;AAC9B,SAAS,yBAAyB;AAClC,SAAS,aAAa,2BAA2B;AAqBjD,MAAM,QAAQ,CAAC,SAAmC;AAChD,QAAM,IAAI,KAAK,QAAQ,GAAG;AAC1B,MAAI,KAAK,EAAG,OAAM,IAAI,MAAM,gCAAgC,IAAI,EAAE;AAClE,SAAO,CAAC,KAAK,MAAM,GAAG,CAAC,GAAG,KAAK,MAAM,IAAI,CAAC,CAAC;AAC7C;AAQO,MAAM,SAAS,OACpB,MACA,SAAkC,CAAC,MACpB;AACf,QAAM,CAAC,QAAQ,MAAM,IAAI,MAAM,IAAI;AAInC,QAAM,MAAO,MAAM,gBAAgB,QAAQ,QAAQ,CAAC,MAAM,CAAC;AAI3D,MAAI,CAAC,OAAO,IAAI,OAAO,MAAM;AAC3B,UAAM,MAAM,IAAI,MAAM,KAAK,WAAW,GAAG,IAAI,SAAS;AACtD,QAAI,OAAO,KAAK,QAAQ;AACxB,UAAM;AAAA,EACR;AACA,SAAO,IAAI;AACb;AAKA,MAAM,kBAAmC;AAAA,EACvC,MAAM,CAAC,QAAQ,YAAY,IAAI,MAAM,GAAyC;AAAA,EAC9E,WAAW,CAAC,MAAM,YAChB,YAAY,MAAM,CAAC,QAAQ,QAAQ,GAA+C,CAAC;AAAA;AAAA;AAAA,EAGrF,QAAQ,CAAC,QAAQ,YAAY,IAAI,MAAM,GAAyC;AAClF;AAIO,SAAS,aACd,MACA,SAAkC,CAAC,GACnC,QAC4B;AAC5B,QAAM,CAAC,QAAQ,MAAM,IAAI,MAAM,IAAI;AACnC,SAAO,cAAoB,iBAAiB,YAAY,MAAM,IAAI,QAAQ,CAAC,MAAM,GAAG,QAAW,MAAM;AACvG;AAKA,MAAM,UAAU,kBAA+B;AAAA,EAC7C,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS,CAAC;AAAA,EACV,OAAO,CAAC,QAAS,MAAM,QAAQ,IAAI,OAAO,IAAK,IAAI,UAA0B;AAC/E,CAAC;AAIM,MAAM,aAAa,MAAmB,QAAQ,IAAI;AAIlD,MAAM,kBAAkB,CAAC,aAC9B,QAAQ,SAAS,QAAQ;AAKpB,MAAM,aAAa,MAAmB,QAAQ,IAAI;","names":[]}
|
|
@@ -27,7 +27,10 @@ __export(MDXComponents_exports, {
|
|
|
27
27
|
});
|
|
28
28
|
module.exports = __toCommonJS(MDXComponents_exports);
|
|
29
29
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
30
|
+
var import_react = require("react");
|
|
30
31
|
var import_Admonition = require("./Admonition");
|
|
32
|
+
var import_linkSpace = require("../linkSpace");
|
|
33
|
+
var import_urlUtils = require("../urlUtils");
|
|
31
34
|
var import_HeadingAnchor = require("./HeadingAnchor");
|
|
32
35
|
var import_Link = require("./Link");
|
|
33
36
|
var import_WikiLink = require("./WikiLink");
|
|
@@ -41,7 +44,17 @@ const DEFAULT_MDX_COMPONENTS = {
|
|
|
41
44
|
children,
|
|
42
45
|
...properties
|
|
43
46
|
}) {
|
|
44
|
-
|
|
47
|
+
const { corpusRoot } = (0, import_react.use)(import_linkSpace.LinkSpaceContext);
|
|
48
|
+
let mapped = href;
|
|
49
|
+
if (href && (href.startsWith(import_linkSpace.FS_PREFIX) || corpusRoot !== null && href.startsWith("/"))) {
|
|
50
|
+
const [pathPart, frag] = (0, import_urlUtils.splitHash)(href);
|
|
51
|
+
const resolution = (0, import_linkSpace.resolveLinkTarget)(pathPart, { corpusRoot });
|
|
52
|
+
if (resolution.state !== "resolved") {
|
|
53
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "ir-link-broken", "data-state": "broken", title: `Invalid ${import_linkSpace.FS_PREFIX} link: ${href}`, children });
|
|
54
|
+
}
|
|
55
|
+
mapped = `${resolution.path}${frag ? `#${frag}` : ""}`;
|
|
56
|
+
}
|
|
57
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Link.Link, { href: mapped, ...properties, children });
|
|
45
58
|
},
|
|
46
59
|
Admonition: import_Admonition.Admonition,
|
|
47
60
|
HeadingAnchor: import_HeadingAnchor.HeadingAnchor,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/MDXComponents.tsx"],"sourcesContent":["import { ReactNode } from 'react';\nimport { Admonition } from './Admonition';\nimport { HeadingAnchor } from './HeadingAnchor';\nimport { Link } from './Link';\nimport { WikiLink } from './WikiLink';\n\n// The link primitives moved to ./Link so WikiLink can reuse Link without a\n// MDXComponents ↔ WikiLink import cycle (check:circular). Re-exported here so the\n// public `Link` / `InternalLink` entry points are unchanged.\nexport { InternalLink, Link } from './Link';\nexport { Admonition } from './Admonition';\nexport type { AdmonitionType } from './Admonition';\nexport { HeadingAnchor } from './HeadingAnchor';\nexport { WikiLink } from './WikiLink';\n\n/** Default MDX component overrides passed to {@link MDXProvider} by `boot`. These\n * are the platform's *phantom defaults* (MARKDOWN_SYNTAX_SPEC §11.2): they are\n * always present in the provider — even for a plain-markdown repo that never\n * calls `boot({ mdxComponents })` — so the platform-emitted `Admonition` (§12)\n * and `WikiLink` (§13) components resolve without the MDX missing-reference guard\n * firing, and Markdown links route in-app via {@link Link}. An app overrides any\n * of them by name via `boot({ mdxComponents })`, which *merges* over these\n * defaults (§11.3) — overriding `WikiLink` alone still keeps `a` and `Admonition`. */\nexport const DEFAULT_MDX_COMPONENTS = {\n a({\n href,\n children,\n ...properties\n }: React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>) {\n return (\n <Link href={
|
|
1
|
+
{"version":3,"sources":["../../src/components/MDXComponents.tsx"],"sourcesContent":["import { ReactNode, use } from 'react';\nimport { Admonition } from './Admonition';\nimport { FS_PREFIX, LinkSpaceContext, normalizeAbsolute, resolveLinkTarget } from '../linkSpace';\nimport { splitHash } from '../urlUtils';\nimport { HeadingAnchor } from './HeadingAnchor';\nimport { Link } from './Link';\nimport { WikiLink } from './WikiLink';\n\n// The link primitives moved to ./Link so WikiLink can reuse Link without a\n// MDXComponents ↔ WikiLink import cycle (check:circular). Re-exported here so the\n// public `Link` / `InternalLink` entry points are unchanged.\nexport { InternalLink, Link } from './Link';\nexport { Admonition } from './Admonition';\nexport type { AdmonitionType } from './Admonition';\nexport { HeadingAnchor } from './HeadingAnchor';\nexport { WikiLink } from './WikiLink';\n\n/** Default MDX component overrides passed to {@link MDXProvider} by `boot`. These\n * are the platform's *phantom defaults* (MARKDOWN_SYNTAX_SPEC §11.2): they are\n * always present in the provider — even for a plain-markdown repo that never\n * calls `boot({ mdxComponents })` — so the platform-emitted `Admonition` (§12)\n * and `WikiLink` (§13) components resolve without the MDX missing-reference guard\n * firing, and Markdown links route in-app via {@link Link}. An app overrides any\n * of them by name via `boot({ mdxComponents })`, which *merges* over these\n * defaults (§11.3) — overriding `WikiLink` alone still keeps `a` and `Admonition`. */\nexport const DEFAULT_MDX_COMPONENTS = {\n a({\n href,\n children,\n ...properties\n }: React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>) {\n // R3-273 link spaces, same shared resolver as WikiLink: an `$fs:` href is\n // translated to its mount-absolute path; an ABSOLUTE href is corpus-rooted\n // when an enclosing LinkSpaceContext declares a corpusRoot (a non-corpus app\n // declares none and is untouched). Relative and external hrefs pass through —\n // <Link> already routes same-app hrefs and renders the rest as plain anchors.\n const { corpusRoot } = use(LinkSpaceContext);\n let mapped = href;\n if (href && (href.startsWith(FS_PREFIX) || (corpusRoot !== null && href.startsWith('/')))) {\n const [pathPart, frag] = splitHash(href);\n const resolution = resolveLinkTarget(pathPart, { corpusRoot });\n if (resolution.state !== 'resolved') {\n // Malformed `$fs:` (incl. scheme smuggling) — broken text, never an anchor.\n return (\n <span className=\"ir-link-broken\" data-state=\"broken\" title={`Invalid ${FS_PREFIX} link: ${href}`}>\n {children}\n </span>\n );\n }\n mapped = `${resolution.path}${frag ? `#${frag}` : ''}`;\n }\n return (\n <Link href={mapped} {...properties}>\n {children}\n </Link>\n );\n },\n Admonition,\n HeadingAnchor,\n WikiLink,\n} as Record<string, (props: any) => ReactNode>;\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA4CU;AA5CV,mBAA+B;AAC/B,wBAA2B;AAC3B,uBAAkF;AAClF,sBAA0B;AAC1B,2BAA8B;AAC9B,kBAAqB;AACrB,sBAAyB;AAKzB,IAAAA,eAAmC;AACnC,IAAAC,qBAA2B;AAE3B,IAAAC,wBAA8B;AAC9B,IAAAC,mBAAyB;AAUlB,MAAM,yBAAyB;AAAA,EACpC,EAAE;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACL,GAA8F;AAM5F,UAAM,EAAE,WAAW,QAAI,kBAAI,iCAAgB;AAC3C,QAAI,SAAS;AACb,QAAI,SAAS,KAAK,WAAW,0BAAS,KAAM,eAAe,QAAQ,KAAK,WAAW,GAAG,IAAK;AACzF,YAAM,CAAC,UAAU,IAAI,QAAI,2BAAU,IAAI;AACvC,YAAM,iBAAa,oCAAkB,UAAU,EAAE,WAAW,CAAC;AAC7D,UAAI,WAAW,UAAU,YAAY;AAEnC,eACE,4CAAC,UAAK,WAAU,kBAAiB,cAAW,UAAS,OAAO,WAAW,0BAAS,UAAU,IAAI,IAC3F,UACH;AAAA,MAEJ;AACA,eAAS,GAAG,WAAW,IAAI,GAAG,OAAO,IAAI,IAAI,KAAK,EAAE;AAAA,IACtD;AACA,WACE,4CAAC,oBAAK,MAAM,QAAS,GAAG,YACrB,UACH;AAAA,EAEJ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;","names":["import_Link","import_Admonition","import_HeadingAnchor","import_WikiLink"]}
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import "../chunk-VHAA22YE.js";
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
|
+
import { use } from "react";
|
|
3
4
|
import { Admonition } from "./Admonition";
|
|
5
|
+
import { FS_PREFIX, LinkSpaceContext, resolveLinkTarget } from "../linkSpace";
|
|
6
|
+
import { splitHash } from "../urlUtils";
|
|
4
7
|
import { HeadingAnchor } from "./HeadingAnchor";
|
|
5
8
|
import { Link } from "./Link";
|
|
6
9
|
import { WikiLink } from "./WikiLink";
|
|
@@ -14,7 +17,17 @@ const DEFAULT_MDX_COMPONENTS = {
|
|
|
14
17
|
children,
|
|
15
18
|
...properties
|
|
16
19
|
}) {
|
|
17
|
-
|
|
20
|
+
const { corpusRoot } = use(LinkSpaceContext);
|
|
21
|
+
let mapped = href;
|
|
22
|
+
if (href && (href.startsWith(FS_PREFIX) || corpusRoot !== null && href.startsWith("/"))) {
|
|
23
|
+
const [pathPart, frag] = splitHash(href);
|
|
24
|
+
const resolution = resolveLinkTarget(pathPart, { corpusRoot });
|
|
25
|
+
if (resolution.state !== "resolved") {
|
|
26
|
+
return /* @__PURE__ */ jsx("span", { className: "ir-link-broken", "data-state": "broken", title: `Invalid ${FS_PREFIX} link: ${href}`, children });
|
|
27
|
+
}
|
|
28
|
+
mapped = `${resolution.path}${frag ? `#${frag}` : ""}`;
|
|
29
|
+
}
|
|
30
|
+
return /* @__PURE__ */ jsx(Link, { href: mapped, ...properties, children });
|
|
18
31
|
},
|
|
19
32
|
Admonition,
|
|
20
33
|
HeadingAnchor,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/MDXComponents.tsx"],"sourcesContent":["import { ReactNode } from 'react';\nimport { Admonition } from './Admonition';\nimport { HeadingAnchor } from './HeadingAnchor';\nimport { Link } from './Link';\nimport { WikiLink } from './WikiLink';\n\n// The link primitives moved to ./Link so WikiLink can reuse Link without a\n// MDXComponents ↔ WikiLink import cycle (check:circular). Re-exported here so the\n// public `Link` / `InternalLink` entry points are unchanged.\nexport { InternalLink, Link } from './Link';\nexport { Admonition } from './Admonition';\nexport type { AdmonitionType } from './Admonition';\nexport { HeadingAnchor } from './HeadingAnchor';\nexport { WikiLink } from './WikiLink';\n\n/** Default MDX component overrides passed to {@link MDXProvider} by `boot`. These\n * are the platform's *phantom defaults* (MARKDOWN_SYNTAX_SPEC §11.2): they are\n * always present in the provider — even for a plain-markdown repo that never\n * calls `boot({ mdxComponents })` — so the platform-emitted `Admonition` (§12)\n * and `WikiLink` (§13) components resolve without the MDX missing-reference guard\n * firing, and Markdown links route in-app via {@link Link}. An app overrides any\n * of them by name via `boot({ mdxComponents })`, which *merges* over these\n * defaults (§11.3) — overriding `WikiLink` alone still keeps `a` and `Admonition`. */\nexport const DEFAULT_MDX_COMPONENTS = {\n a({\n href,\n children,\n ...properties\n }: React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>) {\n return (\n <Link href={
|
|
1
|
+
{"version":3,"sources":["../../src/components/MDXComponents.tsx"],"sourcesContent":["import { ReactNode, use } from 'react';\nimport { Admonition } from './Admonition';\nimport { FS_PREFIX, LinkSpaceContext, normalizeAbsolute, resolveLinkTarget } from '../linkSpace';\nimport { splitHash } from '../urlUtils';\nimport { HeadingAnchor } from './HeadingAnchor';\nimport { Link } from './Link';\nimport { WikiLink } from './WikiLink';\n\n// The link primitives moved to ./Link so WikiLink can reuse Link without a\n// MDXComponents ↔ WikiLink import cycle (check:circular). Re-exported here so the\n// public `Link` / `InternalLink` entry points are unchanged.\nexport { InternalLink, Link } from './Link';\nexport { Admonition } from './Admonition';\nexport type { AdmonitionType } from './Admonition';\nexport { HeadingAnchor } from './HeadingAnchor';\nexport { WikiLink } from './WikiLink';\n\n/** Default MDX component overrides passed to {@link MDXProvider} by `boot`. These\n * are the platform's *phantom defaults* (MARKDOWN_SYNTAX_SPEC §11.2): they are\n * always present in the provider — even for a plain-markdown repo that never\n * calls `boot({ mdxComponents })` — so the platform-emitted `Admonition` (§12)\n * and `WikiLink` (§13) components resolve without the MDX missing-reference guard\n * firing, and Markdown links route in-app via {@link Link}. An app overrides any\n * of them by name via `boot({ mdxComponents })`, which *merges* over these\n * defaults (§11.3) — overriding `WikiLink` alone still keeps `a` and `Admonition`. */\nexport const DEFAULT_MDX_COMPONENTS = {\n a({\n href,\n children,\n ...properties\n }: React.DetailedHTMLProps<React.AnchorHTMLAttributes<HTMLAnchorElement>, HTMLAnchorElement>) {\n // R3-273 link spaces, same shared resolver as WikiLink: an `$fs:` href is\n // translated to its mount-absolute path; an ABSOLUTE href is corpus-rooted\n // when an enclosing LinkSpaceContext declares a corpusRoot (a non-corpus app\n // declares none and is untouched). Relative and external hrefs pass through —\n // <Link> already routes same-app hrefs and renders the rest as plain anchors.\n const { corpusRoot } = use(LinkSpaceContext);\n let mapped = href;\n if (href && (href.startsWith(FS_PREFIX) || (corpusRoot !== null && href.startsWith('/')))) {\n const [pathPart, frag] = splitHash(href);\n const resolution = resolveLinkTarget(pathPart, { corpusRoot });\n if (resolution.state !== 'resolved') {\n // Malformed `$fs:` (incl. scheme smuggling) — broken text, never an anchor.\n return (\n <span className=\"ir-link-broken\" data-state=\"broken\" title={`Invalid ${FS_PREFIX} link: ${href}`}>\n {children}\n </span>\n );\n }\n mapped = `${resolution.path}${frag ? `#${frag}` : ''}`;\n }\n return (\n <Link href={mapped} {...properties}>\n {children}\n </Link>\n );\n },\n Admonition,\n HeadingAnchor,\n WikiLink,\n} as Record<string, (props: any) => ReactNode>;\n"],"mappings":";AA4CU;AA5CV,SAAoB,WAAW;AAC/B,SAAS,kBAAkB;AAC3B,SAAS,WAAW,kBAAqC,yBAAyB;AAClF,SAAS,iBAAiB;AAC1B,SAAS,qBAAqB;AAC9B,SAAS,YAAY;AACrB,SAAS,gBAAgB;AAKzB,SAAS,cAAc,QAAAA,aAAY;AACnC,SAAS,cAAAC,mBAAkB;AAE3B,SAAS,iBAAAC,sBAAqB;AAC9B,SAAS,YAAAC,iBAAgB;AAUlB,MAAM,yBAAyB;AAAA,EACpC,EAAE;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACL,GAA8F;AAM5F,UAAM,EAAE,WAAW,IAAI,IAAI,gBAAgB;AAC3C,QAAI,SAAS;AACb,QAAI,SAAS,KAAK,WAAW,SAAS,KAAM,eAAe,QAAQ,KAAK,WAAW,GAAG,IAAK;AACzF,YAAM,CAAC,UAAU,IAAI,IAAI,UAAU,IAAI;AACvC,YAAM,aAAa,kBAAkB,UAAU,EAAE,WAAW,CAAC;AAC7D,UAAI,WAAW,UAAU,YAAY;AAEnC,eACE,oBAAC,UAAK,WAAU,kBAAiB,cAAW,UAAS,OAAO,WAAW,SAAS,UAAU,IAAI,IAC3F,UACH;AAAA,MAEJ;AACA,eAAS,GAAG,WAAW,IAAI,GAAG,OAAO,IAAI,IAAI,KAAK,EAAE;AAAA,IACtD;AACA,WACE,oBAAC,QAAK,MAAM,QAAS,GAAG,YACrB,UACH;AAAA,EAEJ;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF;","names":["Link","Admonition","HeadingAnchor","WikiLink"]}
|
|
@@ -27,25 +27,11 @@ var import_Link = require("./Link");
|
|
|
27
27
|
var import_Include = require("./Include");
|
|
28
28
|
var import_TinkerableContext = require("../TinkerableContext");
|
|
29
29
|
var import_urlUtils = require("../urlUtils");
|
|
30
|
+
var import_linkSpace = require("../linkSpace");
|
|
30
31
|
const labelFromTarget = (target) => {
|
|
31
32
|
const base = target.split(/[\\/]/).pop() ?? target;
|
|
32
33
|
return base.replace(/\.mdx?$/i, "") || target;
|
|
33
34
|
};
|
|
34
|
-
const normalize = (path) => {
|
|
35
|
-
const out = [];
|
|
36
|
-
for (const seg of path.split("/")) {
|
|
37
|
-
if (seg === "" || seg === ".") continue;
|
|
38
|
-
if (seg === "..") out.pop();
|
|
39
|
-
else out.push(seg);
|
|
40
|
-
}
|
|
41
|
-
return "/" + out.join("/");
|
|
42
|
-
};
|
|
43
|
-
const resolveWikiTarget = (target, currentFile) => {
|
|
44
|
-
if (target.startsWith("/")) return normalize(target);
|
|
45
|
-
if (!currentFile) return void 0;
|
|
46
|
-
const dir = currentFile.slice(0, currentFile.lastIndexOf("/"));
|
|
47
|
-
return normalize(`${dir}/${target}`);
|
|
48
|
-
};
|
|
49
35
|
const WikiLink = ({
|
|
50
36
|
target,
|
|
51
37
|
label,
|
|
@@ -53,6 +39,7 @@ const WikiLink = ({
|
|
|
53
39
|
...rest
|
|
54
40
|
}) => {
|
|
55
41
|
const { filesMetadata } = (0, import_react.use)(import_TinkerableContext.TinkerableContext);
|
|
42
|
+
const { corpusRoot } = (0, import_react.use)(import_linkSpace.LinkSpaceContext);
|
|
56
43
|
const renderContext = (0, import_react.use)(import_Include.RenderExportedComponentContext);
|
|
57
44
|
const currentFile = renderContext?.evaluationContext?.evaluation?.module?.filepath;
|
|
58
45
|
const rawTarget = target ?? "";
|
|
@@ -64,7 +51,20 @@ const WikiLink = ({
|
|
|
64
51
|
if (pathPart === "" && frag) {
|
|
65
52
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Link.Link, { href: `#${frag}`, className: "ir-wikilink", "data-state": "anchor", ...rest, children: text });
|
|
66
53
|
}
|
|
67
|
-
const
|
|
54
|
+
const resolution = (0, import_linkSpace.resolveLinkTarget)(pathPart, { currentFile, corpusRoot });
|
|
55
|
+
if (resolution.state === "invalid") {
|
|
56
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
57
|
+
"span",
|
|
58
|
+
{
|
|
59
|
+
className: "ir-wikilink ir-wikilink-broken",
|
|
60
|
+
"data-state": "broken",
|
|
61
|
+
title: `Invalid ${import_linkSpace.FS_PREFIX} target: ${pathPart}`,
|
|
62
|
+
...rest,
|
|
63
|
+
children: text
|
|
64
|
+
}
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
const resolved = resolution.state === "resolved" ? resolution.path : void 0;
|
|
68
68
|
const files = filesMetadata ?? {};
|
|
69
69
|
const loaded = Object.keys(files).length > 0;
|
|
70
70
|
if (resolved !== void 0) {
|
|
@@ -88,7 +88,9 @@ const WikiLink = ({
|
|
|
88
88
|
);
|
|
89
89
|
}
|
|
90
90
|
}
|
|
91
|
-
|
|
91
|
+
const translated = pathPart.startsWith(import_linkSpace.FS_PREFIX) || corpusRoot !== null && pathPart.startsWith("/");
|
|
92
|
+
const href = translated && resolved !== void 0 ? `${resolved}${frag ? `#${frag}` : ""}` : rawTarget;
|
|
93
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_Link.Link, { href, className: "ir-wikilink", "data-state": "resolved", ...rest, children: text });
|
|
92
94
|
};
|
|
93
95
|
// Annotate the CommonJS export names for ESM import in node:
|
|
94
96
|
0 && (module.exports = {
|