@immediately-run/sdk 0.29.0 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/dist/boot.cjs +5 -1
  2. package/dist/boot.cjs.map +1 -1
  3. package/dist/boot.js +5 -1
  4. package/dist/boot.js.map +1 -1
  5. package/dist/components/Link.cjs +25 -0
  6. package/dist/components/Link.cjs.map +1 -1
  7. package/dist/components/Link.d.cts +7 -1
  8. package/dist/components/Link.d.ts +7 -1
  9. package/dist/components/Link.js +24 -0
  10. package/dist/components/Link.js.map +1 -1
  11. package/dist/components/ScrollAfterNavigation.cjs +66 -0
  12. package/dist/components/ScrollAfterNavigation.cjs.map +1 -0
  13. package/dist/components/ScrollAfterNavigation.d.cts +24 -0
  14. package/dist/components/ScrollAfterNavigation.d.ts +24 -0
  15. package/dist/components/ScrollAfterNavigation.js +41 -0
  16. package/dist/components/ScrollAfterNavigation.js.map +1 -0
  17. package/dist/components/WikiLink.cjs +10 -2
  18. package/dist/components/WikiLink.cjs.map +1 -1
  19. package/dist/components/WikiLink.d.cts +11 -3
  20. package/dist/components/WikiLink.d.ts +11 -3
  21. package/dist/components/WikiLink.js +10 -2
  22. package/dist/components/WikiLink.js.map +1 -1
  23. package/dist/index.cjs +3 -1
  24. package/dist/index.cjs.map +1 -1
  25. package/dist/index.d.cts +5 -0
  26. package/dist/index.d.ts +5 -0
  27. package/dist/index.js +1 -0
  28. package/dist/index.js.map +1 -1
  29. package/dist/safeContent/SafeContent.cjs +54 -0
  30. package/dist/safeContent/SafeContent.cjs.map +1 -0
  31. package/dist/safeContent/SafeContent.d.cts +13 -0
  32. package/dist/safeContent/SafeContent.d.ts +13 -0
  33. package/dist/safeContent/SafeContent.js +30 -0
  34. package/dist/safeContent/SafeContent.js.map +1 -0
  35. package/dist/safeContent/index.cjs +43 -0
  36. package/dist/safeContent/index.cjs.map +1 -0
  37. package/dist/safeContent/index.d.cts +6 -0
  38. package/dist/safeContent/index.d.ts +6 -0
  39. package/dist/safeContent/index.js +14 -0
  40. package/dist/safeContent/index.js.map +1 -0
  41. package/dist/safeContent/parseSafeMdast.cjs +69 -0
  42. package/dist/safeContent/parseSafeMdast.cjs.map +1 -0
  43. package/dist/safeContent/parseSafeMdast.d.cts +31 -0
  44. package/dist/safeContent/parseSafeMdast.d.ts +31 -0
  45. package/dist/safeContent/parseSafeMdast.js +35 -0
  46. package/dist/safeContent/parseSafeMdast.js.map +1 -0
  47. package/dist/safeContent/renderMdast.cjs +138 -0
  48. package/dist/safeContent/renderMdast.cjs.map +1 -0
  49. package/dist/safeContent/renderMdast.d.cts +27 -0
  50. package/dist/safeContent/renderMdast.d.ts +27 -0
  51. package/dist/safeContent/renderMdast.js +114 -0
  52. package/dist/safeContent/renderMdast.js.map +1 -0
  53. package/dist/safeContent/sanitizeUrl.cjs +39 -0
  54. package/dist/safeContent/sanitizeUrl.cjs.map +1 -0
  55. package/dist/safeContent/sanitizeUrl.d.cts +9 -0
  56. package/dist/safeContent/sanitizeUrl.d.ts +9 -0
  57. package/dist/safeContent/sanitizeUrl.js +15 -0
  58. package/dist/safeContent/sanitizeUrl.js.map +1 -0
  59. package/dist/safeContent/wikilink.cjs +63 -0
  60. package/dist/safeContent/wikilink.cjs.map +1 -0
  61. package/dist/safeContent/wikilink.d.cts +21 -0
  62. package/dist/safeContent/wikilink.d.ts +21 -0
  63. package/dist/safeContent/wikilink.js +38 -0
  64. package/dist/safeContent/wikilink.js.map +1 -0
  65. package/dist/scrollToId.cjs +46 -0
  66. package/dist/scrollToId.cjs.map +1 -0
  67. package/dist/scrollToId.d.cts +12 -0
  68. package/dist/scrollToId.d.ts +12 -0
  69. package/dist/scrollToId.js +22 -0
  70. package/dist/scrollToId.js.map +1 -0
  71. package/dist/urlUtils.cjs +9 -1
  72. package/dist/urlUtils.cjs.map +1 -1
  73. package/dist/urlUtils.d.cts +10 -1
  74. package/dist/urlUtils.d.ts +10 -1
  75. package/dist/urlUtils.js +8 -1
  76. package/dist/urlUtils.js.map +1 -1
  77. package/dist/version.cjs +1 -1
  78. package/dist/version.cjs.map +1 -1
  79. package/dist/version.d.cts +1 -1
  80. package/dist/version.d.ts +1 -1
  81. package/dist/version.js +1 -1
  82. package/dist/version.js.map +1 -1
  83. package/package.json +9 -3
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var scrollToId_exports = {};
20
+ __export(scrollToId_exports, {
21
+ scrollToId: () => scrollToId
22
+ });
23
+ module.exports = __toCommonJS(scrollToId_exports);
24
+ const scrollToId = (id) => {
25
+ if (!id || typeof document === "undefined") return false;
26
+ let el = null;
27
+ try {
28
+ el = document.getElementById(id);
29
+ if (!el) {
30
+ const escaped = typeof CSS !== "undefined" && typeof CSS.escape === "function" ? CSS.escape(id) : id.replace(/["\\]/g, "\\$&");
31
+ el = document.querySelector(`[data-slug="${escaped}"]`);
32
+ }
33
+ } catch {
34
+ return false;
35
+ }
36
+ if (el && typeof el.scrollIntoView === "function") {
37
+ el.scrollIntoView();
38
+ return true;
39
+ }
40
+ return false;
41
+ };
42
+ // Annotate the CommonJS export names for ESM import in node:
43
+ 0 && (module.exports = {
44
+ scrollToId
45
+ });
46
+ //# sourceMappingURL=scrollToId.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/scrollToId.ts"],"sourcesContent":["/**\n * Scroll the element addressed by a fragment id into view (deep-linking Capability\n * C, MARKDOWN_SYNTAX_SPEC §13.5). Tries the element's own `id` first — a heading's\n * `sec-…`/text-slug id (§15) — then the `[data-slug]` fallback, so a citation that\n * used the prose text slug still lands even when the heading's own id is the\n * prose-independent `sec-…` id. Returns whether a target was found; the caller\n * decides whether to fall back to top-of-page. **Never throws** — a missing\n * fragment is a soft failure, not an error.\n */\nexport const scrollToId = (id: string): boolean => {\n if (!id || typeof document === 'undefined') return false;\n let el: Element | null = null;\n try {\n el = document.getElementById(id);\n if (!el) {\n // A citation that targeted the prose text slug still resolves via the\n // `data-slug` hook the kernel emits alongside the `sec-…` id.\n const escaped =\n typeof CSS !== 'undefined' && typeof CSS.escape === 'function'\n ? CSS.escape(id)\n : id.replace(/[\"\\\\]/g, '\\\\$&');\n el = document.querySelector(`[data-slug=\"${escaped}\"]`);\n }\n } catch {\n return false;\n }\n if (el && typeof (el as HTMLElement).scrollIntoView === 'function') {\n (el as HTMLElement).scrollIntoView();\n return true;\n }\n return false;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AASO,MAAM,aAAa,CAAC,OAAwB;AACjD,MAAI,CAAC,MAAM,OAAO,aAAa,YAAa,QAAO;AACnD,MAAI,KAAqB;AACzB,MAAI;AACF,SAAK,SAAS,eAAe,EAAE;AAC/B,QAAI,CAAC,IAAI;AAGP,YAAM,UACJ,OAAO,QAAQ,eAAe,OAAO,IAAI,WAAW,aAChD,IAAI,OAAO,EAAE,IACb,GAAG,QAAQ,UAAU,MAAM;AACjC,WAAK,SAAS,cAAc,eAAe,OAAO,IAAI;AAAA,IACxD;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,MAAM,OAAQ,GAAmB,mBAAmB,YAAY;AAClE,IAAC,GAAmB,eAAe;AACnC,WAAO;AAAA,EACT;AACA,SAAO;AACT;","names":[]}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Scroll the element addressed by a fragment id into view (deep-linking Capability
3
+ * C, MARKDOWN_SYNTAX_SPEC §13.5). Tries the element's own `id` first — a heading's
4
+ * `sec-…`/text-slug id (§15) — then the `[data-slug]` fallback, so a citation that
5
+ * used the prose text slug still lands even when the heading's own id is the
6
+ * prose-independent `sec-…` id. Returns whether a target was found; the caller
7
+ * decides whether to fall back to top-of-page. **Never throws** — a missing
8
+ * fragment is a soft failure, not an error.
9
+ */
10
+ declare const scrollToId: (id: string) => boolean;
11
+
12
+ export { scrollToId };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Scroll the element addressed by a fragment id into view (deep-linking Capability
3
+ * C, MARKDOWN_SYNTAX_SPEC §13.5). Tries the element's own `id` first — a heading's
4
+ * `sec-…`/text-slug id (§15) — then the `[data-slug]` fallback, so a citation that
5
+ * used the prose text slug still lands even when the heading's own id is the
6
+ * prose-independent `sec-…` id. Returns whether a target was found; the caller
7
+ * decides whether to fall back to top-of-page. **Never throws** — a missing
8
+ * fragment is a soft failure, not an error.
9
+ */
10
+ declare const scrollToId: (id: string) => boolean;
11
+
12
+ export { scrollToId };
@@ -0,0 +1,22 @@
1
+ const scrollToId = (id) => {
2
+ if (!id || typeof document === "undefined") return false;
3
+ let el = null;
4
+ try {
5
+ el = document.getElementById(id);
6
+ if (!el) {
7
+ const escaped = typeof CSS !== "undefined" && typeof CSS.escape === "function" ? CSS.escape(id) : id.replace(/["\\]/g, "\\$&");
8
+ el = document.querySelector(`[data-slug="${escaped}"]`);
9
+ }
10
+ } catch {
11
+ return false;
12
+ }
13
+ if (el && typeof el.scrollIntoView === "function") {
14
+ el.scrollIntoView();
15
+ return true;
16
+ }
17
+ return false;
18
+ };
19
+ export {
20
+ scrollToId
21
+ };
22
+ //# sourceMappingURL=scrollToId.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/scrollToId.ts"],"sourcesContent":["/**\n * Scroll the element addressed by a fragment id into view (deep-linking Capability\n * C, MARKDOWN_SYNTAX_SPEC §13.5). Tries the element's own `id` first — a heading's\n * `sec-…`/text-slug id (§15) — then the `[data-slug]` fallback, so a citation that\n * used the prose text slug still lands even when the heading's own id is the\n * prose-independent `sec-…` id. Returns whether a target was found; the caller\n * decides whether to fall back to top-of-page. **Never throws** — a missing\n * fragment is a soft failure, not an error.\n */\nexport const scrollToId = (id: string): boolean => {\n if (!id || typeof document === 'undefined') return false;\n let el: Element | null = null;\n try {\n el = document.getElementById(id);\n if (!el) {\n // A citation that targeted the prose text slug still resolves via the\n // `data-slug` hook the kernel emits alongside the `sec-…` id.\n const escaped =\n typeof CSS !== 'undefined' && typeof CSS.escape === 'function'\n ? CSS.escape(id)\n : id.replace(/[\"\\\\]/g, '\\\\$&');\n el = document.querySelector(`[data-slug=\"${escaped}\"]`);\n }\n } catch {\n return false;\n }\n if (el && typeof (el as HTMLElement).scrollIntoView === 'function') {\n (el as HTMLElement).scrollIntoView();\n return true;\n }\n return false;\n};\n"],"mappings":"AASO,MAAM,aAAa,CAAC,OAAwB;AACjD,MAAI,CAAC,MAAM,OAAO,aAAa,YAAa,QAAO;AACnD,MAAI,KAAqB;AACzB,MAAI;AACF,SAAK,SAAS,eAAe,EAAE;AAC/B,QAAI,CAAC,IAAI;AAGP,YAAM,UACJ,OAAO,QAAQ,eAAe,OAAO,IAAI,WAAW,aAChD,IAAI,OAAO,EAAE,IACb,GAAG,QAAQ,UAAU,MAAM;AACjC,WAAK,SAAS,cAAc,eAAe,OAAO,IAAI;AAAA,IACxD;AAAA,EACF,QAAQ;AACN,WAAO;AAAA,EACT;AACA,MAAI,MAAM,OAAQ,GAAmB,mBAAmB,YAAY;AAClE,IAAC,GAAmB,eAAe;AACnC,WAAO;AAAA,EACT;AACA,SAAO;AACT;","names":[]}
package/dist/urlUtils.cjs CHANGED
@@ -31,6 +31,7 @@ __export(urlUtils_exports, {
31
31
  parsePath: () => parsePath,
32
32
  parseTarget: () => parseTarget,
33
33
  repositoryPrefixURL: () => repositoryPrefixURL,
34
+ splitHash: () => splitHash,
34
35
  underAppRoot: () => underAppRoot
35
36
  });
36
37
  module.exports = __toCommonJS(urlUtils_exports);
@@ -45,6 +46,10 @@ const getOuterHostname = (outerHref) => {
45
46
  const getSearchParams = (search) => Object.fromEntries(
46
47
  [...new URLSearchParams(search ?? window.location.search).entries()]
47
48
  );
49
+ const splitHash = (target) => {
50
+ const i = target.indexOf("#");
51
+ return i === -1 ? [target, ""] : [target.slice(0, i), target.slice(i + 1)];
52
+ };
48
53
  const parseTarget = (target, navigation) => {
49
54
  const newNavigation = { ...navigation };
50
55
  let [prehash, hash] = target.split("#");
@@ -72,11 +77,13 @@ const repositoryPrefixURL = (outerHref, navigationState) => constructUrl(outerHr
72
77
  });
73
78
  const constructOuterUrl = (previousOuterHref, sandboxTarget, navigationState, addFilesPrefix = true) => {
74
79
  if (isAbsolutePath(sandboxTarget)) {
80
+ const [pathPart, hash] = splitHash(sandboxTarget);
75
81
  return constructUrl(
76
82
  previousOuterHref,
77
83
  {
78
84
  ...navigationState,
79
- sandboxPath: addFilesPrefix ? (0, import_pathUtils.joinPaths)(FILES_PREFIX, sandboxTarget) : sandboxTarget
85
+ sandboxPath: addFilesPrefix ? (0, import_pathUtils.joinPaths)(FILES_PREFIX, pathPart) : pathPart,
86
+ hash
80
87
  }
81
88
  );
82
89
  }
@@ -164,6 +171,7 @@ const constructUrl = (outerHref, navigationState) => {
164
171
  parsePath,
165
172
  parseTarget,
166
173
  repositoryPrefixURL,
174
+ splitHash,
167
175
  underAppRoot
168
176
  });
169
177
  //# sourceMappingURL=urlUtils.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/urlUtils.ts"],"sourcesContent":["import { joinPaths } from \"./pathUtils\";\nimport { NavigationState, PathState } from \"./TinkerableContext\";\n\nexport const FILES_PREFIX = '/files';\n\n/**\n * Mount point of the Git repository inside the sandbox filesystem. The sandbox\n * fs is rooted at `/` (so apps can reach dynamic mounts like `/firestore`), with\n * the repo mounted here. URL subpaths are repo-relative, so the file router\n * resolves them under `APP_ROOT`.\n */\nexport const APP_ROOT = '/app';\n\n/** Resolve a repo-relative path (e.g. a URL subpath) to its absolute sandbox path. */\nexport const underAppRoot = (repoRelativePath: string): string =>\n joinPaths(APP_ROOT, repoRelativePath);\n\nexport const getOuterHostname = (outerHref:string) => {\n const url = new URL(outerHref);\n return `${url.protocol}//${url.hostname}`;\n}\n\nexport const getSearchParams = (search?: string): Record<string, string> => Object.fromEntries(\n [...(new URLSearchParams(search ?? window.location.search).entries())]);\n\n\nexport const parseTarget = (target: string, navigation: NavigationState): NavigationState => {\n const newNavigation = { ...navigation };\n let [prehash, hash] = target.split(\"#\")\n if (prehash) {\n let [path, search] = prehash.split(\"?\")\n if (path) {\n newNavigation.sandboxPath = path\n }\n newNavigation.search = search ? search : '';\n }\n newNavigation.hash = hash ? hash : '';\n return newNavigation\n}\n\n\nexport const maybeParseUrl = (str: string): URL | null => {\n try {\n return new URL(str);\n } catch (_) {\n return null;\n }\n}\n\nexport const isAbsolutePath = (sandboxPath: string) => sandboxPath.startsWith('/');\n\nexport const repositoryPrefixURL = (outerHref:string, navigationState: NavigationState) => constructUrl(outerHref, {\n ...navigationState,\n sandboxPath: ''\n });\n\nexport const constructOuterUrl = (previousOuterHref:string, sandboxTarget:string, navigationState: NavigationState, addFilesPrefix=true):string => {\n if (isAbsolutePath(sandboxTarget)) {\n return constructUrl(\n previousOuterHref,\n {\n ...navigationState,\n sandboxPath: addFilesPrefix ? joinPaths(FILES_PREFIX, sandboxTarget) : sandboxTarget\n })\n }\n return (\n new URL(\n sandboxTarget,\n constructUrl(\n previousOuterHref,\n navigationState\n )\n )\n ).toString();\n}\n\nexport const isInternalHref = (outerHref:string, target: string, navigationState: NavigationState) => {\n const parsedUrl = maybeParseUrl(target);\n if (parsedUrl) {\n return target.startsWith(repositoryPrefixURL(outerHref, navigationState));\n }\n // if target is not a valid URL, then assume it's relative.\n return true;\n}\n\nexport type PathSegment = {\n name: string,\n pattern: string,\n transform?: (pathSegment: string) => string,\n // When true, the leading slash that delimits this segment is optional, so the\n // whole `/segment` group can be absent. Used for the trailing sandboxPath:\n // an outer href of `/mode/provider/namespace/repository/ref` (no trailing\n // slash, no sub-path) must still parse, otherwise the regex matches nothing\n // and every segment comes back empty.\n optionalLeadingSlash?: boolean\n}\n\nconst PATH_SEGMENTS: PathSegment[] = [\n { name: 'mode', pattern: '\\\\w+' },\n { name: 'provider', pattern: '[a-zA-Z0-9-_]+' },\n { name: 'namespace', pattern: '[a-zA-Z0-9-_]+' },\n { name: 'repository', pattern: '[a-zA-Z0-9-_]+' },\n { name: 'ref', pattern: '[a-zA-Z0-9-_]+' },\n { name: 'sandboxPath', pattern: '.*', transform: s => `/${s}`, optionalLeadingSlash: true }\n];\n\nconst OUTER_HREF_REGEXP = new RegExp(\n '^' +\n PATH_SEGMENTS.map(({ name, pattern, optionalLeadingSlash }) =>\n optionalLeadingSlash\n ? `(?:\\/(?<${name}>${pattern}))?`\n : `\\/(?<${name}>${pattern})`\n ).join('') +\n \"$\"\n);\n\n\nexport const parsePath = (pathname: string): PathState => {\n const matchResults = pathname.match(OUTER_HREF_REGEXP)?.groups ?? {};\n return PATH_SEGMENTS.reduce((acc: Partial<PathState>, { name, transform }: PathSegment) => {\n let value: string | undefined = undefined;\n if (name in matchResults) {\n value = matchResults[name];\n }\n if (!value) {\n // fall back to default value if var not present in\n value = '';\n }\n if (typeof value === 'string') {\n acc[name] = transform ? transform(value) : value;\n }\n return acc;\n }, {}) as PathState;\n}\n\nexport const parseHref = (href: string): NavigationState => {\n const parsedUrl = new URL(href);\n const pathnameState = parsePath(parsedUrl.pathname);\n return {\n ...pathnameState,\n search: parsedUrl.search.substring(1),\n hash: parsedUrl.hash.substring(1),\n } as NavigationState\n}\n\nconst stripSlashPrefix = (s: string): string => s.startsWith('/') ? s.substring(1) : s;\n\nexport const constructUrl = (outerHref:string, navigationState: NavigationState): string => {\n const path = PATH_SEGMENTS.map(({ name }) => {\n let value = navigationState[name];\n return stripSlashPrefix(value ?? '');\n }).join('/');\n const host = getOuterHostname(outerHref);\n let url = `${host}/${path}`\n if (navigationState.search) {\n url += '?' + navigationState.search\n }\n if (navigationState.hash) {\n url += '#' + navigationState.hash\n }\n return url;\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/urlUtils.ts"],"sourcesContent":["import { joinPaths } from \"./pathUtils\";\nimport { NavigationState, PathState } from \"./TinkerableContext\";\n\nexport const FILES_PREFIX = '/files';\n\n/**\n * Mount point of the Git repository inside the sandbox filesystem. The sandbox\n * fs is rooted at `/` (so apps can reach dynamic mounts like `/firestore`), with\n * the repo mounted here. URL subpaths are repo-relative, so the file router\n * resolves them under `APP_ROOT`.\n */\nexport const APP_ROOT = '/app';\n\n/** Resolve a repo-relative path (e.g. a URL subpath) to its absolute sandbox path. */\nexport const underAppRoot = (repoRelativePath: string): string =>\n joinPaths(APP_ROOT, repoRelativePath);\n\nexport const getOuterHostname = (outerHref:string) => {\n const url = new URL(outerHref);\n return `${url.protocol}//${url.hostname}`;\n}\n\nexport const getSearchParams = (search?: string): Record<string, string> => Object.fromEntries(\n [...(new URLSearchParams(search ?? window.location.search).entries())]);\n\n/**\n * Split a link target into its path part and its `#fragment` (the `#` dropped),\n * e.g. `\"FOO.mdx#sec-8-9\"` → `[\"FOO.mdx\", \"sec-8-9\"]`, `\"#sec-3\"` → `[\"\", \"sec-3\"]`,\n * `\"FOO.mdx\"` → `[\"FOO.mdx\", \"\"]`. Only the **first** `#` splits — a fragment never\n * contains another `#`. The seam between wiki-links (§13) and heading ids (§15):\n * existence is resolved on the path part, the fragment is threaded to navigation.\n * MARKDOWN_SYNTAX_SPEC §13.5.\n */\nexport const splitHash = (target: string): [string, string] => {\n const i = target.indexOf('#');\n return i === -1 ? [target, ''] : [target.slice(0, i), target.slice(i + 1)];\n};\n\n\nexport const parseTarget = (target: string, navigation: NavigationState): NavigationState => {\n const newNavigation = { ...navigation };\n let [prehash, hash] = target.split(\"#\")\n if (prehash) {\n let [path, search] = prehash.split(\"?\")\n if (path) {\n newNavigation.sandboxPath = path\n }\n newNavigation.search = search ? search : '';\n }\n newNavigation.hash = hash ? hash : '';\n return newNavigation\n}\n\n\nexport const maybeParseUrl = (str: string): URL | null => {\n try {\n return new URL(str);\n } catch (_) {\n return null;\n }\n}\n\nexport const isAbsolutePath = (sandboxPath: string) => sandboxPath.startsWith('/');\n\nexport const repositoryPrefixURL = (outerHref:string, navigationState: NavigationState) => constructUrl(outerHref, {\n ...navigationState,\n sandboxPath: ''\n });\n\nexport const constructOuterUrl = (previousOuterHref:string, sandboxTarget:string, navigationState: NavigationState, addFilesPrefix=true):string => {\n if (isAbsolutePath(sandboxTarget)) {\n // Split a trailing `#fragment` off before the target is folded into the\n // sandboxPath, and carry it in `hash` instead — a fragment addresses a section,\n // not a file, so it must not leak into the path (MARKDOWN_SYNTAX_SPEC §13.5). An\n // absolute target with no fragment clears any stale hash from the prior state.\n const [pathPart, hash] = splitHash(sandboxTarget);\n return constructUrl(\n previousOuterHref,\n {\n ...navigationState,\n sandboxPath: addFilesPrefix ? joinPaths(FILES_PREFIX, pathPart) : pathPart,\n hash,\n })\n }\n return (\n new URL(\n sandboxTarget,\n constructUrl(\n previousOuterHref,\n navigationState\n )\n )\n ).toString();\n}\n\nexport const isInternalHref = (outerHref:string, target: string, navigationState: NavigationState) => {\n const parsedUrl = maybeParseUrl(target);\n if (parsedUrl) {\n return target.startsWith(repositoryPrefixURL(outerHref, navigationState));\n }\n // if target is not a valid URL, then assume it's relative.\n return true;\n}\n\nexport type PathSegment = {\n name: string,\n pattern: string,\n transform?: (pathSegment: string) => string,\n // When true, the leading slash that delimits this segment is optional, so the\n // whole `/segment` group can be absent. Used for the trailing sandboxPath:\n // an outer href of `/mode/provider/namespace/repository/ref` (no trailing\n // slash, no sub-path) must still parse, otherwise the regex matches nothing\n // and every segment comes back empty.\n optionalLeadingSlash?: boolean\n}\n\nconst PATH_SEGMENTS: PathSegment[] = [\n { name: 'mode', pattern: '\\\\w+' },\n { name: 'provider', pattern: '[a-zA-Z0-9-_]+' },\n { name: 'namespace', pattern: '[a-zA-Z0-9-_]+' },\n { name: 'repository', pattern: '[a-zA-Z0-9-_]+' },\n { name: 'ref', pattern: '[a-zA-Z0-9-_]+' },\n { name: 'sandboxPath', pattern: '.*', transform: s => `/${s}`, optionalLeadingSlash: true }\n];\n\nconst OUTER_HREF_REGEXP = new RegExp(\n '^' +\n PATH_SEGMENTS.map(({ name, pattern, optionalLeadingSlash }) =>\n optionalLeadingSlash\n ? `(?:\\/(?<${name}>${pattern}))?`\n : `\\/(?<${name}>${pattern})`\n ).join('') +\n \"$\"\n);\n\n\nexport const parsePath = (pathname: string): PathState => {\n const matchResults = pathname.match(OUTER_HREF_REGEXP)?.groups ?? {};\n return PATH_SEGMENTS.reduce((acc: Partial<PathState>, { name, transform }: PathSegment) => {\n let value: string | undefined = undefined;\n if (name in matchResults) {\n value = matchResults[name];\n }\n if (!value) {\n // fall back to default value if var not present in\n value = '';\n }\n if (typeof value === 'string') {\n acc[name] = transform ? transform(value) : value;\n }\n return acc;\n }, {}) as PathState;\n}\n\nexport const parseHref = (href: string): NavigationState => {\n const parsedUrl = new URL(href);\n const pathnameState = parsePath(parsedUrl.pathname);\n return {\n ...pathnameState,\n search: parsedUrl.search.substring(1),\n hash: parsedUrl.hash.substring(1),\n } as NavigationState\n}\n\nconst stripSlashPrefix = (s: string): string => s.startsWith('/') ? s.substring(1) : s;\n\nexport const constructUrl = (outerHref:string, navigationState: NavigationState): string => {\n const path = PATH_SEGMENTS.map(({ name }) => {\n let value = navigationState[name];\n return stripSlashPrefix(value ?? '');\n }).join('/');\n const host = getOuterHostname(outerHref);\n let url = `${host}/${path}`\n if (navigationState.search) {\n url += '?' + navigationState.search\n }\n if (navigationState.hash) {\n url += '#' + navigationState.hash\n }\n return url;\n}\n"],"mappings":"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","names":[]}
@@ -15,6 +15,15 @@ declare const APP_ROOT = "/app";
15
15
  declare const underAppRoot: (repoRelativePath: string) => string;
16
16
  declare const getOuterHostname: (outerHref: string) => string;
17
17
  declare const getSearchParams: (search?: string) => Record<string, string>;
18
+ /**
19
+ * Split a link target into its path part and its `#fragment` (the `#` dropped),
20
+ * e.g. `"FOO.mdx#sec-8-9"` → `["FOO.mdx", "sec-8-9"]`, `"#sec-3"` → `["", "sec-3"]`,
21
+ * `"FOO.mdx"` → `["FOO.mdx", ""]`. Only the **first** `#` splits — a fragment never
22
+ * contains another `#`. The seam between wiki-links (§13) and heading ids (§15):
23
+ * existence is resolved on the path part, the fragment is threaded to navigation.
24
+ * MARKDOWN_SYNTAX_SPEC §13.5.
25
+ */
26
+ declare const splitHash: (target: string) => [string, string];
18
27
  declare const parseTarget: (target: string, navigation: NavigationState) => NavigationState;
19
28
  declare const maybeParseUrl: (str: string) => URL | null;
20
29
  declare const isAbsolutePath: (sandboxPath: string) => boolean;
@@ -31,4 +40,4 @@ declare const parsePath: (pathname: string) => PathState;
31
40
  declare const parseHref: (href: string) => NavigationState;
32
41
  declare const constructUrl: (outerHref: string, navigationState: NavigationState) => string;
33
42
 
34
- export { APP_ROOT, FILES_PREFIX, type PathSegment, constructOuterUrl, constructUrl, getOuterHostname, getSearchParams, isAbsolutePath, isInternalHref, maybeParseUrl, parseHref, parsePath, parseTarget, repositoryPrefixURL, underAppRoot };
43
+ export { APP_ROOT, FILES_PREFIX, type PathSegment, constructOuterUrl, constructUrl, getOuterHostname, getSearchParams, isAbsolutePath, isInternalHref, maybeParseUrl, parseHref, parsePath, parseTarget, repositoryPrefixURL, splitHash, underAppRoot };
@@ -15,6 +15,15 @@ declare const APP_ROOT = "/app";
15
15
  declare const underAppRoot: (repoRelativePath: string) => string;
16
16
  declare const getOuterHostname: (outerHref: string) => string;
17
17
  declare const getSearchParams: (search?: string) => Record<string, string>;
18
+ /**
19
+ * Split a link target into its path part and its `#fragment` (the `#` dropped),
20
+ * e.g. `"FOO.mdx#sec-8-9"` → `["FOO.mdx", "sec-8-9"]`, `"#sec-3"` → `["", "sec-3"]`,
21
+ * `"FOO.mdx"` → `["FOO.mdx", ""]`. Only the **first** `#` splits — a fragment never
22
+ * contains another `#`. The seam between wiki-links (§13) and heading ids (§15):
23
+ * existence is resolved on the path part, the fragment is threaded to navigation.
24
+ * MARKDOWN_SYNTAX_SPEC §13.5.
25
+ */
26
+ declare const splitHash: (target: string) => [string, string];
18
27
  declare const parseTarget: (target: string, navigation: NavigationState) => NavigationState;
19
28
  declare const maybeParseUrl: (str: string) => URL | null;
20
29
  declare const isAbsolutePath: (sandboxPath: string) => boolean;
@@ -31,4 +40,4 @@ declare const parsePath: (pathname: string) => PathState;
31
40
  declare const parseHref: (href: string) => NavigationState;
32
41
  declare const constructUrl: (outerHref: string, navigationState: NavigationState) => string;
33
42
 
34
- export { APP_ROOT, FILES_PREFIX, type PathSegment, constructOuterUrl, constructUrl, getOuterHostname, getSearchParams, isAbsolutePath, isInternalHref, maybeParseUrl, parseHref, parsePath, parseTarget, repositoryPrefixURL, underAppRoot };
43
+ export { APP_ROOT, FILES_PREFIX, type PathSegment, constructOuterUrl, constructUrl, getOuterHostname, getSearchParams, isAbsolutePath, isInternalHref, maybeParseUrl, parseHref, parsePath, parseTarget, repositoryPrefixURL, splitHash, underAppRoot };
package/dist/urlUtils.js CHANGED
@@ -9,6 +9,10 @@ const getOuterHostname = (outerHref) => {
9
9
  const getSearchParams = (search) => Object.fromEntries(
10
10
  [...new URLSearchParams(search ?? window.location.search).entries()]
11
11
  );
12
+ const splitHash = (target) => {
13
+ const i = target.indexOf("#");
14
+ return i === -1 ? [target, ""] : [target.slice(0, i), target.slice(i + 1)];
15
+ };
12
16
  const parseTarget = (target, navigation) => {
13
17
  const newNavigation = { ...navigation };
14
18
  let [prehash, hash] = target.split("#");
@@ -36,11 +40,13 @@ const repositoryPrefixURL = (outerHref, navigationState) => constructUrl(outerHr
36
40
  });
37
41
  const constructOuterUrl = (previousOuterHref, sandboxTarget, navigationState, addFilesPrefix = true) => {
38
42
  if (isAbsolutePath(sandboxTarget)) {
43
+ const [pathPart, hash] = splitHash(sandboxTarget);
39
44
  return constructUrl(
40
45
  previousOuterHref,
41
46
  {
42
47
  ...navigationState,
43
- sandboxPath: addFilesPrefix ? joinPaths(FILES_PREFIX, sandboxTarget) : sandboxTarget
48
+ sandboxPath: addFilesPrefix ? joinPaths(FILES_PREFIX, pathPart) : pathPart,
49
+ hash
44
50
  }
45
51
  );
46
52
  }
@@ -127,6 +133,7 @@ export {
127
133
  parsePath,
128
134
  parseTarget,
129
135
  repositoryPrefixURL,
136
+ splitHash,
130
137
  underAppRoot
131
138
  };
132
139
  //# sourceMappingURL=urlUtils.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/urlUtils.ts"],"sourcesContent":["import { joinPaths } from \"./pathUtils\";\nimport { NavigationState, PathState } from \"./TinkerableContext\";\n\nexport const FILES_PREFIX = '/files';\n\n/**\n * Mount point of the Git repository inside the sandbox filesystem. The sandbox\n * fs is rooted at `/` (so apps can reach dynamic mounts like `/firestore`), with\n * the repo mounted here. URL subpaths are repo-relative, so the file router\n * resolves them under `APP_ROOT`.\n */\nexport const APP_ROOT = '/app';\n\n/** Resolve a repo-relative path (e.g. a URL subpath) to its absolute sandbox path. */\nexport const underAppRoot = (repoRelativePath: string): string =>\n joinPaths(APP_ROOT, repoRelativePath);\n\nexport const getOuterHostname = (outerHref:string) => {\n const url = new URL(outerHref);\n return `${url.protocol}//${url.hostname}`;\n}\n\nexport const getSearchParams = (search?: string): Record<string, string> => Object.fromEntries(\n [...(new URLSearchParams(search ?? window.location.search).entries())]);\n\n\nexport const parseTarget = (target: string, navigation: NavigationState): NavigationState => {\n const newNavigation = { ...navigation };\n let [prehash, hash] = target.split(\"#\")\n if (prehash) {\n let [path, search] = prehash.split(\"?\")\n if (path) {\n newNavigation.sandboxPath = path\n }\n newNavigation.search = search ? search : '';\n }\n newNavigation.hash = hash ? hash : '';\n return newNavigation\n}\n\n\nexport const maybeParseUrl = (str: string): URL | null => {\n try {\n return new URL(str);\n } catch (_) {\n return null;\n }\n}\n\nexport const isAbsolutePath = (sandboxPath: string) => sandboxPath.startsWith('/');\n\nexport const repositoryPrefixURL = (outerHref:string, navigationState: NavigationState) => constructUrl(outerHref, {\n ...navigationState,\n sandboxPath: ''\n });\n\nexport const constructOuterUrl = (previousOuterHref:string, sandboxTarget:string, navigationState: NavigationState, addFilesPrefix=true):string => {\n if (isAbsolutePath(sandboxTarget)) {\n return constructUrl(\n previousOuterHref,\n {\n ...navigationState,\n sandboxPath: addFilesPrefix ? joinPaths(FILES_PREFIX, sandboxTarget) : sandboxTarget\n })\n }\n return (\n new URL(\n sandboxTarget,\n constructUrl(\n previousOuterHref,\n navigationState\n )\n )\n ).toString();\n}\n\nexport const isInternalHref = (outerHref:string, target: string, navigationState: NavigationState) => {\n const parsedUrl = maybeParseUrl(target);\n if (parsedUrl) {\n return target.startsWith(repositoryPrefixURL(outerHref, navigationState));\n }\n // if target is not a valid URL, then assume it's relative.\n return true;\n}\n\nexport type PathSegment = {\n name: string,\n pattern: string,\n transform?: (pathSegment: string) => string,\n // When true, the leading slash that delimits this segment is optional, so the\n // whole `/segment` group can be absent. Used for the trailing sandboxPath:\n // an outer href of `/mode/provider/namespace/repository/ref` (no trailing\n // slash, no sub-path) must still parse, otherwise the regex matches nothing\n // and every segment comes back empty.\n optionalLeadingSlash?: boolean\n}\n\nconst PATH_SEGMENTS: PathSegment[] = [\n { name: 'mode', pattern: '\\\\w+' },\n { name: 'provider', pattern: '[a-zA-Z0-9-_]+' },\n { name: 'namespace', pattern: '[a-zA-Z0-9-_]+' },\n { name: 'repository', pattern: '[a-zA-Z0-9-_]+' },\n { name: 'ref', pattern: '[a-zA-Z0-9-_]+' },\n { name: 'sandboxPath', pattern: '.*', transform: s => `/${s}`, optionalLeadingSlash: true }\n];\n\nconst OUTER_HREF_REGEXP = new RegExp(\n '^' +\n PATH_SEGMENTS.map(({ name, pattern, optionalLeadingSlash }) =>\n optionalLeadingSlash\n ? `(?:\\/(?<${name}>${pattern}))?`\n : `\\/(?<${name}>${pattern})`\n ).join('') +\n \"$\"\n);\n\n\nexport const parsePath = (pathname: string): PathState => {\n const matchResults = pathname.match(OUTER_HREF_REGEXP)?.groups ?? {};\n return PATH_SEGMENTS.reduce((acc: Partial<PathState>, { name, transform }: PathSegment) => {\n let value: string | undefined = undefined;\n if (name in matchResults) {\n value = matchResults[name];\n }\n if (!value) {\n // fall back to default value if var not present in\n value = '';\n }\n if (typeof value === 'string') {\n acc[name] = transform ? transform(value) : value;\n }\n return acc;\n }, {}) as PathState;\n}\n\nexport const parseHref = (href: string): NavigationState => {\n const parsedUrl = new URL(href);\n const pathnameState = parsePath(parsedUrl.pathname);\n return {\n ...pathnameState,\n search: parsedUrl.search.substring(1),\n hash: parsedUrl.hash.substring(1),\n } as NavigationState\n}\n\nconst stripSlashPrefix = (s: string): string => s.startsWith('/') ? s.substring(1) : s;\n\nexport const constructUrl = (outerHref:string, navigationState: NavigationState): string => {\n const path = PATH_SEGMENTS.map(({ name }) => {\n let value = navigationState[name];\n return stripSlashPrefix(value ?? '');\n }).join('/');\n const host = getOuterHostname(outerHref);\n let url = `${host}/${path}`\n if (navigationState.search) {\n url += '?' + navigationState.search\n }\n if (navigationState.hash) {\n url += '#' + navigationState.hash\n }\n return url;\n}\n"],"mappings":"AAAA,SAAS,iBAAiB;AAGnB,MAAM,eAAe;AAQrB,MAAM,WAAW;AAGjB,MAAM,eAAe,CAAC,qBAC3B,UAAU,UAAU,gBAAgB;AAE/B,MAAM,mBAAmB,CAAC,cAAqB;AACpD,QAAM,MAAM,IAAI,IAAI,SAAS;AAC7B,SAAO,GAAG,IAAI,QAAQ,KAAK,IAAI,QAAQ;AACzC;AAEO,MAAM,kBAAkB,CAAC,WAA4C,OAAO;AAAA,EACjF,CAAC,GAAI,IAAI,gBAAgB,UAAU,OAAO,SAAS,MAAM,EAAE,QAAQ,CAAE;AAAC;AAGjE,MAAM,cAAc,CAAC,QAAgB,eAAiD;AAC3F,QAAM,gBAAgB,EAAE,GAAG,WAAW;AACtC,MAAI,CAAC,SAAS,IAAI,IAAI,OAAO,MAAM,GAAG;AACtC,MAAI,SAAS;AACX,QAAI,CAAC,MAAM,MAAM,IAAI,QAAQ,MAAM,GAAG;AACtC,QAAI,MAAM;AACR,oBAAc,cAAc;AAAA,IAC9B;AACA,kBAAc,SAAS,SAAS,SAAS;AAAA,EAC3C;AACA,gBAAc,OAAO,OAAO,OAAO;AACnC,SAAO;AACT;AAGO,MAAM,gBAAgB,CAAC,QAA4B;AACxD,MAAI;AACF,WAAO,IAAI,IAAI,GAAG;AAAA,EACpB,SAAS,GAAG;AACV,WAAO;AAAA,EACT;AACF;AAEO,MAAM,iBAAiB,CAAC,gBAAwB,YAAY,WAAW,GAAG;AAE1E,MAAM,sBAAsB,CAAC,WAAkB,oBAAqC,aAAa,WAAW;AAAA,EAC7G,GAAG;AAAA,EACH,aAAa;AACf,CAAC;AAEE,MAAM,oBAAoB,CAAC,mBAA0B,eAAsB,iBAAkC,iBAAe,SAAgB;AACjJ,MAAI,eAAe,aAAa,GAAG;AACjC,WAAO;AAAA,MACL;AAAA,MACA;AAAA,QACE,GAAG;AAAA,QACH,aAAa,iBAAiB,UAAU,cAAc,aAAa,IAAI;AAAA,MACzE;AAAA,IAAC;AAAA,EACL;AACA,SACE,IAAI;AAAA,IACF;AAAA,IACA;AAAA,MACE;AAAA,MACA;AAAA,IACF;AAAA,EACF,EACA,SAAS;AACb;AAEO,MAAM,iBAAiB,CAAC,WAAkB,QAAgB,oBAAqC;AACpG,QAAM,YAAY,cAAc,MAAM;AACtC,MAAI,WAAW;AACb,WAAO,OAAO,WAAW,oBAAoB,WAAW,eAAe,CAAC;AAAA,EAC1E;AAEA,SAAO;AACT;AAcA,MAAM,gBAA+B;AAAA,EACnC,EAAE,MAAM,QAAQ,SAAS,OAAO;AAAA,EAChC,EAAE,MAAM,YAAY,SAAS,iBAAiB;AAAA,EAC9C,EAAE,MAAM,aAAa,SAAS,iBAAiB;AAAA,EAC/C,EAAE,MAAM,cAAc,SAAS,iBAAiB;AAAA,EAChD,EAAE,MAAM,OAAO,SAAS,iBAAiB;AAAA,EACzC,EAAE,MAAM,eAAe,SAAS,MAAM,WAAW,OAAK,IAAI,CAAC,IAAI,sBAAsB,KAAK;AAC5F;AAEA,MAAM,oBAAoB,IAAI;AAAA,EAC5B,MACA,cAAc;AAAA,IAAI,CAAC,EAAE,MAAM,SAAS,qBAAqB,MACvD,uBACI,UAAW,IAAI,IAAI,OAAO,QAC1B,OAAQ,IAAI,IAAI,OAAO;AAAA,EAC7B,EAAE,KAAK,EAAE,IACT;AACF;AAGO,MAAM,YAAY,CAAC,aAAgC;AACxD,QAAM,eAAe,SAAS,MAAM,iBAAiB,GAAG,UAAU,CAAC;AACnE,SAAO,cAAc,OAAO,CAAC,KAAyB,EAAE,MAAM,UAAU,MAAmB;AACzF,QAAI,QAA4B;AAChC,QAAI,QAAQ,cAAc;AACxB,cAAQ,aAAa,IAAI;AAAA,IAC3B;AACA,QAAI,CAAC,OAAO;AAEV,cAAQ;AAAA,IACV;AACA,QAAI,OAAO,UAAU,UAAU;AAC7B,UAAI,IAAI,IAAI,YAAY,UAAU,KAAK,IAAI;AAAA,IAC7C;AACA,WAAO;AAAA,EACT,GAAG,CAAC,CAAC;AACP;AAEO,MAAM,YAAY,CAAC,SAAkC;AAC1D,QAAM,YAAY,IAAI,IAAI,IAAI;AAC9B,QAAM,gBAAgB,UAAU,UAAU,QAAQ;AAClD,SAAO;AAAA,IACL,GAAG;AAAA,IACH,QAAQ,UAAU,OAAO,UAAU,CAAC;AAAA,IACpC,MAAM,UAAU,KAAK,UAAU,CAAC;AAAA,EAClC;AACF;AAEA,MAAM,mBAAmB,CAAC,MAAsB,EAAE,WAAW,GAAG,IAAI,EAAE,UAAU,CAAC,IAAI;AAE9E,MAAM,eAAe,CAAC,WAAkB,oBAA6C;AAC1F,QAAM,OAAO,cAAc,IAAI,CAAC,EAAE,KAAK,MAAM;AAC3C,QAAI,QAAQ,gBAAgB,IAAI;AAChC,WAAO,iBAAiB,SAAS,EAAE;AAAA,EACrC,CAAC,EAAE,KAAK,GAAG;AACX,QAAM,OAAO,iBAAiB,SAAS;AACvC,MAAI,MAAM,GAAG,IAAI,IAAI,IAAI;AACzB,MAAI,gBAAgB,QAAQ;AAC1B,WAAO,MAAM,gBAAgB;AAAA,EAC/B;AACA,MAAI,gBAAgB,MAAM;AACxB,WAAO,MAAM,gBAAgB;AAAA,EAC/B;AACA,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../src/urlUtils.ts"],"sourcesContent":["import { joinPaths } from \"./pathUtils\";\nimport { NavigationState, PathState } from \"./TinkerableContext\";\n\nexport const FILES_PREFIX = '/files';\n\n/**\n * Mount point of the Git repository inside the sandbox filesystem. The sandbox\n * fs is rooted at `/` (so apps can reach dynamic mounts like `/firestore`), with\n * the repo mounted here. URL subpaths are repo-relative, so the file router\n * resolves them under `APP_ROOT`.\n */\nexport const APP_ROOT = '/app';\n\n/** Resolve a repo-relative path (e.g. a URL subpath) to its absolute sandbox path. */\nexport const underAppRoot = (repoRelativePath: string): string =>\n joinPaths(APP_ROOT, repoRelativePath);\n\nexport const getOuterHostname = (outerHref:string) => {\n const url = new URL(outerHref);\n return `${url.protocol}//${url.hostname}`;\n}\n\nexport const getSearchParams = (search?: string): Record<string, string> => Object.fromEntries(\n [...(new URLSearchParams(search ?? window.location.search).entries())]);\n\n/**\n * Split a link target into its path part and its `#fragment` (the `#` dropped),\n * e.g. `\"FOO.mdx#sec-8-9\"` → `[\"FOO.mdx\", \"sec-8-9\"]`, `\"#sec-3\"` → `[\"\", \"sec-3\"]`,\n * `\"FOO.mdx\"` → `[\"FOO.mdx\", \"\"]`. Only the **first** `#` splits — a fragment never\n * contains another `#`. The seam between wiki-links (§13) and heading ids (§15):\n * existence is resolved on the path part, the fragment is threaded to navigation.\n * MARKDOWN_SYNTAX_SPEC §13.5.\n */\nexport const splitHash = (target: string): [string, string] => {\n const i = target.indexOf('#');\n return i === -1 ? [target, ''] : [target.slice(0, i), target.slice(i + 1)];\n};\n\n\nexport const parseTarget = (target: string, navigation: NavigationState): NavigationState => {\n const newNavigation = { ...navigation };\n let [prehash, hash] = target.split(\"#\")\n if (prehash) {\n let [path, search] = prehash.split(\"?\")\n if (path) {\n newNavigation.sandboxPath = path\n }\n newNavigation.search = search ? search : '';\n }\n newNavigation.hash = hash ? hash : '';\n return newNavigation\n}\n\n\nexport const maybeParseUrl = (str: string): URL | null => {\n try {\n return new URL(str);\n } catch (_) {\n return null;\n }\n}\n\nexport const isAbsolutePath = (sandboxPath: string) => sandboxPath.startsWith('/');\n\nexport const repositoryPrefixURL = (outerHref:string, navigationState: NavigationState) => constructUrl(outerHref, {\n ...navigationState,\n sandboxPath: ''\n });\n\nexport const constructOuterUrl = (previousOuterHref:string, sandboxTarget:string, navigationState: NavigationState, addFilesPrefix=true):string => {\n if (isAbsolutePath(sandboxTarget)) {\n // Split a trailing `#fragment` off before the target is folded into the\n // sandboxPath, and carry it in `hash` instead — a fragment addresses a section,\n // not a file, so it must not leak into the path (MARKDOWN_SYNTAX_SPEC §13.5). An\n // absolute target with no fragment clears any stale hash from the prior state.\n const [pathPart, hash] = splitHash(sandboxTarget);\n return constructUrl(\n previousOuterHref,\n {\n ...navigationState,\n sandboxPath: addFilesPrefix ? joinPaths(FILES_PREFIX, pathPart) : pathPart,\n hash,\n })\n }\n return (\n new URL(\n sandboxTarget,\n constructUrl(\n previousOuterHref,\n navigationState\n )\n )\n ).toString();\n}\n\nexport const isInternalHref = (outerHref:string, target: string, navigationState: NavigationState) => {\n const parsedUrl = maybeParseUrl(target);\n if (parsedUrl) {\n return target.startsWith(repositoryPrefixURL(outerHref, navigationState));\n }\n // if target is not a valid URL, then assume it's relative.\n return true;\n}\n\nexport type PathSegment = {\n name: string,\n pattern: string,\n transform?: (pathSegment: string) => string,\n // When true, the leading slash that delimits this segment is optional, so the\n // whole `/segment` group can be absent. Used for the trailing sandboxPath:\n // an outer href of `/mode/provider/namespace/repository/ref` (no trailing\n // slash, no sub-path) must still parse, otherwise the regex matches nothing\n // and every segment comes back empty.\n optionalLeadingSlash?: boolean\n}\n\nconst PATH_SEGMENTS: PathSegment[] = [\n { name: 'mode', pattern: '\\\\w+' },\n { name: 'provider', pattern: '[a-zA-Z0-9-_]+' },\n { name: 'namespace', pattern: '[a-zA-Z0-9-_]+' },\n { name: 'repository', pattern: '[a-zA-Z0-9-_]+' },\n { name: 'ref', pattern: '[a-zA-Z0-9-_]+' },\n { name: 'sandboxPath', pattern: '.*', transform: s => `/${s}`, optionalLeadingSlash: true }\n];\n\nconst OUTER_HREF_REGEXP = new RegExp(\n '^' +\n PATH_SEGMENTS.map(({ name, pattern, optionalLeadingSlash }) =>\n optionalLeadingSlash\n ? `(?:\\/(?<${name}>${pattern}))?`\n : `\\/(?<${name}>${pattern})`\n ).join('') +\n \"$\"\n);\n\n\nexport const parsePath = (pathname: string): PathState => {\n const matchResults = pathname.match(OUTER_HREF_REGEXP)?.groups ?? {};\n return PATH_SEGMENTS.reduce((acc: Partial<PathState>, { name, transform }: PathSegment) => {\n let value: string | undefined = undefined;\n if (name in matchResults) {\n value = matchResults[name];\n }\n if (!value) {\n // fall back to default value if var not present in\n value = '';\n }\n if (typeof value === 'string') {\n acc[name] = transform ? transform(value) : value;\n }\n return acc;\n }, {}) as PathState;\n}\n\nexport const parseHref = (href: string): NavigationState => {\n const parsedUrl = new URL(href);\n const pathnameState = parsePath(parsedUrl.pathname);\n return {\n ...pathnameState,\n search: parsedUrl.search.substring(1),\n hash: parsedUrl.hash.substring(1),\n } as NavigationState\n}\n\nconst stripSlashPrefix = (s: string): string => s.startsWith('/') ? s.substring(1) : s;\n\nexport const constructUrl = (outerHref:string, navigationState: NavigationState): string => {\n const path = PATH_SEGMENTS.map(({ name }) => {\n let value = navigationState[name];\n return stripSlashPrefix(value ?? '');\n }).join('/');\n const host = getOuterHostname(outerHref);\n let url = `${host}/${path}`\n if (navigationState.search) {\n url += '?' + navigationState.search\n }\n if (navigationState.hash) {\n url += '#' + navigationState.hash\n }\n return url;\n}\n"],"mappings":"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","names":[]}
package/dist/version.cjs CHANGED
@@ -21,7 +21,7 @@ __export(version_exports, {
21
21
  SDK_VERSION: () => SDK_VERSION
22
22
  });
23
23
  module.exports = __toCommonJS(version_exports);
24
- const SDK_VERSION = "0.29.0";
24
+ const SDK_VERSION = "0.31.0";
25
25
  // Annotate the CommonJS export names for ESM import in node:
26
26
  0 && (module.exports = {
27
27
  SDK_VERSION
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/version.ts"],"sourcesContent":["// GENERATED by scripts/gen-version.mjs from package.json — do not edit by hand.\n// Regenerated on every build (prebuild); kept honest by version.test.ts.\n\n/** This SDK's package version, baked from package.json at build (SP2-6). */\nexport const SDK_VERSION = '0.29.0';\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIO,MAAM,cAAc;","names":[]}
1
+ {"version":3,"sources":["../src/version.ts"],"sourcesContent":["// GENERATED by scripts/gen-version.mjs from package.json — do not edit by hand.\n// Regenerated on every build (prebuild); kept honest by version.test.ts.\n\n/** This SDK's package version, baked from package.json at build (SP2-6). */\nexport const SDK_VERSION = '0.31.0';\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAIO,MAAM,cAAc;","names":[]}
@@ -1,4 +1,4 @@
1
1
  /** This SDK's package version, baked from package.json at build (SP2-6). */
2
- declare const SDK_VERSION = "0.29.0";
2
+ declare const SDK_VERSION = "0.31.0";
3
3
 
4
4
  export { SDK_VERSION };
package/dist/version.d.ts CHANGED
@@ -1,4 +1,4 @@
1
1
  /** This SDK's package version, baked from package.json at build (SP2-6). */
2
- declare const SDK_VERSION = "0.29.0";
2
+ declare const SDK_VERSION = "0.31.0";
3
3
 
4
4
  export { SDK_VERSION };
package/dist/version.js CHANGED
@@ -1,4 +1,4 @@
1
- const SDK_VERSION = "0.29.0";
1
+ const SDK_VERSION = "0.31.0";
2
2
  export {
3
3
  SDK_VERSION
4
4
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/version.ts"],"sourcesContent":["// GENERATED by scripts/gen-version.mjs from package.json — do not edit by hand.\n// Regenerated on every build (prebuild); kept honest by version.test.ts.\n\n/** This SDK's package version, baked from package.json at build (SP2-6). */\nexport const SDK_VERSION = '0.29.0';\n"],"mappings":"AAIO,MAAM,cAAc;","names":[]}
1
+ {"version":3,"sources":["../src/version.ts"],"sourcesContent":["// GENERATED by scripts/gen-version.mjs from package.json — do not edit by hand.\n// Regenerated on every build (prebuild); kept honest by version.test.ts.\n\n/** This SDK's package version, baked from package.json at build (SP2-6). */\nexport const SDK_VERSION = '0.31.0';\n"],"mappings":"AAIO,MAAM,cAAc;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@immediately-run/sdk",
3
- "version": "0.29.0",
3
+ "version": "0.31.0",
4
4
  "description": "Runtime SDK for code executing inside an immediately.run sandbox.",
5
5
  "license": "MIT",
6
6
  "repository": "github:immediately-run/immediately-run-sdk",
@@ -32,15 +32,21 @@
32
32
  "check:circular": "node scripts/check-circular.mjs",
33
33
  "api:check": "node scripts/check-api-stability.mjs",
34
34
  "api:update": "node scripts/check-api-stability.mjs --update",
35
- "verify": "npm run check:circular && npm test && npm run build && npm run api:check",
35
+ "verify": "npm run check:circular && npm test && npm run test:safe-content && npm run build && npm run api:check",
36
36
  "docs": "typedoc --json docs/api.json && node scripts/gen-llms.mjs",
37
- "prepublishOnly": "npm run check:circular && npm run build && npm run api:check"
37
+ "prepublishOnly": "npm run check:circular && npm run build && npm run api:check",
38
+ "test:safe-content": "node scripts/build-safecontent-e2e.mjs && node --test test/safeContent.e2e.mjs"
38
39
  },
39
40
  "peerDependencies": {
40
41
  "react": "^19.0.0",
41
42
  "react-dom": "^19.0.0"
42
43
  },
43
44
  "dependencies": {
45
+ "mdast-util-from-markdown": "^2.0.3",
46
+ "mdast-util-gfm": "^3.1.0",
47
+ "mdast-util-mdx-jsx": "^3.2.0",
48
+ "micromark-extension-gfm": "^3.0.0",
49
+ "micromark-extension-mdx-jsx": "^3.0.2",
44
50
  "react-error-boundary": "^6.0.0"
45
51
  },
46
52
  "devDependencies": {