@immediately-run/sdk 0.15.0 → 0.16.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/README.md +27 -3
- package/dist/MDXProvider.cjs.map +1 -1
- package/dist/MDXProvider.d.cts +4 -0
- package/dist/MDXProvider.d.ts +4 -0
- package/dist/MDXProvider.js.map +1 -1
- package/dist/RoutingSpec.cjs.map +1 -1
- package/dist/RoutingSpec.d.cts +20 -3
- package/dist/RoutingSpec.d.ts +20 -3
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.d.cts +2 -0
- package/dist/auth.d.ts +2 -0
- package/dist/auth.js.map +1 -1
- package/dist/boot.cjs +17 -7
- package/dist/boot.cjs.map +1 -1
- package/dist/boot.d.cts +28 -4
- package/dist/boot.d.ts +28 -4
- package/dist/boot.js +16 -7
- package/dist/boot.js.map +1 -1
- package/dist/components/Include.cjs.map +1 -1
- package/dist/components/Include.d.cts +7 -0
- package/dist/components/Include.d.ts +7 -0
- package/dist/components/Include.js.map +1 -1
- package/dist/components/MDXComponents.cjs.map +1 -1
- package/dist/components/MDXComponents.d.cts +6 -0
- package/dist/components/MDXComponents.d.ts +6 -0
- package/dist/components/MDXComponents.js.map +1 -1
- package/dist/components/Routes.cjs +59 -0
- package/dist/components/Routes.cjs.map +1 -0
- package/dist/components/Routes.d.cts +34 -0
- package/dist/components/Routes.d.ts +34 -0
- package/dist/components/Routes.js +34 -0
- package/dist/components/Routes.js.map +1 -0
- package/dist/contribute.cjs.map +1 -1
- package/dist/contribute.d.cts +2 -0
- package/dist/contribute.d.ts +2 -0
- package/dist/contribute.js.map +1 -1
- package/dist/diagnostics.cjs.map +1 -1
- package/dist/diagnostics.d.cts +3 -0
- package/dist/diagnostics.d.ts +3 -0
- package/dist/diagnostics.js.map +1 -1
- package/dist/formFactor.cjs.map +1 -1
- package/dist/formFactor.d.cts +2 -0
- package/dist/formFactor.d.ts +2 -0
- package/dist/formFactor.js.map +1 -1
- package/dist/hooks.cjs +27 -28
- package/dist/hooks.cjs.map +1 -1
- package/dist/hooks.d.cts +39 -4
- package/dist/hooks.d.ts +39 -4
- package/dist/hooks.js +27 -29
- 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 +6 -4
- package/dist/index.d.ts +6 -4
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/irMarkers.cjs.map +1 -1
- package/dist/irMarkers.d.cts +1 -0
- package/dist/irMarkers.d.ts +1 -0
- package/dist/irMarkers.js.map +1 -1
- package/dist/llm.cjs.map +1 -1
- package/dist/llm.d.cts +5 -0
- package/dist/llm.d.ts +5 -0
- package/dist/llm.js.map +1 -1
- package/dist/loading.cjs +186 -0
- package/dist/loading.cjs.map +1 -0
- package/dist/loading.d.cts +48 -0
- package/dist/loading.d.ts +48 -0
- package/dist/loading.js +162 -0
- package/dist/loading.js.map +1 -0
- package/dist/mounts.cjs.map +1 -1
- package/dist/mounts.d.cts +3 -1
- package/dist/mounts.d.ts +3 -1
- package/dist/mounts.js.map +1 -1
- package/dist/netFetch.cjs.map +1 -1
- package/dist/netFetch.d.cts +2 -0
- package/dist/netFetch.d.ts +2 -0
- package/dist/netFetch.js.map +1 -1
- package/dist/onFsChange.cjs.map +1 -1
- package/dist/onFsChange.d.cts +1 -0
- package/dist/onFsChange.d.ts +1 -0
- package/dist/onFsChange.js.map +1 -1
- package/dist/protocolStream.cjs.map +1 -1
- package/dist/protocolStream.d.cts +3 -0
- package/dist/protocolStream.d.ts +3 -0
- package/dist/protocolStream.js.map +1 -1
- package/dist/ready.cjs.map +1 -1
- package/dist/ready.d.cts +7 -0
- package/dist/ready.d.ts +7 -0
- package/dist/ready.js.map +1 -1
- package/dist/routeMatch.cjs +72 -0
- package/dist/routeMatch.cjs.map +1 -0
- package/dist/routeMatch.d.cts +19 -0
- package/dist/routeMatch.d.ts +19 -0
- package/dist/routeMatch.js +46 -0
- package/dist/routeMatch.js.map +1 -0
- package/dist/routing.cjs +35 -14
- package/dist/routing.cjs.map +1 -1
- package/dist/routing.d.cts +33 -4
- package/dist/routing.d.ts +33 -4
- package/dist/routing.js +32 -14
- package/dist/routing.js.map +1 -1
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.d.cts +1 -0
- package/dist/runtime.d.ts +1 -0
- package/dist/runtime.js.map +1 -1
- package/dist/sandboxTypes.cjs.map +1 -1
- package/dist/sandboxTypes.d.cts +30 -7
- package/dist/sandboxTypes.d.ts +30 -7
- 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 +6 -2
package/dist/hooks.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks.ts"],"sourcesContent":["import { use,
|
|
1
|
+
{"version":3,"sources":["../src/hooks.ts"],"sourcesContent":["import { use, useMemo, useRef } from 'react';\nimport { TinkerableContext } from './TinkerableContext';\nimport {\n FilesMetadata,\n Metadata,\n MetadataQueryEntry,\n MetadataQueryFunction,\n MetadataQueryResult,\n} from './sandboxTypes';\n\nconst entriesEqual = <T>(a: MetadataQueryEntry<T>[], b: MetadataQueryEntry<T>[]): boolean => {\n if (a.length !== b.length) {\n return false;\n }\n for (let i = 0; i < a.length; i++) {\n // Unchanged frontmatter keeps its object identity across `metadata-update`\n // merges, so an identity check on `meta` is a sound \"did this match change?\".\n if (a[i].path !== b[i].path || a[i].meta !== b[i].meta) {\n return false;\n }\n }\n return true;\n};\n\n/**\n * Query the file metadata store (MDX frontmatter) with a plain JS function.\n *\n * The query receives every file's frontmatter keyed by path and returns the paths\n * that match; the hook resolves each path back to its frontmatter and returns an\n * array of `{ path, meta }` entries — so a single call gives you everything you\n * filtered on, with no second lookup. A throwing query is reported as `{ error }`\n * rather than crashing the render.\n *\n * The query runs synchronously during render (no empty first frame), and the\n * returned array keeps its identity while the matches are unchanged, so it is safe\n * to use directly in downstream `useMemo`/`useEffect` dependency arrays.\n *\n * Pass a type parameter to get typed frontmatter throughout:\n * ```ts\n * interface PostMeta { title: string; date: string; draft?: boolean }\n * const posts = useMetadataQuery<PostMeta>((files) =>\n * Object.entries(files)\n * .filter(([, m]) => !m.draft)\n * .sort(([, a], [, b]) => b.date.localeCompare(a.date))\n * .map(([path]) => path),\n * );\n * ```\n */\nexport const useMetadataQuery = <T = Metadata>(\n queryFunction: MetadataQueryFunction<T>,\n): MetadataQueryResult<T> => {\n const { filesMetadata } = use(TinkerableContext);\n const previous = useRef<MetadataQueryEntry<T>[]>([]);\n return useMemo<MetadataQueryResult<T>>(() => {\n const files = (filesMetadata ?? {}) as FilesMetadata<T>;\n let entries: MetadataQueryEntry<T>[];\n try {\n entries = queryFunction(files).map((path) => ({ path, meta: files[path] }));\n } catch (error) {\n return { error };\n }\n // Preserve the prior array reference when nothing matched differently.\n if (entriesEqual(entries, previous.current)) {\n return previous.current;\n }\n previous.current = entries;\n return entries;\n }, [filesMetadata, queryFunction]);\n};\n\n/**\n * Read one file's metadata (MDX frontmatter) by repo-relative path. Returns\n * `undefined` when the path has no metadata. Pass a type parameter for typed\n * field access.\n */\nexport const useFileMetadata = <T = Metadata>(path: string): T | undefined => {\n const { filesMetadata } = use(TinkerableContext);\n return useMemo(\n () => (filesMetadata ?? {})[path] as T | undefined,\n [path, filesMetadata],\n );\n};\n\n/**\n * The raw, reactive metadata store: a map from file path to frontmatter. The\n * escape hatch for apps that want to render their own index rather than express it\n * as a path-returning query. Pass a type parameter for typed frontmatter values.\n */\nexport const useAllMetadata = <T = Metadata>(): FilesMetadata<T> => {\n const { filesMetadata } = use(TinkerableContext);\n return (filesMetadata ?? {}) as FilesMetadata<T>;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAqC;AACrC,+BAAkC;AASlC,MAAM,eAAe,CAAI,GAA4B,MAAwC;AAC3F,MAAI,EAAE,WAAW,EAAE,QAAQ;AACzB,WAAO;AAAA,EACT;AACA,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;AAGjC,QAAI,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM;AACtD,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AA0BO,MAAM,mBAAmB,CAC9B,kBAC2B;AAC3B,QAAM,EAAE,cAAc,QAAI,kBAAI,0CAAiB;AAC/C,QAAM,eAAW,qBAAgC,CAAC,CAAC;AACnD,aAAO,sBAAgC,MAAM;AAC3C,UAAM,QAAS,iBAAiB,CAAC;AACjC,QAAI;AACJ,QAAI;AACF,gBAAU,cAAc,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,MAAM,MAAM,IAAI,EAAE,EAAE;AAAA,IAC5E,SAAS,OAAO;AACd,aAAO,EAAE,MAAM;AAAA,IACjB;AAEA,QAAI,aAAa,SAAS,SAAS,OAAO,GAAG;AAC3C,aAAO,SAAS;AAAA,IAClB;AACA,aAAS,UAAU;AACnB,WAAO;AAAA,EACT,GAAG,CAAC,eAAe,aAAa,CAAC;AACnC;AAOO,MAAM,kBAAkB,CAAe,SAAgC;AAC5E,QAAM,EAAE,cAAc,QAAI,kBAAI,0CAAiB;AAC/C,aAAO;AAAA,IACL,OAAO,iBAAiB,CAAC,GAAG,IAAI;AAAA,IAChC,CAAC,MAAM,aAAa;AAAA,EACtB;AACF;AAOO,MAAM,iBAAiB,MAAsC;AAClE,QAAM,EAAE,cAAc,QAAI,kBAAI,0CAAiB;AAC/C,SAAQ,iBAAiB,CAAC;AAC5B;","names":[]}
|
package/dist/hooks.d.cts
CHANGED
|
@@ -1,6 +1,41 @@
|
|
|
1
|
-
import { Metadata, MetadataQueryFunction, MetadataQueryResult } from './sandboxTypes.cjs';
|
|
1
|
+
import { Metadata, FilesMetadata, MetadataQueryFunction, MetadataQueryResult } from './sandboxTypes.cjs';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Query the file metadata store (MDX frontmatter) with a plain JS function.
|
|
5
|
+
*
|
|
6
|
+
* The query receives every file's frontmatter keyed by path and returns the paths
|
|
7
|
+
* that match; the hook resolves each path back to its frontmatter and returns an
|
|
8
|
+
* array of `{ path, meta }` entries — so a single call gives you everything you
|
|
9
|
+
* filtered on, with no second lookup. A throwing query is reported as `{ error }`
|
|
10
|
+
* rather than crashing the render.
|
|
11
|
+
*
|
|
12
|
+
* The query runs synchronously during render (no empty first frame), and the
|
|
13
|
+
* returned array keeps its identity while the matches are unchanged, so it is safe
|
|
14
|
+
* to use directly in downstream `useMemo`/`useEffect` dependency arrays.
|
|
15
|
+
*
|
|
16
|
+
* Pass a type parameter to get typed frontmatter throughout:
|
|
17
|
+
* ```ts
|
|
18
|
+
* interface PostMeta { title: string; date: string; draft?: boolean }
|
|
19
|
+
* const posts = useMetadataQuery<PostMeta>((files) =>
|
|
20
|
+
* Object.entries(files)
|
|
21
|
+
* .filter(([, m]) => !m.draft)
|
|
22
|
+
* .sort(([, a], [, b]) => b.date.localeCompare(a.date))
|
|
23
|
+
* .map(([path]) => path),
|
|
24
|
+
* );
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
declare const useMetadataQuery: <T = Metadata>(queryFunction: MetadataQueryFunction<T>) => MetadataQueryResult<T>;
|
|
28
|
+
/**
|
|
29
|
+
* Read one file's metadata (MDX frontmatter) by repo-relative path. Returns
|
|
30
|
+
* `undefined` when the path has no metadata. Pass a type parameter for typed
|
|
31
|
+
* field access.
|
|
32
|
+
*/
|
|
33
|
+
declare const useFileMetadata: <T = Metadata>(path: string) => T | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* The raw, reactive metadata store: a map from file path to frontmatter. The
|
|
36
|
+
* escape hatch for apps that want to render their own index rather than express it
|
|
37
|
+
* as a path-returning query. Pass a type parameter for typed frontmatter values.
|
|
38
|
+
*/
|
|
39
|
+
declare const useAllMetadata: <T = Metadata>() => FilesMetadata<T>;
|
|
5
40
|
|
|
6
|
-
export { useFileMetadata, useMetadataQuery };
|
|
41
|
+
export { useAllMetadata, useFileMetadata, useMetadataQuery };
|
package/dist/hooks.d.ts
CHANGED
|
@@ -1,6 +1,41 @@
|
|
|
1
|
-
import { Metadata, MetadataQueryFunction, MetadataQueryResult } from './sandboxTypes.js';
|
|
1
|
+
import { Metadata, FilesMetadata, MetadataQueryFunction, MetadataQueryResult } from './sandboxTypes.js';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/**
|
|
4
|
+
* Query the file metadata store (MDX frontmatter) with a plain JS function.
|
|
5
|
+
*
|
|
6
|
+
* The query receives every file's frontmatter keyed by path and returns the paths
|
|
7
|
+
* that match; the hook resolves each path back to its frontmatter and returns an
|
|
8
|
+
* array of `{ path, meta }` entries — so a single call gives you everything you
|
|
9
|
+
* filtered on, with no second lookup. A throwing query is reported as `{ error }`
|
|
10
|
+
* rather than crashing the render.
|
|
11
|
+
*
|
|
12
|
+
* The query runs synchronously during render (no empty first frame), and the
|
|
13
|
+
* returned array keeps its identity while the matches are unchanged, so it is safe
|
|
14
|
+
* to use directly in downstream `useMemo`/`useEffect` dependency arrays.
|
|
15
|
+
*
|
|
16
|
+
* Pass a type parameter to get typed frontmatter throughout:
|
|
17
|
+
* ```ts
|
|
18
|
+
* interface PostMeta { title: string; date: string; draft?: boolean }
|
|
19
|
+
* const posts = useMetadataQuery<PostMeta>((files) =>
|
|
20
|
+
* Object.entries(files)
|
|
21
|
+
* .filter(([, m]) => !m.draft)
|
|
22
|
+
* .sort(([, a], [, b]) => b.date.localeCompare(a.date))
|
|
23
|
+
* .map(([path]) => path),
|
|
24
|
+
* );
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
declare const useMetadataQuery: <T = Metadata>(queryFunction: MetadataQueryFunction<T>) => MetadataQueryResult<T>;
|
|
28
|
+
/**
|
|
29
|
+
* Read one file's metadata (MDX frontmatter) by repo-relative path. Returns
|
|
30
|
+
* `undefined` when the path has no metadata. Pass a type parameter for typed
|
|
31
|
+
* field access.
|
|
32
|
+
*/
|
|
33
|
+
declare const useFileMetadata: <T = Metadata>(path: string) => T | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* The raw, reactive metadata store: a map from file path to frontmatter. The
|
|
36
|
+
* escape hatch for apps that want to render their own index rather than express it
|
|
37
|
+
* as a path-returning query. Pass a type parameter for typed frontmatter values.
|
|
38
|
+
*/
|
|
39
|
+
declare const useAllMetadata: <T = Metadata>() => FilesMetadata<T>;
|
|
5
40
|
|
|
6
|
-
export { useFileMetadata, useMetadataQuery };
|
|
41
|
+
export { useAllMetadata, useFileMetadata, useMetadataQuery };
|
package/dist/hooks.js
CHANGED
|
@@ -1,18 +1,11 @@
|
|
|
1
|
-
import { use,
|
|
1
|
+
import { use, useMemo, useRef } from "react";
|
|
2
2
|
import { TinkerableContext } from "./TinkerableContext";
|
|
3
|
-
const
|
|
4
|
-
try {
|
|
5
|
-
return { result: queryFunction(filesMetadata) };
|
|
6
|
-
} catch (e) {
|
|
7
|
-
return { error: e };
|
|
8
|
-
}
|
|
9
|
-
};
|
|
10
|
-
const arraysEqual = (a, b) => {
|
|
3
|
+
const entriesEqual = (a, b) => {
|
|
11
4
|
if (a.length !== b.length) {
|
|
12
5
|
return false;
|
|
13
6
|
}
|
|
14
7
|
for (let i = 0; i < a.length; i++) {
|
|
15
|
-
if (a[i] !== b[i]) {
|
|
8
|
+
if (a[i].path !== b[i].path || a[i].meta !== b[i].meta) {
|
|
16
9
|
return false;
|
|
17
10
|
}
|
|
18
11
|
}
|
|
@@ -20,30 +13,35 @@ const arraysEqual = (a, b) => {
|
|
|
20
13
|
};
|
|
21
14
|
const useMetadataQuery = (queryFunction) => {
|
|
22
15
|
const { filesMetadata } = use(TinkerableContext);
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
}, [filesMetadata, setQueryResult, queryFunction]);
|
|
39
|
-
return queryResult;
|
|
16
|
+
const previous = useRef([]);
|
|
17
|
+
return useMemo(() => {
|
|
18
|
+
const files = filesMetadata ?? {};
|
|
19
|
+
let entries;
|
|
20
|
+
try {
|
|
21
|
+
entries = queryFunction(files).map((path) => ({ path, meta: files[path] }));
|
|
22
|
+
} catch (error) {
|
|
23
|
+
return { error };
|
|
24
|
+
}
|
|
25
|
+
if (entriesEqual(entries, previous.current)) {
|
|
26
|
+
return previous.current;
|
|
27
|
+
}
|
|
28
|
+
previous.current = entries;
|
|
29
|
+
return entries;
|
|
30
|
+
}, [filesMetadata, queryFunction]);
|
|
40
31
|
};
|
|
41
32
|
const useFileMetadata = (path) => {
|
|
42
33
|
const { filesMetadata } = use(TinkerableContext);
|
|
43
|
-
|
|
44
|
-
|
|
34
|
+
return useMemo(
|
|
35
|
+
() => (filesMetadata ?? {})[path],
|
|
36
|
+
[path, filesMetadata]
|
|
37
|
+
);
|
|
38
|
+
};
|
|
39
|
+
const useAllMetadata = () => {
|
|
40
|
+
const { filesMetadata } = use(TinkerableContext);
|
|
41
|
+
return filesMetadata ?? {};
|
|
45
42
|
};
|
|
46
43
|
export {
|
|
44
|
+
useAllMetadata,
|
|
47
45
|
useFileMetadata,
|
|
48
46
|
useMetadataQuery
|
|
49
47
|
};
|
package/dist/hooks.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hooks.ts"],"sourcesContent":["import { use,
|
|
1
|
+
{"version":3,"sources":["../src/hooks.ts"],"sourcesContent":["import { use, useMemo, useRef } from 'react';\nimport { TinkerableContext } from './TinkerableContext';\nimport {\n FilesMetadata,\n Metadata,\n MetadataQueryEntry,\n MetadataQueryFunction,\n MetadataQueryResult,\n} from './sandboxTypes';\n\nconst entriesEqual = <T>(a: MetadataQueryEntry<T>[], b: MetadataQueryEntry<T>[]): boolean => {\n if (a.length !== b.length) {\n return false;\n }\n for (let i = 0; i < a.length; i++) {\n // Unchanged frontmatter keeps its object identity across `metadata-update`\n // merges, so an identity check on `meta` is a sound \"did this match change?\".\n if (a[i].path !== b[i].path || a[i].meta !== b[i].meta) {\n return false;\n }\n }\n return true;\n};\n\n/**\n * Query the file metadata store (MDX frontmatter) with a plain JS function.\n *\n * The query receives every file's frontmatter keyed by path and returns the paths\n * that match; the hook resolves each path back to its frontmatter and returns an\n * array of `{ path, meta }` entries — so a single call gives you everything you\n * filtered on, with no second lookup. A throwing query is reported as `{ error }`\n * rather than crashing the render.\n *\n * The query runs synchronously during render (no empty first frame), and the\n * returned array keeps its identity while the matches are unchanged, so it is safe\n * to use directly in downstream `useMemo`/`useEffect` dependency arrays.\n *\n * Pass a type parameter to get typed frontmatter throughout:\n * ```ts\n * interface PostMeta { title: string; date: string; draft?: boolean }\n * const posts = useMetadataQuery<PostMeta>((files) =>\n * Object.entries(files)\n * .filter(([, m]) => !m.draft)\n * .sort(([, a], [, b]) => b.date.localeCompare(a.date))\n * .map(([path]) => path),\n * );\n * ```\n */\nexport const useMetadataQuery = <T = Metadata>(\n queryFunction: MetadataQueryFunction<T>,\n): MetadataQueryResult<T> => {\n const { filesMetadata } = use(TinkerableContext);\n const previous = useRef<MetadataQueryEntry<T>[]>([]);\n return useMemo<MetadataQueryResult<T>>(() => {\n const files = (filesMetadata ?? {}) as FilesMetadata<T>;\n let entries: MetadataQueryEntry<T>[];\n try {\n entries = queryFunction(files).map((path) => ({ path, meta: files[path] }));\n } catch (error) {\n return { error };\n }\n // Preserve the prior array reference when nothing matched differently.\n if (entriesEqual(entries, previous.current)) {\n return previous.current;\n }\n previous.current = entries;\n return entries;\n }, [filesMetadata, queryFunction]);\n};\n\n/**\n * Read one file's metadata (MDX frontmatter) by repo-relative path. Returns\n * `undefined` when the path has no metadata. Pass a type parameter for typed\n * field access.\n */\nexport const useFileMetadata = <T = Metadata>(path: string): T | undefined => {\n const { filesMetadata } = use(TinkerableContext);\n return useMemo(\n () => (filesMetadata ?? {})[path] as T | undefined,\n [path, filesMetadata],\n );\n};\n\n/**\n * The raw, reactive metadata store: a map from file path to frontmatter. The\n * escape hatch for apps that want to render their own index rather than express it\n * as a path-returning query. Pass a type parameter for typed frontmatter values.\n */\nexport const useAllMetadata = <T = Metadata>(): FilesMetadata<T> => {\n const { filesMetadata } = use(TinkerableContext);\n return (filesMetadata ?? {}) as FilesMetadata<T>;\n};\n"],"mappings":"AAAA,SAAS,KAAK,SAAS,cAAc;AACrC,SAAS,yBAAyB;AASlC,MAAM,eAAe,CAAI,GAA4B,MAAwC;AAC3F,MAAI,EAAE,WAAW,EAAE,QAAQ;AACzB,WAAO;AAAA,EACT;AACA,WAAS,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;AAGjC,QAAI,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,MAAM;AACtD,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AA0BO,MAAM,mBAAmB,CAC9B,kBAC2B;AAC3B,QAAM,EAAE,cAAc,IAAI,IAAI,iBAAiB;AAC/C,QAAM,WAAW,OAAgC,CAAC,CAAC;AACnD,SAAO,QAAgC,MAAM;AAC3C,UAAM,QAAS,iBAAiB,CAAC;AACjC,QAAI;AACJ,QAAI;AACF,gBAAU,cAAc,KAAK,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,MAAM,MAAM,IAAI,EAAE,EAAE;AAAA,IAC5E,SAAS,OAAO;AACd,aAAO,EAAE,MAAM;AAAA,IACjB;AAEA,QAAI,aAAa,SAAS,SAAS,OAAO,GAAG;AAC3C,aAAO,SAAS;AAAA,IAClB;AACA,aAAS,UAAU;AACnB,WAAO;AAAA,EACT,GAAG,CAAC,eAAe,aAAa,CAAC;AACnC;AAOO,MAAM,kBAAkB,CAAe,SAAgC;AAC5E,QAAM,EAAE,cAAc,IAAI,IAAI,iBAAiB;AAC/C,SAAO;AAAA,IACL,OAAO,iBAAiB,CAAC,GAAG,IAAI;AAAA,IAChC,CAAC,MAAM,aAAa;AAAA,EACtB;AACF;AAOO,MAAM,iBAAiB,MAAsC;AAClE,QAAM,EAAE,cAAc,IAAI,IAAI,iBAAiB;AAC/C,SAAQ,iBAAiB,CAAC;AAC5B;","names":[]}
|
package/dist/index.cjs
CHANGED
|
@@ -20,6 +20,7 @@ __reExport(index_exports, require("./routing"), module.exports);
|
|
|
20
20
|
__reExport(index_exports, require("./boot"), module.exports);
|
|
21
21
|
__reExport(index_exports, require("./components/Include"), module.exports);
|
|
22
22
|
__reExport(index_exports, require("./components/MDXComponents"), module.exports);
|
|
23
|
+
__reExport(index_exports, require("./components/Routes"), module.exports);
|
|
23
24
|
__reExport(index_exports, require("./hooks"), module.exports);
|
|
24
25
|
__reExport(index_exports, require("./auth"), module.exports);
|
|
25
26
|
__reExport(index_exports, require("./theme"), module.exports);
|
|
@@ -41,6 +42,7 @@ __reExport(index_exports, require("./tasks"), module.exports);
|
|
|
41
42
|
__reExport(index_exports, require("./runtime"), module.exports);
|
|
42
43
|
__reExport(index_exports, require("./irMarkers"), module.exports);
|
|
43
44
|
__reExport(index_exports, require("./ready"), module.exports);
|
|
45
|
+
__reExport(index_exports, require("./loading"), module.exports);
|
|
44
46
|
__reExport(index_exports, require("./protocolStream"), module.exports);
|
|
45
47
|
__reExport(index_exports, require("./sandboxTypes"), module.exports);
|
|
46
48
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -50,6 +52,7 @@ __reExport(index_exports, require("./sandboxTypes"), module.exports);
|
|
|
50
52
|
...require("./boot"),
|
|
51
53
|
...require("./components/Include"),
|
|
52
54
|
...require("./components/MDXComponents"),
|
|
55
|
+
...require("./components/Routes"),
|
|
53
56
|
...require("./hooks"),
|
|
54
57
|
...require("./auth"),
|
|
55
58
|
...require("./theme"),
|
|
@@ -71,6 +74,7 @@ __reExport(index_exports, require("./sandboxTypes"), module.exports);
|
|
|
71
74
|
...require("./runtime"),
|
|
72
75
|
...require("./irMarkers"),
|
|
73
76
|
...require("./ready"),
|
|
77
|
+
...require("./loading"),
|
|
74
78
|
...require("./protocolStream"),
|
|
75
79
|
...require("./sandboxTypes")
|
|
76
80
|
});
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from \"./MDXProvider\";\nexport * from \"./routing\";\nexport * from \"./boot\";\nexport * from './components/Include';\nexport * from './components/MDXComponents';\nexport * from './hooks'\nexport * from './auth';\nexport * from './theme';\nexport * from './editorContext';\nexport * from './editor';\nexport * from './formFactor';\nexport * from './region';\nexport * from './mounts';\nexport * from './contribute';\nexport * from './catalog';\nexport * from './ipc';\nexport * from './dnd';\nexport * from './netFetch';\nexport * from './secrets';\nexport * from './llm';\nexport * from './diagnostics';\nexport * from './onFsChange';\nexport * from './tasks';\nexport * from './runtime';\nexport * from './irMarkers';\nexport * from './ready';\nexport * from './protocolStream';\nexport * from './sandboxTypes';\n"],"mappings":";;;;;;;;;;;;;;;AAAA;AAAA;AAAA,0BAAc,0BAAd;AACA,0BAAc,sBADd;AAEA,0BAAc,mBAFd;AAGA,0BAAc,iCAHd;AAIA,0BAAc,uCAJd;AAKA,0BAAc,
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from \"./MDXProvider\";\nexport * from \"./routing\";\nexport * from \"./boot\";\nexport * from './components/Include';\nexport * from './components/MDXComponents';\nexport * from './components/Routes';\nexport * from './hooks'\nexport * from './auth';\nexport * from './theme';\nexport * from './editorContext';\nexport * from './editor';\nexport * from './formFactor';\nexport * from './region';\nexport * from './mounts';\nexport * from './contribute';\nexport * from './catalog';\nexport * from './ipc';\nexport * from './dnd';\nexport * from './netFetch';\nexport * from './secrets';\nexport * from './llm';\nexport * from './diagnostics';\nexport * from './onFsChange';\nexport * from './tasks';\nexport * from './runtime';\nexport * from './irMarkers';\nexport * from './ready';\nexport * from './loading';\nexport * from './protocolStream';\nexport * from './sandboxTypes';\n"],"mappings":";;;;;;;;;;;;;;;AAAA;AAAA;AAAA,0BAAc,0BAAd;AACA,0BAAc,sBADd;AAEA,0BAAc,mBAFd;AAGA,0BAAc,iCAHd;AAIA,0BAAc,uCAJd;AAKA,0BAAc,gCALd;AAMA,0BAAc,oBANd;AAOA,0BAAc,mBAPd;AAQA,0BAAc,oBARd;AASA,0BAAc,4BATd;AAUA,0BAAc,qBAVd;AAWA,0BAAc,yBAXd;AAYA,0BAAc,qBAZd;AAaA,0BAAc,qBAbd;AAcA,0BAAc,yBAdd;AAeA,0BAAc,sBAfd;AAgBA,0BAAc,kBAhBd;AAiBA,0BAAc,kBAjBd;AAkBA,0BAAc,uBAlBd;AAmBA,0BAAc,sBAnBd;AAoBA,0BAAc,kBApBd;AAqBA,0BAAc,0BArBd;AAsBA,0BAAc,yBAtBd;AAuBA,0BAAc,oBAvBd;AAwBA,0BAAc,sBAxBd;AAyBA,0BAAc,wBAzBd;AA0BA,0BAAc,oBA1Bd;AA2BA,0BAAc,sBA3Bd;AA4BA,0BAAc,6BA5Bd;AA6BA,0BAAc,2BA7Bd;","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export { MDXProvider, useMDXComponents } from './MDXProvider.cjs';
|
|
2
|
-
export { AppliedRoutingRule, Router, applyRoutingRule, navigate, useTinkerableLink } from './routing.cjs';
|
|
3
|
-
export { BootProps, DEFAULT_ROUTING_SPEC, TinkerableApp, boot } from './boot.cjs';
|
|
2
|
+
export { AppliedRoutingRule, Router, applyRoutingRule, navigate, renderRoute, useRoute, useRouteParams, useTinkerableLink } from './routing.cjs';
|
|
3
|
+
export { BootProps, CATCH_ALL_ROUTING_SPEC, DEFAULT_ROUTING_SPEC, TinkerableApp, boot } from './boot.cjs';
|
|
4
4
|
export { Include, RenderExportedComponent, RenderExportedComponentContext, RenderFileContextType } from './components/Include.cjs';
|
|
5
5
|
export { DEFAULT_MDX_COMPONENTS, InternalLink, Link } from './components/MDXComponents.cjs';
|
|
6
|
-
export {
|
|
6
|
+
export { Route, RouteProps, Routes } from './components/Routes.cjs';
|
|
7
|
+
export { useAllMetadata, useFileMetadata, useMetadataQuery } from './hooks.cjs';
|
|
7
8
|
export { AuthState, AuthStatus, SandboxUser, getAuthState, onAuthChange, useAuth } from './auth.cjs';
|
|
8
9
|
export { HostTheme, getHostTheme, onHostThemeChange, setHostTheme, useHostTheme } from './theme.cjs';
|
|
9
10
|
export { EditorContext, getEditorContext, onEditorContextChange, useEditorContext } from './editorContext.cjs';
|
|
@@ -24,8 +25,9 @@ export { DirCap, FileCap, TaskInput, cancelTask, capDir, capFile, completeTask,
|
|
|
24
25
|
export { SDK_PROTOCOL_VERSION, SdkHandshake, announceHandshake, sdkHandshake } from './runtime.cjs';
|
|
25
26
|
export { ForwardedMarker, IR_MARKERS, IrMarkerName, isAllowedMarkerName, isIrMarkerName, resolveInteractive, validateMarker } from './irMarkers.cjs';
|
|
26
27
|
export { ReadyState, __resetReady, __setReadyDeps, getReadyState, onReady, reportReady } from './ready.cjs';
|
|
28
|
+
export { LOADING_TIMINGS, LoadingRegion, Skeleton, SkeletonArchetype, SkeletonRow, Spinner } from './loading.cjs';
|
|
27
29
|
export { StreamError, StreamFrame, StreamTransport, consumeStream, protocolStream } from './protocolStream.cjs';
|
|
28
|
-
export { EvaluationContext, FileQueryResult, FilesMetadata, Metadata, MetadataQueryFunction, MetadataQueryResult, ModuleExports } from './sandboxTypes.cjs';
|
|
30
|
+
export { EvaluationContext, FileQueryResult, FilesMetadata, Metadata, MetadataQueryEntry, MetadataQueryFunction, MetadataQueryResult, ModuleExports } from './sandboxTypes.cjs';
|
|
29
31
|
export { ImmediatelyRunGlobal, getHostRuntime } from './hostRuntime.cjs';
|
|
30
32
|
export { SDK_VERSION } from './version.cjs';
|
|
31
33
|
import 'react';
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
export { MDXProvider, useMDXComponents } from './MDXProvider.js';
|
|
2
|
-
export { AppliedRoutingRule, Router, applyRoutingRule, navigate, useTinkerableLink } from './routing.js';
|
|
3
|
-
export { BootProps, DEFAULT_ROUTING_SPEC, TinkerableApp, boot } from './boot.js';
|
|
2
|
+
export { AppliedRoutingRule, Router, applyRoutingRule, navigate, renderRoute, useRoute, useRouteParams, useTinkerableLink } from './routing.js';
|
|
3
|
+
export { BootProps, CATCH_ALL_ROUTING_SPEC, DEFAULT_ROUTING_SPEC, TinkerableApp, boot } from './boot.js';
|
|
4
4
|
export { Include, RenderExportedComponent, RenderExportedComponentContext, RenderFileContextType } from './components/Include.js';
|
|
5
5
|
export { DEFAULT_MDX_COMPONENTS, InternalLink, Link } from './components/MDXComponents.js';
|
|
6
|
-
export {
|
|
6
|
+
export { Route, RouteProps, Routes } from './components/Routes.js';
|
|
7
|
+
export { useAllMetadata, useFileMetadata, useMetadataQuery } from './hooks.js';
|
|
7
8
|
export { AuthState, AuthStatus, SandboxUser, getAuthState, onAuthChange, useAuth } from './auth.js';
|
|
8
9
|
export { HostTheme, getHostTheme, onHostThemeChange, setHostTheme, useHostTheme } from './theme.js';
|
|
9
10
|
export { EditorContext, getEditorContext, onEditorContextChange, useEditorContext } from './editorContext.js';
|
|
@@ -24,8 +25,9 @@ export { DirCap, FileCap, TaskInput, cancelTask, capDir, capFile, completeTask,
|
|
|
24
25
|
export { SDK_PROTOCOL_VERSION, SdkHandshake, announceHandshake, sdkHandshake } from './runtime.js';
|
|
25
26
|
export { ForwardedMarker, IR_MARKERS, IrMarkerName, isAllowedMarkerName, isIrMarkerName, resolveInteractive, validateMarker } from './irMarkers.js';
|
|
26
27
|
export { ReadyState, __resetReady, __setReadyDeps, getReadyState, onReady, reportReady } from './ready.js';
|
|
28
|
+
export { LOADING_TIMINGS, LoadingRegion, Skeleton, SkeletonArchetype, SkeletonRow, Spinner } from './loading.js';
|
|
27
29
|
export { StreamError, StreamFrame, StreamTransport, consumeStream, protocolStream } from './protocolStream.js';
|
|
28
|
-
export { EvaluationContext, FileQueryResult, FilesMetadata, Metadata, MetadataQueryFunction, MetadataQueryResult, ModuleExports } from './sandboxTypes.js';
|
|
30
|
+
export { EvaluationContext, FileQueryResult, FilesMetadata, Metadata, MetadataQueryEntry, MetadataQueryFunction, MetadataQueryResult, ModuleExports } from './sandboxTypes.js';
|
|
29
31
|
export { ImmediatelyRunGlobal, getHostRuntime } from './hostRuntime.js';
|
|
30
32
|
export { SDK_VERSION } from './version.js';
|
|
31
33
|
import 'react';
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ export * from "./routing";
|
|
|
3
3
|
export * from "./boot";
|
|
4
4
|
export * from "./components/Include";
|
|
5
5
|
export * from "./components/MDXComponents";
|
|
6
|
+
export * from "./components/Routes";
|
|
6
7
|
export * from "./hooks";
|
|
7
8
|
export * from "./auth";
|
|
8
9
|
export * from "./theme";
|
|
@@ -24,6 +25,7 @@ export * from "./tasks";
|
|
|
24
25
|
export * from "./runtime";
|
|
25
26
|
export * from "./irMarkers";
|
|
26
27
|
export * from "./ready";
|
|
28
|
+
export * from "./loading";
|
|
27
29
|
export * from "./protocolStream";
|
|
28
30
|
export * from "./sandboxTypes";
|
|
29
31
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from \"./MDXProvider\";\nexport * from \"./routing\";\nexport * from \"./boot\";\nexport * from './components/Include';\nexport * from './components/MDXComponents';\nexport * from './hooks'\nexport * from './auth';\nexport * from './theme';\nexport * from './editorContext';\nexport * from './editor';\nexport * from './formFactor';\nexport * from './region';\nexport * from './mounts';\nexport * from './contribute';\nexport * from './catalog';\nexport * from './ipc';\nexport * from './dnd';\nexport * from './netFetch';\nexport * from './secrets';\nexport * from './llm';\nexport * from './diagnostics';\nexport * from './onFsChange';\nexport * from './tasks';\nexport * from './runtime';\nexport * from './irMarkers';\nexport * from './ready';\nexport * from './protocolStream';\nexport * from './sandboxTypes';\n"],"mappings":"AAAA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from \"./MDXProvider\";\nexport * from \"./routing\";\nexport * from \"./boot\";\nexport * from './components/Include';\nexport * from './components/MDXComponents';\nexport * from './components/Routes';\nexport * from './hooks'\nexport * from './auth';\nexport * from './theme';\nexport * from './editorContext';\nexport * from './editor';\nexport * from './formFactor';\nexport * from './region';\nexport * from './mounts';\nexport * from './contribute';\nexport * from './catalog';\nexport * from './ipc';\nexport * from './dnd';\nexport * from './netFetch';\nexport * from './secrets';\nexport * from './llm';\nexport * from './diagnostics';\nexport * from './onFsChange';\nexport * from './tasks';\nexport * from './runtime';\nexport * from './irMarkers';\nexport * from './ready';\nexport * from './loading';\nexport * from './protocolStream';\nexport * from './sandboxTypes';\n"],"mappings":"AAAA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;","names":[]}
|
package/dist/irMarkers.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/irMarkers.ts"],"sourcesContent":["// The `ir.*` load-profiling marker vocabulary + the host-side allowlist + the\n// `ir.interactive` precedence rule (LOAD_PROFILING_SPEC §3/§3.1, R3-46).\n//\n// This module is PURE and dependency-free on purpose: the spec (§3.1) requires the\n// host's vocabulary allowlist to be MIRRORED from this same definition (\"additions\n// to the vocabulary are spec amendments, mirrored in the host allowlist in the same\n// change\"). Keeping the table + validator here, with no transport/React imports,\n// lets the sandbox side (this SDK) and the host (site-main, its own mirrored copy)\n// share an identical contract that is unit-tested in both places.\n\n/**\n * Each `ir.*` marker name → the attribute keys its payload may carry (the §3\n * table). A forwarded marker is accepted only if BOTH its name is defined here AND\n * every attribute key it carries is in that marker's allowed set (LP-5). An empty\n * array means \"no attributes\" (a bare mark).\n */\nexport const IR_MARKERS = {\n \"ir.open\": [\"url\", \"provider\", \"ns\", \"repo\", \"ref\", \"refKind\"],\n \"ir.fetch\": [\"source\", \"bytes\", \"requestCount\", \"cacheHit\", \"httpStatus\"],\n \"ir.mount\": [\"phantomCount\", \"writablePrimed\"],\n \"ir.sandbox.boot\": [],\n \"ir.transpile\": [\"moduleCount\", \"cacheHit\", \"bytesIn\", \"bytesOut\"],\n \"ir.deps\": [\"depCount\", \"bytes\", \"requestCount\", \"cacheHit\", \"cdn\"],\n \"ir.eval\": [\"moduleCount\"],\n \"ir.fmp\": [],\n \"ir.interactive\": [\"cold\"],\n \"ir.verify\": [\"result\", \"blocking\"],\n \"ir.refresh\": [\"bytes\"],\n} as const;\n\nexport type IrMarkerName = keyof typeof IR_MARKERS;\n\n// Per-module / per-dep sub-marks (§3): a defined aggregate plus a `[…]` selector,\n// e.g. `ir.transpile.mod[/src/App.tsx]` or `ir.deps.pkg[react@18.2.0]`. A sub-mark\n// inherits its aggregate's attribute schema.\nconst SUBMARK_RE = /^(ir\\.transpile\\.mod|ir\\.deps\\.pkg)\\[[^\\]]+\\]$/;\nconst submarkAggregate = (name: string): IrMarkerName | null =>\n name.startsWith(\"ir.transpile.mod\") ? \"ir.transpile\" : name.startsWith(\"ir.deps.pkg\") ? \"ir.deps\" : null;\n\n/** Is `name` a defined top-level `ir.*` marker (not a sub-mark)? */\nexport const isIrMarkerName = (name: string): name is IrMarkerName =>\n Object.prototype.hasOwnProperty.call(IR_MARKERS, name);\n\n/** Is `name` an accepted marker name — a defined top-level marker OR a recognized\n * per-module/per-dep sub-mark? */\nexport const isAllowedMarkerName = (name: string): boolean =>\n isIrMarkerName(name) || SUBMARK_RE.test(name);\n\n/** A marker forwarded across the origin boundary (§3.2): a name, the sandbox-side\n * `performance.now()` timestamp, and the optional attribute payload. */\nexport interface ForwardedMarker {\n name: string;\n /** Sandbox-relative timestamp (`performance.now()`) at emission (§3.2). */\n at: number;\n attrs?: Record<string, unknown>;\n}\n\n/**\n * The LP-5 vocabulary allowlist (pure). Accept a forwarded marker ONLY if its name\n * is in the vocabulary AND every attribute key is in that marker's schema. An\n * unknown name — or a defined name carrying an out-of-schema attribute key — is\n * DROPPED (returns `null`), never recorded. This is the gate against an untrusted\n * sandbox minting arbitrary names/values into the host timeline (an injection\n * surface for dashboards / the deferred RUM endpoint). Sub-marks inherit their\n * aggregate's schema.\n */\nexport function validateMarker(m: ForwardedMarker | null | undefined): ForwardedMarker | null {\n if (!m || typeof m.name !== \"string\") return null;\n if (typeof m.at !== \"number\" || !Number.isFinite(m.at)) return null;\n if (!isAllowedMarkerName(m.name)) return null;\n const base: IrMarkerName = isIrMarkerName(m.name) ? m.name : submarkAggregate(m.name)!;\n const allowed: readonly string[] = IR_MARKERS[base];\n const attrs = m.attrs;\n if (attrs !== undefined) {\n if (typeof attrs !== \"object\" || attrs === null) return null;\n for (const key of Object.keys(attrs)) {\n if (!allowed.includes(key)) return null; // out-of-schema attribute → drop the whole marker\n }\n }\n return { name: m.name, at: m.at, ...(attrs !== undefined ? { attrs } : {}) };\n}\n\n/**\n * `ir.interactive = max(rootRenderCommit, reportReady)` (LP2-3). An app-called\n * `reportReady()` may only DELAY interactive — never advance it before the root\n * render commits. A `reportReady()` that fires early is recorded but takes effect\n * at the commit; if the app never reports, the commit alone stands. Budgets bind to\n * the later of the two, so an app can't game its budget by declaring itself ready\n * before it has rendered anything. PURE.\n */\nexport function resolveInteractive(rootRenderCommitAt: number, reportReadyAt?: number): number {\n if (reportReadyAt === undefined) return rootRenderCommitAt;\n return Math.max(rootRenderCommitAt, reportReadyAt);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBO,MAAM,aAAa;AAAA,EACxB,WAAW,CAAC,OAAO,YAAY,MAAM,QAAQ,OAAO,SAAS;AAAA,EAC7D,YAAY,CAAC,UAAU,SAAS,gBAAgB,YAAY,YAAY;AAAA,EACxE,YAAY,CAAC,gBAAgB,gBAAgB;AAAA,EAC7C,mBAAmB,CAAC;AAAA,EACpB,gBAAgB,CAAC,eAAe,YAAY,WAAW,UAAU;AAAA,EACjE,WAAW,CAAC,YAAY,SAAS,gBAAgB,YAAY,KAAK;AAAA,EAClE,WAAW,CAAC,aAAa;AAAA,EACzB,UAAU,CAAC;AAAA,EACX,kBAAkB,CAAC,MAAM;AAAA,EACzB,aAAa,CAAC,UAAU,UAAU;AAAA,EAClC,cAAc,CAAC,OAAO;AACxB;
|
|
1
|
+
{"version":3,"sources":["../src/irMarkers.ts"],"sourcesContent":["// The `ir.*` load-profiling marker vocabulary + the host-side allowlist + the\n// `ir.interactive` precedence rule (LOAD_PROFILING_SPEC §3/§3.1, R3-46).\n//\n// This module is PURE and dependency-free on purpose: the spec (§3.1) requires the\n// host's vocabulary allowlist to be MIRRORED from this same definition (\"additions\n// to the vocabulary are spec amendments, mirrored in the host allowlist in the same\n// change\"). Keeping the table + validator here, with no transport/React imports,\n// lets the sandbox side (this SDK) and the host (site-main, its own mirrored copy)\n// share an identical contract that is unit-tested in both places.\n\n/**\n * Each `ir.*` marker name → the attribute keys its payload may carry (the §3\n * table). A forwarded marker is accepted only if BOTH its name is defined here AND\n * every attribute key it carries is in that marker's allowed set (LP-5). An empty\n * array means \"no attributes\" (a bare mark).\n */\nexport const IR_MARKERS = {\n \"ir.open\": [\"url\", \"provider\", \"ns\", \"repo\", \"ref\", \"refKind\"],\n \"ir.fetch\": [\"source\", \"bytes\", \"requestCount\", \"cacheHit\", \"httpStatus\"],\n \"ir.mount\": [\"phantomCount\", \"writablePrimed\"],\n \"ir.sandbox.boot\": [],\n \"ir.transpile\": [\"moduleCount\", \"cacheHit\", \"bytesIn\", \"bytesOut\"],\n \"ir.deps\": [\"depCount\", \"bytes\", \"requestCount\", \"cacheHit\", \"cdn\"],\n \"ir.eval\": [\"moduleCount\"],\n \"ir.fmp\": [],\n \"ir.interactive\": [\"cold\"],\n \"ir.verify\": [\"result\", \"blocking\"],\n \"ir.refresh\": [\"bytes\"],\n} as const;\n\n/** A canonical `ir.*` load-profiling marker name (a key of {@link IR_MARKERS}). */\nexport type IrMarkerName = keyof typeof IR_MARKERS;\n\n// Per-module / per-dep sub-marks (§3): a defined aggregate plus a `[…]` selector,\n// e.g. `ir.transpile.mod[/src/App.tsx]` or `ir.deps.pkg[react@18.2.0]`. A sub-mark\n// inherits its aggregate's attribute schema.\nconst SUBMARK_RE = /^(ir\\.transpile\\.mod|ir\\.deps\\.pkg)\\[[^\\]]+\\]$/;\nconst submarkAggregate = (name: string): IrMarkerName | null =>\n name.startsWith(\"ir.transpile.mod\") ? \"ir.transpile\" : name.startsWith(\"ir.deps.pkg\") ? \"ir.deps\" : null;\n\n/** Is `name` a defined top-level `ir.*` marker (not a sub-mark)? */\nexport const isIrMarkerName = (name: string): name is IrMarkerName =>\n Object.prototype.hasOwnProperty.call(IR_MARKERS, name);\n\n/** Is `name` an accepted marker name — a defined top-level marker OR a recognized\n * per-module/per-dep sub-mark? */\nexport const isAllowedMarkerName = (name: string): boolean =>\n isIrMarkerName(name) || SUBMARK_RE.test(name);\n\n/** A marker forwarded across the origin boundary (§3.2): a name, the sandbox-side\n * `performance.now()` timestamp, and the optional attribute payload. */\nexport interface ForwardedMarker {\n name: string;\n /** Sandbox-relative timestamp (`performance.now()`) at emission (§3.2). */\n at: number;\n attrs?: Record<string, unknown>;\n}\n\n/**\n * The LP-5 vocabulary allowlist (pure). Accept a forwarded marker ONLY if its name\n * is in the vocabulary AND every attribute key is in that marker's schema. An\n * unknown name — or a defined name carrying an out-of-schema attribute key — is\n * DROPPED (returns `null`), never recorded. This is the gate against an untrusted\n * sandbox minting arbitrary names/values into the host timeline (an injection\n * surface for dashboards / the deferred RUM endpoint). Sub-marks inherit their\n * aggregate's schema.\n */\nexport function validateMarker(m: ForwardedMarker | null | undefined): ForwardedMarker | null {\n if (!m || typeof m.name !== \"string\") return null;\n if (typeof m.at !== \"number\" || !Number.isFinite(m.at)) return null;\n if (!isAllowedMarkerName(m.name)) return null;\n const base: IrMarkerName = isIrMarkerName(m.name) ? m.name : submarkAggregate(m.name)!;\n const allowed: readonly string[] = IR_MARKERS[base];\n const attrs = m.attrs;\n if (attrs !== undefined) {\n if (typeof attrs !== \"object\" || attrs === null) return null;\n for (const key of Object.keys(attrs)) {\n if (!allowed.includes(key)) return null; // out-of-schema attribute → drop the whole marker\n }\n }\n return { name: m.name, at: m.at, ...(attrs !== undefined ? { attrs } : {}) };\n}\n\n/**\n * `ir.interactive = max(rootRenderCommit, reportReady)` (LP2-3). An app-called\n * `reportReady()` may only DELAY interactive — never advance it before the root\n * render commits. A `reportReady()` that fires early is recorded but takes effect\n * at the commit; if the app never reports, the commit alone stands. Budgets bind to\n * the later of the two, so an app can't game its budget by declaring itself ready\n * before it has rendered anything. PURE.\n */\nexport function resolveInteractive(rootRenderCommitAt: number, reportReadyAt?: number): number {\n if (reportReadyAt === undefined) return rootRenderCommitAt;\n return Math.max(rootRenderCommitAt, reportReadyAt);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgBO,MAAM,aAAa;AAAA,EACxB,WAAW,CAAC,OAAO,YAAY,MAAM,QAAQ,OAAO,SAAS;AAAA,EAC7D,YAAY,CAAC,UAAU,SAAS,gBAAgB,YAAY,YAAY;AAAA,EACxE,YAAY,CAAC,gBAAgB,gBAAgB;AAAA,EAC7C,mBAAmB,CAAC;AAAA,EACpB,gBAAgB,CAAC,eAAe,YAAY,WAAW,UAAU;AAAA,EACjE,WAAW,CAAC,YAAY,SAAS,gBAAgB,YAAY,KAAK;AAAA,EAClE,WAAW,CAAC,aAAa;AAAA,EACzB,UAAU,CAAC;AAAA,EACX,kBAAkB,CAAC,MAAM;AAAA,EACzB,aAAa,CAAC,UAAU,UAAU;AAAA,EAClC,cAAc,CAAC,OAAO;AACxB;AAQA,MAAM,aAAa;AACnB,MAAM,mBAAmB,CAAC,SACxB,KAAK,WAAW,kBAAkB,IAAI,iBAAiB,KAAK,WAAW,aAAa,IAAI,YAAY;AAG/F,MAAM,iBAAiB,CAAC,SAC7B,OAAO,UAAU,eAAe,KAAK,YAAY,IAAI;AAIhD,MAAM,sBAAsB,CAAC,SAClC,eAAe,IAAI,KAAK,WAAW,KAAK,IAAI;AAoBvC,SAAS,eAAe,GAA+D;AAC5F,MAAI,CAAC,KAAK,OAAO,EAAE,SAAS,SAAU,QAAO;AAC7C,MAAI,OAAO,EAAE,OAAO,YAAY,CAAC,OAAO,SAAS,EAAE,EAAE,EAAG,QAAO;AAC/D,MAAI,CAAC,oBAAoB,EAAE,IAAI,EAAG,QAAO;AACzC,QAAM,OAAqB,eAAe,EAAE,IAAI,IAAI,EAAE,OAAO,iBAAiB,EAAE,IAAI;AACpF,QAAM,UAA6B,WAAW,IAAI;AAClD,QAAM,QAAQ,EAAE;AAChB,MAAI,UAAU,QAAW;AACvB,QAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,eAAW,OAAO,OAAO,KAAK,KAAK,GAAG;AACpC,UAAI,CAAC,QAAQ,SAAS,GAAG,EAAG,QAAO;AAAA,IACrC;AAAA,EACF;AACA,SAAO,EAAE,MAAM,EAAE,MAAM,IAAI,EAAE,IAAI,GAAI,UAAU,SAAY,EAAE,MAAM,IAAI,CAAC,EAAG;AAC7E;AAUO,SAAS,mBAAmB,oBAA4B,eAAgC;AAC7F,MAAI,kBAAkB,OAAW,QAAO;AACxC,SAAO,KAAK,IAAI,oBAAoB,aAAa;AACnD;","names":[]}
|
package/dist/irMarkers.d.cts
CHANGED
|
@@ -17,6 +17,7 @@ declare const IR_MARKERS: {
|
|
|
17
17
|
readonly "ir.verify": readonly ["result", "blocking"];
|
|
18
18
|
readonly "ir.refresh": readonly ["bytes"];
|
|
19
19
|
};
|
|
20
|
+
/** A canonical `ir.*` load-profiling marker name (a key of {@link IR_MARKERS}). */
|
|
20
21
|
type IrMarkerName = keyof typeof IR_MARKERS;
|
|
21
22
|
/** Is `name` a defined top-level `ir.*` marker (not a sub-mark)? */
|
|
22
23
|
declare const isIrMarkerName: (name: string) => name is IrMarkerName;
|
package/dist/irMarkers.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ declare const IR_MARKERS: {
|
|
|
17
17
|
readonly "ir.verify": readonly ["result", "blocking"];
|
|
18
18
|
readonly "ir.refresh": readonly ["bytes"];
|
|
19
19
|
};
|
|
20
|
+
/** A canonical `ir.*` load-profiling marker name (a key of {@link IR_MARKERS}). */
|
|
20
21
|
type IrMarkerName = keyof typeof IR_MARKERS;
|
|
21
22
|
/** Is `name` a defined top-level `ir.*` marker (not a sub-mark)? */
|
|
22
23
|
declare const isIrMarkerName: (name: string) => name is IrMarkerName;
|
package/dist/irMarkers.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/irMarkers.ts"],"sourcesContent":["// The `ir.*` load-profiling marker vocabulary + the host-side allowlist + the\n// `ir.interactive` precedence rule (LOAD_PROFILING_SPEC §3/§3.1, R3-46).\n//\n// This module is PURE and dependency-free on purpose: the spec (§3.1) requires the\n// host's vocabulary allowlist to be MIRRORED from this same definition (\"additions\n// to the vocabulary are spec amendments, mirrored in the host allowlist in the same\n// change\"). Keeping the table + validator here, with no transport/React imports,\n// lets the sandbox side (this SDK) and the host (site-main, its own mirrored copy)\n// share an identical contract that is unit-tested in both places.\n\n/**\n * Each `ir.*` marker name → the attribute keys its payload may carry (the §3\n * table). A forwarded marker is accepted only if BOTH its name is defined here AND\n * every attribute key it carries is in that marker's allowed set (LP-5). An empty\n * array means \"no attributes\" (a bare mark).\n */\nexport const IR_MARKERS = {\n \"ir.open\": [\"url\", \"provider\", \"ns\", \"repo\", \"ref\", \"refKind\"],\n \"ir.fetch\": [\"source\", \"bytes\", \"requestCount\", \"cacheHit\", \"httpStatus\"],\n \"ir.mount\": [\"phantomCount\", \"writablePrimed\"],\n \"ir.sandbox.boot\": [],\n \"ir.transpile\": [\"moduleCount\", \"cacheHit\", \"bytesIn\", \"bytesOut\"],\n \"ir.deps\": [\"depCount\", \"bytes\", \"requestCount\", \"cacheHit\", \"cdn\"],\n \"ir.eval\": [\"moduleCount\"],\n \"ir.fmp\": [],\n \"ir.interactive\": [\"cold\"],\n \"ir.verify\": [\"result\", \"blocking\"],\n \"ir.refresh\": [\"bytes\"],\n} as const;\n\nexport type IrMarkerName = keyof typeof IR_MARKERS;\n\n// Per-module / per-dep sub-marks (§3): a defined aggregate plus a `[…]` selector,\n// e.g. `ir.transpile.mod[/src/App.tsx]` or `ir.deps.pkg[react@18.2.0]`. A sub-mark\n// inherits its aggregate's attribute schema.\nconst SUBMARK_RE = /^(ir\\.transpile\\.mod|ir\\.deps\\.pkg)\\[[^\\]]+\\]$/;\nconst submarkAggregate = (name: string): IrMarkerName | null =>\n name.startsWith(\"ir.transpile.mod\") ? \"ir.transpile\" : name.startsWith(\"ir.deps.pkg\") ? \"ir.deps\" : null;\n\n/** Is `name` a defined top-level `ir.*` marker (not a sub-mark)? */\nexport const isIrMarkerName = (name: string): name is IrMarkerName =>\n Object.prototype.hasOwnProperty.call(IR_MARKERS, name);\n\n/** Is `name` an accepted marker name — a defined top-level marker OR a recognized\n * per-module/per-dep sub-mark? */\nexport const isAllowedMarkerName = (name: string): boolean =>\n isIrMarkerName(name) || SUBMARK_RE.test(name);\n\n/** A marker forwarded across the origin boundary (§3.2): a name, the sandbox-side\n * `performance.now()` timestamp, and the optional attribute payload. */\nexport interface ForwardedMarker {\n name: string;\n /** Sandbox-relative timestamp (`performance.now()`) at emission (§3.2). */\n at: number;\n attrs?: Record<string, unknown>;\n}\n\n/**\n * The LP-5 vocabulary allowlist (pure). Accept a forwarded marker ONLY if its name\n * is in the vocabulary AND every attribute key is in that marker's schema. An\n * unknown name — or a defined name carrying an out-of-schema attribute key — is\n * DROPPED (returns `null`), never recorded. This is the gate against an untrusted\n * sandbox minting arbitrary names/values into the host timeline (an injection\n * surface for dashboards / the deferred RUM endpoint). Sub-marks inherit their\n * aggregate's schema.\n */\nexport function validateMarker(m: ForwardedMarker | null | undefined): ForwardedMarker | null {\n if (!m || typeof m.name !== \"string\") return null;\n if (typeof m.at !== \"number\" || !Number.isFinite(m.at)) return null;\n if (!isAllowedMarkerName(m.name)) return null;\n const base: IrMarkerName = isIrMarkerName(m.name) ? m.name : submarkAggregate(m.name)!;\n const allowed: readonly string[] = IR_MARKERS[base];\n const attrs = m.attrs;\n if (attrs !== undefined) {\n if (typeof attrs !== \"object\" || attrs === null) return null;\n for (const key of Object.keys(attrs)) {\n if (!allowed.includes(key)) return null; // out-of-schema attribute → drop the whole marker\n }\n }\n return { name: m.name, at: m.at, ...(attrs !== undefined ? { attrs } : {}) };\n}\n\n/**\n * `ir.interactive = max(rootRenderCommit, reportReady)` (LP2-3). An app-called\n * `reportReady()` may only DELAY interactive — never advance it before the root\n * render commits. A `reportReady()` that fires early is recorded but takes effect\n * at the commit; if the app never reports, the commit alone stands. Budgets bind to\n * the later of the two, so an app can't game its budget by declaring itself ready\n * before it has rendered anything. PURE.\n */\nexport function resolveInteractive(rootRenderCommitAt: number, reportReadyAt?: number): number {\n if (reportReadyAt === undefined) return rootRenderCommitAt;\n return Math.max(rootRenderCommitAt, reportReadyAt);\n}\n"],"mappings":"AAgBO,MAAM,aAAa;AAAA,EACxB,WAAW,CAAC,OAAO,YAAY,MAAM,QAAQ,OAAO,SAAS;AAAA,EAC7D,YAAY,CAAC,UAAU,SAAS,gBAAgB,YAAY,YAAY;AAAA,EACxE,YAAY,CAAC,gBAAgB,gBAAgB;AAAA,EAC7C,mBAAmB,CAAC;AAAA,EACpB,gBAAgB,CAAC,eAAe,YAAY,WAAW,UAAU;AAAA,EACjE,WAAW,CAAC,YAAY,SAAS,gBAAgB,YAAY,KAAK;AAAA,EAClE,WAAW,CAAC,aAAa;AAAA,EACzB,UAAU,CAAC;AAAA,EACX,kBAAkB,CAAC,MAAM;AAAA,EACzB,aAAa,CAAC,UAAU,UAAU;AAAA,EAClC,cAAc,CAAC,OAAO;AACxB;
|
|
1
|
+
{"version":3,"sources":["../src/irMarkers.ts"],"sourcesContent":["// The `ir.*` load-profiling marker vocabulary + the host-side allowlist + the\n// `ir.interactive` precedence rule (LOAD_PROFILING_SPEC §3/§3.1, R3-46).\n//\n// This module is PURE and dependency-free on purpose: the spec (§3.1) requires the\n// host's vocabulary allowlist to be MIRRORED from this same definition (\"additions\n// to the vocabulary are spec amendments, mirrored in the host allowlist in the same\n// change\"). Keeping the table + validator here, with no transport/React imports,\n// lets the sandbox side (this SDK) and the host (site-main, its own mirrored copy)\n// share an identical contract that is unit-tested in both places.\n\n/**\n * Each `ir.*` marker name → the attribute keys its payload may carry (the §3\n * table). A forwarded marker is accepted only if BOTH its name is defined here AND\n * every attribute key it carries is in that marker's allowed set (LP-5). An empty\n * array means \"no attributes\" (a bare mark).\n */\nexport const IR_MARKERS = {\n \"ir.open\": [\"url\", \"provider\", \"ns\", \"repo\", \"ref\", \"refKind\"],\n \"ir.fetch\": [\"source\", \"bytes\", \"requestCount\", \"cacheHit\", \"httpStatus\"],\n \"ir.mount\": [\"phantomCount\", \"writablePrimed\"],\n \"ir.sandbox.boot\": [],\n \"ir.transpile\": [\"moduleCount\", \"cacheHit\", \"bytesIn\", \"bytesOut\"],\n \"ir.deps\": [\"depCount\", \"bytes\", \"requestCount\", \"cacheHit\", \"cdn\"],\n \"ir.eval\": [\"moduleCount\"],\n \"ir.fmp\": [],\n \"ir.interactive\": [\"cold\"],\n \"ir.verify\": [\"result\", \"blocking\"],\n \"ir.refresh\": [\"bytes\"],\n} as const;\n\n/** A canonical `ir.*` load-profiling marker name (a key of {@link IR_MARKERS}). */\nexport type IrMarkerName = keyof typeof IR_MARKERS;\n\n// Per-module / per-dep sub-marks (§3): a defined aggregate plus a `[…]` selector,\n// e.g. `ir.transpile.mod[/src/App.tsx]` or `ir.deps.pkg[react@18.2.0]`. A sub-mark\n// inherits its aggregate's attribute schema.\nconst SUBMARK_RE = /^(ir\\.transpile\\.mod|ir\\.deps\\.pkg)\\[[^\\]]+\\]$/;\nconst submarkAggregate = (name: string): IrMarkerName | null =>\n name.startsWith(\"ir.transpile.mod\") ? \"ir.transpile\" : name.startsWith(\"ir.deps.pkg\") ? \"ir.deps\" : null;\n\n/** Is `name` a defined top-level `ir.*` marker (not a sub-mark)? */\nexport const isIrMarkerName = (name: string): name is IrMarkerName =>\n Object.prototype.hasOwnProperty.call(IR_MARKERS, name);\n\n/** Is `name` an accepted marker name — a defined top-level marker OR a recognized\n * per-module/per-dep sub-mark? */\nexport const isAllowedMarkerName = (name: string): boolean =>\n isIrMarkerName(name) || SUBMARK_RE.test(name);\n\n/** A marker forwarded across the origin boundary (§3.2): a name, the sandbox-side\n * `performance.now()` timestamp, and the optional attribute payload. */\nexport interface ForwardedMarker {\n name: string;\n /** Sandbox-relative timestamp (`performance.now()`) at emission (§3.2). */\n at: number;\n attrs?: Record<string, unknown>;\n}\n\n/**\n * The LP-5 vocabulary allowlist (pure). Accept a forwarded marker ONLY if its name\n * is in the vocabulary AND every attribute key is in that marker's schema. An\n * unknown name — or a defined name carrying an out-of-schema attribute key — is\n * DROPPED (returns `null`), never recorded. This is the gate against an untrusted\n * sandbox minting arbitrary names/values into the host timeline (an injection\n * surface for dashboards / the deferred RUM endpoint). Sub-marks inherit their\n * aggregate's schema.\n */\nexport function validateMarker(m: ForwardedMarker | null | undefined): ForwardedMarker | null {\n if (!m || typeof m.name !== \"string\") return null;\n if (typeof m.at !== \"number\" || !Number.isFinite(m.at)) return null;\n if (!isAllowedMarkerName(m.name)) return null;\n const base: IrMarkerName = isIrMarkerName(m.name) ? m.name : submarkAggregate(m.name)!;\n const allowed: readonly string[] = IR_MARKERS[base];\n const attrs = m.attrs;\n if (attrs !== undefined) {\n if (typeof attrs !== \"object\" || attrs === null) return null;\n for (const key of Object.keys(attrs)) {\n if (!allowed.includes(key)) return null; // out-of-schema attribute → drop the whole marker\n }\n }\n return { name: m.name, at: m.at, ...(attrs !== undefined ? { attrs } : {}) };\n}\n\n/**\n * `ir.interactive = max(rootRenderCommit, reportReady)` (LP2-3). An app-called\n * `reportReady()` may only DELAY interactive — never advance it before the root\n * render commits. A `reportReady()` that fires early is recorded but takes effect\n * at the commit; if the app never reports, the commit alone stands. Budgets bind to\n * the later of the two, so an app can't game its budget by declaring itself ready\n * before it has rendered anything. PURE.\n */\nexport function resolveInteractive(rootRenderCommitAt: number, reportReadyAt?: number): number {\n if (reportReadyAt === undefined) return rootRenderCommitAt;\n return Math.max(rootRenderCommitAt, reportReadyAt);\n}\n"],"mappings":"AAgBO,MAAM,aAAa;AAAA,EACxB,WAAW,CAAC,OAAO,YAAY,MAAM,QAAQ,OAAO,SAAS;AAAA,EAC7D,YAAY,CAAC,UAAU,SAAS,gBAAgB,YAAY,YAAY;AAAA,EACxE,YAAY,CAAC,gBAAgB,gBAAgB;AAAA,EAC7C,mBAAmB,CAAC;AAAA,EACpB,gBAAgB,CAAC,eAAe,YAAY,WAAW,UAAU;AAAA,EACjE,WAAW,CAAC,YAAY,SAAS,gBAAgB,YAAY,KAAK;AAAA,EAClE,WAAW,CAAC,aAAa;AAAA,EACzB,UAAU,CAAC;AAAA,EACX,kBAAkB,CAAC,MAAM;AAAA,EACzB,aAAa,CAAC,UAAU,UAAU;AAAA,EAClC,cAAc,CAAC,OAAO;AACxB;AAQA,MAAM,aAAa;AACnB,MAAM,mBAAmB,CAAC,SACxB,KAAK,WAAW,kBAAkB,IAAI,iBAAiB,KAAK,WAAW,aAAa,IAAI,YAAY;AAG/F,MAAM,iBAAiB,CAAC,SAC7B,OAAO,UAAU,eAAe,KAAK,YAAY,IAAI;AAIhD,MAAM,sBAAsB,CAAC,SAClC,eAAe,IAAI,KAAK,WAAW,KAAK,IAAI;AAoBvC,SAAS,eAAe,GAA+D;AAC5F,MAAI,CAAC,KAAK,OAAO,EAAE,SAAS,SAAU,QAAO;AAC7C,MAAI,OAAO,EAAE,OAAO,YAAY,CAAC,OAAO,SAAS,EAAE,EAAE,EAAG,QAAO;AAC/D,MAAI,CAAC,oBAAoB,EAAE,IAAI,EAAG,QAAO;AACzC,QAAM,OAAqB,eAAe,EAAE,IAAI,IAAI,EAAE,OAAO,iBAAiB,EAAE,IAAI;AACpF,QAAM,UAA6B,WAAW,IAAI;AAClD,QAAM,QAAQ,EAAE;AAChB,MAAI,UAAU,QAAW;AACvB,QAAI,OAAO,UAAU,YAAY,UAAU,KAAM,QAAO;AACxD,eAAW,OAAO,OAAO,KAAK,KAAK,GAAG;AACpC,UAAI,CAAC,QAAQ,SAAS,GAAG,EAAG,QAAO;AAAA,IACrC;AAAA,EACF;AACA,SAAO,EAAE,MAAM,EAAE,MAAM,IAAI,EAAE,IAAI,GAAI,UAAU,SAAY,EAAE,MAAM,IAAI,CAAC,EAAG;AAC7E;AAUO,SAAS,mBAAmB,oBAA4B,eAAgC;AAC7F,MAAI,kBAAkB,OAAW,QAAO;AACxC,SAAO,KAAK,IAAI,oBAAoB,aAAa;AACnD;","names":[]}
|
package/dist/llm.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/llm.ts"],"sourcesContent":["// Provider-agnostic LLM chat — the `llm.chat@1` slot (SERVICE_PROVIDERS_SPEC;\n// LLM_AND_AGENTS_SPEC §8 D5).\n//\n// An app calls ONE chat slot and never worries about which provider the user has a\n// key for: the HOST resolves which vendor answers from the key the user holds\n// (`SecretView.boundOrigin`) plus their `preferredImplementation` choice, normalizes\n// the wire format, injects the key host-side at the §6 net:fetch point (the\n// look-at-nothing proxy), and streams normalized deltas back. The app never names a\n// vendor, never sees the key, and needs NO `net:fetch`/`secrets` grant of its own —\n// only the `llm:chat` capability (elevated, app-scoped: a fork earns it by consent).\n//\n// Inert until the host implements `protocol-llm` (the `chat` stream) + the\n// `llm-provider` describe channel; the contract ships here so apps (the file-explorer\n// summarize fork) can be written against it — exactly how `secrets.ts` shipped ahead\n// of `protocol-secrets`.\nimport { invokeStream } from './catalog';\nimport { createPushChannel } from './pushChannel';\n\nexport type ChatRole = 'system' | 'user' | 'assistant' | 'tool';\n\n/** A part of a message. `image` is only honored when the resolved provider\n * advertises `features.vision` (§2.5) — branch on {@link describeChat} first. */\nexport type ContentPart =\n | { type: 'text'; text: string }\n | { type: 'image'; mimeType: string; data: string }; // data: base64, no data: URL prefix\n\nexport interface ChatMessage {\n role: ChatRole;\n content: ContentPart[];\n}\n\n/** A tool the model may call — honored only when `features.tools`. */\nexport interface ToolDef {\n name: string;\n description?: string;\n /** JSON-Schema for the tool's arguments. */\n inputSchema: Record<string, unknown>;\n}\n\nexport interface ChatRequest {\n messages: ChatMessage[];\n /** Honored only when the resolved provider advertises `features.tools`. */\n tools?: ToolDef[];\n /** `'json'` honored only when `features.jsonMode`. Defaults to `'text'`. */\n responseFormat?: 'text' | 'json';\n maxTokens?: number;\n /** An ABSTRACT tier hint, never a vendor model id — the host maps it to a concrete\n * model on the resolved provider. Omit to take the provider's default. */\n modelHint?: 'fast' | 'smart';\n}\n\n/** One streamed chunk. Consumers typically accumulate `text-delta`s. */\nexport type ChatDelta =\n | { type: 'text-delta'; text: string }\n | { type: 'tool-call'; id: string; name: string; input: unknown }\n | { type: 'usage'; inputTokens: number; outputTokens: number };\n\nexport type ChatStopReason = 'end' | 'length' | 'tool' | 'filtered';\n\n/** The terminal value of the {@link chat} stream. */\nexport interface ChatResult {\n stopReason: ChatStopReason;\n}\n\n/**\n * Stream a chat completion from whichever provider the user has configured.\n *\n * ```ts\n * let summary = '';\n * for await (const d of chat({ messages: [{ role: 'user', content: [{ type: 'text', text }] }] })) {\n * if (d.type === 'text-delta') summary += d.text;\n * }\n * ```\n *\n * Requires the `llm:chat` capability. If no provider is bound the host fails the\n * stream into the SP-7 connect-me prompt (the user adds a key) — the generator\n * throws with `code: 'auth-required'`; an un-granted call throws `forbidden`.\n */\nexport function chat(req: ChatRequest): AsyncGenerator<ChatDelta, ChatResult, void> {\n return invokeStream<ChatDelta, ChatResult>('llm:chat', req as unknown as Record<string, unknown>);\n}\n\n/** The resolved provider's advertised abilities (SERVICE_PROVIDERS_SPEC §2.5) — read\n * to branch/degrade (offer image upload only when `vision`). */\nexport interface ChatFeatures {\n vision: boolean;\n tools: boolean;\n jsonMode: boolean;\n maxContextTokens: number;\n}\n\n/** Info about the provider the host resolved for this app. `null` when no provider\n * is bound (SP-7: prompt the user to add a key before calling {@link chat}). */\nexport interface ChatProviderInfo {\n /** Opaque provider id, e.g. `llm.chat.anthropic` — never a vendor secret or model id. */\n providerId: string;\n /** True for Host-proxied providers (host-vouched, SP-9); false for app-level ones,\n * whose `features` are an untrusted claim. */\n hostVouched: boolean;\n features: ChatFeatures;\n}\n\n// The `llm-provider` describe channel (Recipe A): the host pushes the resolved\n// provider info on change and replays it on register-frame, gated by `llm:chat`.\n// A message with no `provider` key is ignored; an explicit `null` means \"no provider\n// bound\" (distinct from \"not yet answered\", which keeps the `initial` null).\nconst channel = createPushChannel<ChatProviderInfo | null>({\n pushType: 'llm-provider',\n requestType: 'request-llm-provider',\n initial: null,\n parse: (msg) =>\n 'provider' in msg ? (msg.provider as ChatProviderInfo | null) : undefined,\n});\n\n/** The provider the host resolved for this app (or `null` if none bound). Poll for a\n * one-off read; use {@link onChatProviderChange}/{@link useChatProvider} to react. */\nexport const describeChat = (): ChatProviderInfo | null => channel.get();\n\n/** Subscribe to provider changes (key added/revoked, preference changed). Invoked\n * immediately with the current value, then on every change. Returns unsubscribe. */\nexport const onChatProviderChange = (\n listener: (provider: ChatProviderInfo | null) => void,\n): (() => void) => channel.onChange(listener);\n\n/** React hook returning the resolved chat provider (or `null`), re-rendering on\n * change — gate the summarize affordance on `provider !== null`. */\nexport const useChatProvider = (): ChatProviderInfo | null => channel.use();\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAeA,qBAA6B;AAC7B,yBAAkC;
|
|
1
|
+
{"version":3,"sources":["../src/llm.ts"],"sourcesContent":["// Provider-agnostic LLM chat — the `llm.chat@1` slot (SERVICE_PROVIDERS_SPEC;\n// LLM_AND_AGENTS_SPEC §8 D5).\n//\n// An app calls ONE chat slot and never worries about which provider the user has a\n// key for: the HOST resolves which vendor answers from the key the user holds\n// (`SecretView.boundOrigin`) plus their `preferredImplementation` choice, normalizes\n// the wire format, injects the key host-side at the §6 net:fetch point (the\n// look-at-nothing proxy), and streams normalized deltas back. The app never names a\n// vendor, never sees the key, and needs NO `net:fetch`/`secrets` grant of its own —\n// only the `llm:chat` capability (elevated, app-scoped: a fork earns it by consent).\n//\n// Inert until the host implements `protocol-llm` (the `chat` stream) + the\n// `llm-provider` describe channel; the contract ships here so apps (the file-explorer\n// summarize fork) can be written against it — exactly how `secrets.ts` shipped ahead\n// of `protocol-secrets`.\nimport { invokeStream } from './catalog';\nimport { createPushChannel } from './pushChannel';\n\n/** Who authored a {@link ChatMessage}. */\nexport type ChatRole = 'system' | 'user' | 'assistant' | 'tool';\n\n/** A part of a message. `image` is only honored when the resolved provider\n * advertises `features.vision` (§2.5) — branch on {@link describeChat} first. */\nexport type ContentPart =\n | { type: 'text'; text: string }\n | { type: 'image'; mimeType: string; data: string }; // data: base64, no data: URL prefix\n\n/** One message in a {@link ChatRequest}: a role plus its content parts. */\nexport interface ChatMessage {\n role: ChatRole;\n content: ContentPart[];\n}\n\n/** A tool the model may call — honored only when `features.tools`. */\nexport interface ToolDef {\n name: string;\n description?: string;\n /** JSON-Schema for the tool's arguments. */\n inputSchema: Record<string, unknown>;\n}\n\n/** A host-brokered chat completion request: the messages plus optional tools,\n * response format, and model hint (each honored per the provider's features). */\nexport interface ChatRequest {\n messages: ChatMessage[];\n /** Honored only when the resolved provider advertises `features.tools`. */\n tools?: ToolDef[];\n /** `'json'` honored only when `features.jsonMode`. Defaults to `'text'`. */\n responseFormat?: 'text' | 'json';\n maxTokens?: number;\n /** An ABSTRACT tier hint, never a vendor model id — the host maps it to a concrete\n * model on the resolved provider. Omit to take the provider's default. */\n modelHint?: 'fast' | 'smart';\n}\n\n/** One streamed chunk. Consumers typically accumulate `text-delta`s. */\nexport type ChatDelta =\n | { type: 'text-delta'; text: string }\n | { type: 'tool-call'; id: string; name: string; input: unknown }\n | { type: 'usage'; inputTokens: number; outputTokens: number };\n\n/** Why generation stopped: natural `end`, `length` cap, a `tool` call, or content `filtered`. */\nexport type ChatStopReason = 'end' | 'length' | 'tool' | 'filtered';\n\n/** The terminal value of the {@link chat} stream. */\nexport interface ChatResult {\n stopReason: ChatStopReason;\n}\n\n/**\n * Stream a chat completion from whichever provider the user has configured.\n *\n * ```ts\n * let summary = '';\n * for await (const d of chat({ messages: [{ role: 'user', content: [{ type: 'text', text }] }] })) {\n * if (d.type === 'text-delta') summary += d.text;\n * }\n * ```\n *\n * Requires the `llm:chat` capability. If no provider is bound the host fails the\n * stream into the SP-7 connect-me prompt (the user adds a key) — the generator\n * throws with `code: 'auth-required'`; an un-granted call throws `forbidden`.\n */\nexport function chat(req: ChatRequest): AsyncGenerator<ChatDelta, ChatResult, void> {\n return invokeStream<ChatDelta, ChatResult>('llm:chat', req as unknown as Record<string, unknown>);\n}\n\n/** The resolved provider's advertised abilities (SERVICE_PROVIDERS_SPEC §2.5) — read\n * to branch/degrade (offer image upload only when `vision`). */\nexport interface ChatFeatures {\n vision: boolean;\n tools: boolean;\n jsonMode: boolean;\n maxContextTokens: number;\n}\n\n/** Info about the provider the host resolved for this app. `null` when no provider\n * is bound (SP-7: prompt the user to add a key before calling {@link chat}). */\nexport interface ChatProviderInfo {\n /** Opaque provider id, e.g. `llm.chat.anthropic` — never a vendor secret or model id. */\n providerId: string;\n /** True for Host-proxied providers (host-vouched, SP-9); false for app-level ones,\n * whose `features` are an untrusted claim. */\n hostVouched: boolean;\n features: ChatFeatures;\n}\n\n// The `llm-provider` describe channel (Recipe A): the host pushes the resolved\n// provider info on change and replays it on register-frame, gated by `llm:chat`.\n// A message with no `provider` key is ignored; an explicit `null` means \"no provider\n// bound\" (distinct from \"not yet answered\", which keeps the `initial` null).\nconst channel = createPushChannel<ChatProviderInfo | null>({\n pushType: 'llm-provider',\n requestType: 'request-llm-provider',\n initial: null,\n parse: (msg) =>\n 'provider' in msg ? (msg.provider as ChatProviderInfo | null) : undefined,\n});\n\n/** The provider the host resolved for this app (or `null` if none bound). Poll for a\n * one-off read; use {@link onChatProviderChange}/{@link useChatProvider} to react. */\nexport const describeChat = (): ChatProviderInfo | null => channel.get();\n\n/** Subscribe to provider changes (key added/revoked, preference changed). Invoked\n * immediately with the current value, then on every change. Returns unsubscribe. */\nexport const onChatProviderChange = (\n listener: (provider: ChatProviderInfo | null) => void,\n): (() => void) => channel.onChange(listener);\n\n/** React hook returning the resolved chat provider (or `null`), re-rendering on\n * change — gate the summarize affordance on `provider !== null`. */\nexport const useChatProvider = (): ChatProviderInfo | null => channel.use();\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAeA,qBAA6B;AAC7B,yBAAkC;AAmE3B,SAAS,KAAK,KAA+D;AAClF,aAAO,6BAAoC,YAAY,GAAyC;AAClG;AA0BA,MAAM,cAAU,sCAA2C;AAAA,EACzD,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS;AAAA,EACT,OAAO,CAAC,QACN,cAAc,MAAO,IAAI,WAAuC;AACpE,CAAC;AAIM,MAAM,eAAe,MAA+B,QAAQ,IAAI;AAIhE,MAAM,uBAAuB,CAClC,aACiB,QAAQ,SAAS,QAAQ;AAIrC,MAAM,kBAAkB,MAA+B,QAAQ,IAAI;","names":[]}
|
package/dist/llm.d.cts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** Who authored a {@link ChatMessage}. */
|
|
1
2
|
type ChatRole = 'system' | 'user' | 'assistant' | 'tool';
|
|
2
3
|
/** A part of a message. `image` is only honored when the resolved provider
|
|
3
4
|
* advertises `features.vision` (§2.5) — branch on {@link describeChat} first. */
|
|
@@ -9,6 +10,7 @@ type ContentPart = {
|
|
|
9
10
|
mimeType: string;
|
|
10
11
|
data: string;
|
|
11
12
|
};
|
|
13
|
+
/** One message in a {@link ChatRequest}: a role plus its content parts. */
|
|
12
14
|
interface ChatMessage {
|
|
13
15
|
role: ChatRole;
|
|
14
16
|
content: ContentPart[];
|
|
@@ -20,6 +22,8 @@ interface ToolDef {
|
|
|
20
22
|
/** JSON-Schema for the tool's arguments. */
|
|
21
23
|
inputSchema: Record<string, unknown>;
|
|
22
24
|
}
|
|
25
|
+
/** A host-brokered chat completion request: the messages plus optional tools,
|
|
26
|
+
* response format, and model hint (each honored per the provider's features). */
|
|
23
27
|
interface ChatRequest {
|
|
24
28
|
messages: ChatMessage[];
|
|
25
29
|
/** Honored only when the resolved provider advertises `features.tools`. */
|
|
@@ -45,6 +49,7 @@ type ChatDelta = {
|
|
|
45
49
|
inputTokens: number;
|
|
46
50
|
outputTokens: number;
|
|
47
51
|
};
|
|
52
|
+
/** Why generation stopped: natural `end`, `length` cap, a `tool` call, or content `filtered`. */
|
|
48
53
|
type ChatStopReason = 'end' | 'length' | 'tool' | 'filtered';
|
|
49
54
|
/** The terminal value of the {@link chat} stream. */
|
|
50
55
|
interface ChatResult {
|
package/dist/llm.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/** Who authored a {@link ChatMessage}. */
|
|
1
2
|
type ChatRole = 'system' | 'user' | 'assistant' | 'tool';
|
|
2
3
|
/** A part of a message. `image` is only honored when the resolved provider
|
|
3
4
|
* advertises `features.vision` (§2.5) — branch on {@link describeChat} first. */
|
|
@@ -9,6 +10,7 @@ type ContentPart = {
|
|
|
9
10
|
mimeType: string;
|
|
10
11
|
data: string;
|
|
11
12
|
};
|
|
13
|
+
/** One message in a {@link ChatRequest}: a role plus its content parts. */
|
|
12
14
|
interface ChatMessage {
|
|
13
15
|
role: ChatRole;
|
|
14
16
|
content: ContentPart[];
|
|
@@ -20,6 +22,8 @@ interface ToolDef {
|
|
|
20
22
|
/** JSON-Schema for the tool's arguments. */
|
|
21
23
|
inputSchema: Record<string, unknown>;
|
|
22
24
|
}
|
|
25
|
+
/** A host-brokered chat completion request: the messages plus optional tools,
|
|
26
|
+
* response format, and model hint (each honored per the provider's features). */
|
|
23
27
|
interface ChatRequest {
|
|
24
28
|
messages: ChatMessage[];
|
|
25
29
|
/** Honored only when the resolved provider advertises `features.tools`. */
|
|
@@ -45,6 +49,7 @@ type ChatDelta = {
|
|
|
45
49
|
inputTokens: number;
|
|
46
50
|
outputTokens: number;
|
|
47
51
|
};
|
|
52
|
+
/** Why generation stopped: natural `end`, `length` cap, a `tool` call, or content `filtered`. */
|
|
48
53
|
type ChatStopReason = 'end' | 'length' | 'tool' | 'filtered';
|
|
49
54
|
/** The terminal value of the {@link chat} stream. */
|
|
50
55
|
interface ChatResult {
|
package/dist/llm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/llm.ts"],"sourcesContent":["// Provider-agnostic LLM chat — the `llm.chat@1` slot (SERVICE_PROVIDERS_SPEC;\n// LLM_AND_AGENTS_SPEC §8 D5).\n//\n// An app calls ONE chat slot and never worries about which provider the user has a\n// key for: the HOST resolves which vendor answers from the key the user holds\n// (`SecretView.boundOrigin`) plus their `preferredImplementation` choice, normalizes\n// the wire format, injects the key host-side at the §6 net:fetch point (the\n// look-at-nothing proxy), and streams normalized deltas back. The app never names a\n// vendor, never sees the key, and needs NO `net:fetch`/`secrets` grant of its own —\n// only the `llm:chat` capability (elevated, app-scoped: a fork earns it by consent).\n//\n// Inert until the host implements `protocol-llm` (the `chat` stream) + the\n// `llm-provider` describe channel; the contract ships here so apps (the file-explorer\n// summarize fork) can be written against it — exactly how `secrets.ts` shipped ahead\n// of `protocol-secrets`.\nimport { invokeStream } from './catalog';\nimport { createPushChannel } from './pushChannel';\n\nexport type ChatRole = 'system' | 'user' | 'assistant' | 'tool';\n\n/** A part of a message. `image` is only honored when the resolved provider\n * advertises `features.vision` (§2.5) — branch on {@link describeChat} first. */\nexport type ContentPart =\n | { type: 'text'; text: string }\n | { type: 'image'; mimeType: string; data: string }; // data: base64, no data: URL prefix\n\nexport interface ChatMessage {\n role: ChatRole;\n content: ContentPart[];\n}\n\n/** A tool the model may call — honored only when `features.tools`. */\nexport interface ToolDef {\n name: string;\n description?: string;\n /** JSON-Schema for the tool's arguments. */\n inputSchema: Record<string, unknown>;\n}\n\nexport interface ChatRequest {\n messages: ChatMessage[];\n /** Honored only when the resolved provider advertises `features.tools`. */\n tools?: ToolDef[];\n /** `'json'` honored only when `features.jsonMode`. Defaults to `'text'`. */\n responseFormat?: 'text' | 'json';\n maxTokens?: number;\n /** An ABSTRACT tier hint, never a vendor model id — the host maps it to a concrete\n * model on the resolved provider. Omit to take the provider's default. */\n modelHint?: 'fast' | 'smart';\n}\n\n/** One streamed chunk. Consumers typically accumulate `text-delta`s. */\nexport type ChatDelta =\n | { type: 'text-delta'; text: string }\n | { type: 'tool-call'; id: string; name: string; input: unknown }\n | { type: 'usage'; inputTokens: number; outputTokens: number };\n\nexport type ChatStopReason = 'end' | 'length' | 'tool' | 'filtered';\n\n/** The terminal value of the {@link chat} stream. */\nexport interface ChatResult {\n stopReason: ChatStopReason;\n}\n\n/**\n * Stream a chat completion from whichever provider the user has configured.\n *\n * ```ts\n * let summary = '';\n * for await (const d of chat({ messages: [{ role: 'user', content: [{ type: 'text', text }] }] })) {\n * if (d.type === 'text-delta') summary += d.text;\n * }\n * ```\n *\n * Requires the `llm:chat` capability. If no provider is bound the host fails the\n * stream into the SP-7 connect-me prompt (the user adds a key) — the generator\n * throws with `code: 'auth-required'`; an un-granted call throws `forbidden`.\n */\nexport function chat(req: ChatRequest): AsyncGenerator<ChatDelta, ChatResult, void> {\n return invokeStream<ChatDelta, ChatResult>('llm:chat', req as unknown as Record<string, unknown>);\n}\n\n/** The resolved provider's advertised abilities (SERVICE_PROVIDERS_SPEC §2.5) — read\n * to branch/degrade (offer image upload only when `vision`). */\nexport interface ChatFeatures {\n vision: boolean;\n tools: boolean;\n jsonMode: boolean;\n maxContextTokens: number;\n}\n\n/** Info about the provider the host resolved for this app. `null` when no provider\n * is bound (SP-7: prompt the user to add a key before calling {@link chat}). */\nexport interface ChatProviderInfo {\n /** Opaque provider id, e.g. `llm.chat.anthropic` — never a vendor secret or model id. */\n providerId: string;\n /** True for Host-proxied providers (host-vouched, SP-9); false for app-level ones,\n * whose `features` are an untrusted claim. */\n hostVouched: boolean;\n features: ChatFeatures;\n}\n\n// The `llm-provider` describe channel (Recipe A): the host pushes the resolved\n// provider info on change and replays it on register-frame, gated by `llm:chat`.\n// A message with no `provider` key is ignored; an explicit `null` means \"no provider\n// bound\" (distinct from \"not yet answered\", which keeps the `initial` null).\nconst channel = createPushChannel<ChatProviderInfo | null>({\n pushType: 'llm-provider',\n requestType: 'request-llm-provider',\n initial: null,\n parse: (msg) =>\n 'provider' in msg ? (msg.provider as ChatProviderInfo | null) : undefined,\n});\n\n/** The provider the host resolved for this app (or `null` if none bound). Poll for a\n * one-off read; use {@link onChatProviderChange}/{@link useChatProvider} to react. */\nexport const describeChat = (): ChatProviderInfo | null => channel.get();\n\n/** Subscribe to provider changes (key added/revoked, preference changed). Invoked\n * immediately with the current value, then on every change. Returns unsubscribe. */\nexport const onChatProviderChange = (\n listener: (provider: ChatProviderInfo | null) => void,\n): (() => void) => channel.onChange(listener);\n\n/** React hook returning the resolved chat provider (or `null`), re-rendering on\n * change — gate the summarize affordance on `provider !== null`. */\nexport const useChatProvider = (): ChatProviderInfo | null => channel.use();\n"],"mappings":"AAeA,SAAS,oBAAoB;AAC7B,SAAS,yBAAyB;
|
|
1
|
+
{"version":3,"sources":["../src/llm.ts"],"sourcesContent":["// Provider-agnostic LLM chat — the `llm.chat@1` slot (SERVICE_PROVIDERS_SPEC;\n// LLM_AND_AGENTS_SPEC §8 D5).\n//\n// An app calls ONE chat slot and never worries about which provider the user has a\n// key for: the HOST resolves which vendor answers from the key the user holds\n// (`SecretView.boundOrigin`) plus their `preferredImplementation` choice, normalizes\n// the wire format, injects the key host-side at the §6 net:fetch point (the\n// look-at-nothing proxy), and streams normalized deltas back. The app never names a\n// vendor, never sees the key, and needs NO `net:fetch`/`secrets` grant of its own —\n// only the `llm:chat` capability (elevated, app-scoped: a fork earns it by consent).\n//\n// Inert until the host implements `protocol-llm` (the `chat` stream) + the\n// `llm-provider` describe channel; the contract ships here so apps (the file-explorer\n// summarize fork) can be written against it — exactly how `secrets.ts` shipped ahead\n// of `protocol-secrets`.\nimport { invokeStream } from './catalog';\nimport { createPushChannel } from './pushChannel';\n\n/** Who authored a {@link ChatMessage}. */\nexport type ChatRole = 'system' | 'user' | 'assistant' | 'tool';\n\n/** A part of a message. `image` is only honored when the resolved provider\n * advertises `features.vision` (§2.5) — branch on {@link describeChat} first. */\nexport type ContentPart =\n | { type: 'text'; text: string }\n | { type: 'image'; mimeType: string; data: string }; // data: base64, no data: URL prefix\n\n/** One message in a {@link ChatRequest}: a role plus its content parts. */\nexport interface ChatMessage {\n role: ChatRole;\n content: ContentPart[];\n}\n\n/** A tool the model may call — honored only when `features.tools`. */\nexport interface ToolDef {\n name: string;\n description?: string;\n /** JSON-Schema for the tool's arguments. */\n inputSchema: Record<string, unknown>;\n}\n\n/** A host-brokered chat completion request: the messages plus optional tools,\n * response format, and model hint (each honored per the provider's features). */\nexport interface ChatRequest {\n messages: ChatMessage[];\n /** Honored only when the resolved provider advertises `features.tools`. */\n tools?: ToolDef[];\n /** `'json'` honored only when `features.jsonMode`. Defaults to `'text'`. */\n responseFormat?: 'text' | 'json';\n maxTokens?: number;\n /** An ABSTRACT tier hint, never a vendor model id — the host maps it to a concrete\n * model on the resolved provider. Omit to take the provider's default. */\n modelHint?: 'fast' | 'smart';\n}\n\n/** One streamed chunk. Consumers typically accumulate `text-delta`s. */\nexport type ChatDelta =\n | { type: 'text-delta'; text: string }\n | { type: 'tool-call'; id: string; name: string; input: unknown }\n | { type: 'usage'; inputTokens: number; outputTokens: number };\n\n/** Why generation stopped: natural `end`, `length` cap, a `tool` call, or content `filtered`. */\nexport type ChatStopReason = 'end' | 'length' | 'tool' | 'filtered';\n\n/** The terminal value of the {@link chat} stream. */\nexport interface ChatResult {\n stopReason: ChatStopReason;\n}\n\n/**\n * Stream a chat completion from whichever provider the user has configured.\n *\n * ```ts\n * let summary = '';\n * for await (const d of chat({ messages: [{ role: 'user', content: [{ type: 'text', text }] }] })) {\n * if (d.type === 'text-delta') summary += d.text;\n * }\n * ```\n *\n * Requires the `llm:chat` capability. If no provider is bound the host fails the\n * stream into the SP-7 connect-me prompt (the user adds a key) — the generator\n * throws with `code: 'auth-required'`; an un-granted call throws `forbidden`.\n */\nexport function chat(req: ChatRequest): AsyncGenerator<ChatDelta, ChatResult, void> {\n return invokeStream<ChatDelta, ChatResult>('llm:chat', req as unknown as Record<string, unknown>);\n}\n\n/** The resolved provider's advertised abilities (SERVICE_PROVIDERS_SPEC §2.5) — read\n * to branch/degrade (offer image upload only when `vision`). */\nexport interface ChatFeatures {\n vision: boolean;\n tools: boolean;\n jsonMode: boolean;\n maxContextTokens: number;\n}\n\n/** Info about the provider the host resolved for this app. `null` when no provider\n * is bound (SP-7: prompt the user to add a key before calling {@link chat}). */\nexport interface ChatProviderInfo {\n /** Opaque provider id, e.g. `llm.chat.anthropic` — never a vendor secret or model id. */\n providerId: string;\n /** True for Host-proxied providers (host-vouched, SP-9); false for app-level ones,\n * whose `features` are an untrusted claim. */\n hostVouched: boolean;\n features: ChatFeatures;\n}\n\n// The `llm-provider` describe channel (Recipe A): the host pushes the resolved\n// provider info on change and replays it on register-frame, gated by `llm:chat`.\n// A message with no `provider` key is ignored; an explicit `null` means \"no provider\n// bound\" (distinct from \"not yet answered\", which keeps the `initial` null).\nconst channel = createPushChannel<ChatProviderInfo | null>({\n pushType: 'llm-provider',\n requestType: 'request-llm-provider',\n initial: null,\n parse: (msg) =>\n 'provider' in msg ? (msg.provider as ChatProviderInfo | null) : undefined,\n});\n\n/** The provider the host resolved for this app (or `null` if none bound). Poll for a\n * one-off read; use {@link onChatProviderChange}/{@link useChatProvider} to react. */\nexport const describeChat = (): ChatProviderInfo | null => channel.get();\n\n/** Subscribe to provider changes (key added/revoked, preference changed). Invoked\n * immediately with the current value, then on every change. Returns unsubscribe. */\nexport const onChatProviderChange = (\n listener: (provider: ChatProviderInfo | null) => void,\n): (() => void) => channel.onChange(listener);\n\n/** React hook returning the resolved chat provider (or `null`), re-rendering on\n * change — gate the summarize affordance on `provider !== null`. */\nexport const useChatProvider = (): ChatProviderInfo | null => channel.use();\n"],"mappings":"AAeA,SAAS,oBAAoB;AAC7B,SAAS,yBAAyB;AAmE3B,SAAS,KAAK,KAA+D;AAClF,SAAO,aAAoC,YAAY,GAAyC;AAClG;AA0BA,MAAM,UAAU,kBAA2C;AAAA,EACzD,UAAU;AAAA,EACV,aAAa;AAAA,EACb,SAAS;AAAA,EACT,OAAO,CAAC,QACN,cAAc,MAAO,IAAI,WAAuC;AACpE,CAAC;AAIM,MAAM,eAAe,MAA+B,QAAQ,IAAI;AAIhE,MAAM,uBAAuB,CAClC,aACiB,QAAQ,SAAS,QAAQ;AAIrC,MAAM,kBAAkB,MAA+B,QAAQ,IAAI;","names":[]}
|