@immediately-run/sdk 0.45.3 → 0.47.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/MDXProvider.cjs +1 -5
- package/dist/MDXProvider.cjs.map +1 -1
- package/dist/MDXProvider.js +1 -5
- package/dist/MDXProvider.js.map +1 -1
- package/dist/RoutingSpec.cjs.map +1 -1
- package/dist/TinkerableContext.cjs.map +1 -1
- package/dist/TinkerableContext.js.map +1 -1
- package/dist/auth.cjs.map +1 -1
- package/dist/auth.js.map +1 -1
- package/dist/boot.cjs +2 -9
- package/dist/boot.cjs.map +1 -1
- package/dist/boot.d.cts +2 -2
- package/dist/boot.d.ts +2 -2
- package/dist/boot.js +2 -9
- package/dist/boot.js.map +1 -1
- package/dist/catalog.cjs.map +1 -1
- package/dist/catalog.js.map +1 -1
- package/dist/components/Admonition.cjs +4 -13
- package/dist/components/Admonition.cjs.map +1 -1
- package/dist/components/Admonition.js +4 -13
- package/dist/components/Admonition.js.map +1 -1
- package/dist/components/FileRouter.cjs +11 -3
- package/dist/components/FileRouter.cjs.map +1 -1
- package/dist/components/FileRouter.js +11 -3
- package/dist/components/FileRouter.js.map +1 -1
- package/dist/components/HeadingAnchor.cjs.map +1 -1
- package/dist/components/HeadingAnchor.js.map +1 -1
- package/dist/components/Link.cjs.map +1 -1
- package/dist/components/Link.js.map +1 -1
- package/dist/components/MainContent.cjs +11 -1
- package/dist/components/MainContent.cjs.map +1 -1
- package/dist/components/MainContent.js +11 -1
- package/dist/components/MainContent.js.map +1 -1
- package/dist/components/MountImage.cjs +1 -9
- package/dist/components/MountImage.cjs.map +1 -1
- package/dist/components/MountImage.js +1 -9
- package/dist/components/MountImage.js.map +1 -1
- package/dist/components/Routes.cjs +1 -4
- package/dist/components/Routes.cjs.map +1 -1
- package/dist/components/Routes.d.cts +1 -1
- package/dist/components/Routes.d.ts +1 -1
- package/dist/components/Routes.js +1 -4
- package/dist/components/Routes.js.map +1 -1
- package/dist/components/SafeInclude.cjs +1 -4
- package/dist/components/SafeInclude.cjs.map +1 -1
- package/dist/components/SafeInclude.js +1 -4
- package/dist/components/SafeInclude.js.map +1 -1
- package/dist/components/WikiLink.cjs +1 -10
- package/dist/components/WikiLink.cjs.map +1 -1
- package/dist/components/WikiLink.js +1 -10
- package/dist/components/WikiLink.js.map +1 -1
- package/dist/components/defaults.cjs.map +1 -1
- package/dist/components/defaults.d.cts +1 -1
- package/dist/components/defaults.d.ts +1 -1
- package/dist/components/defaults.js.map +1 -1
- package/dist/components/errors.cjs +3 -1
- package/dist/components/errors.cjs.map +1 -1
- package/dist/components/errors.js +3 -1
- package/dist/components/errors.js.map +1 -1
- package/dist/contextUtils.cjs.map +1 -1
- package/dist/contextUtils.js.map +1 -1
- package/dist/contribute.cjs.map +1 -1
- package/dist/contribute.js.map +1 -1
- package/dist/debug.cjs +2 -1
- package/dist/debug.cjs.map +1 -1
- package/dist/debug.js +3 -8
- package/dist/debug.js.map +1 -1
- package/dist/diagnostics.cjs.map +1 -1
- package/dist/diagnostics.js.map +1 -1
- package/dist/editor.cjs.map +1 -1
- package/dist/editor.js.map +1 -1
- package/dist/editorContext.cjs.map +1 -1
- package/dist/editorContext.js.map +1 -1
- package/dist/fs.cjs.map +1 -1
- package/dist/fs.js.map +1 -1
- package/dist/hostAttention.cjs +57 -0
- package/dist/hostAttention.cjs.map +1 -0
- package/dist/hostAttention.d.cts +39 -0
- package/dist/hostAttention.d.ts +39 -0
- package/dist/hostAttention.js +31 -0
- package/dist/hostAttention.js.map +1 -0
- package/dist/hostTransport.cjs +55 -0
- package/dist/hostTransport.cjs.map +1 -0
- package/dist/hostTransport.d.cts +12 -0
- package/dist/hostTransport.d.ts +12 -0
- package/dist/hostTransport.js +30 -0
- package/dist/hostTransport.js.map +1 -0
- package/dist/index.cjs +4 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/injectedBundler.cjs.map +1 -1
- package/dist/injectedBundler.js.map +1 -1
- package/dist/ipc.cjs +1 -4
- package/dist/ipc.cjs.map +1 -1
- package/dist/ipc.js +1 -4
- package/dist/ipc.js.map +1 -1
- package/dist/irMarkers.cjs.map +1 -1
- package/dist/irMarkers.d.cts +11 -11
- package/dist/irMarkers.d.ts +11 -11
- package/dist/irMarkers.js.map +1 -1
- package/dist/launch.cjs.map +1 -1
- package/dist/launch.js.map +1 -1
- package/dist/linkSpace.cjs +11 -2
- package/dist/linkSpace.cjs.map +1 -1
- package/dist/linkSpace.d.cts +23 -0
- package/dist/linkSpace.d.ts +23 -0
- package/dist/linkSpace.js +11 -2
- package/dist/linkSpace.js.map +1 -1
- package/dist/llm.cjs +26 -8
- package/dist/llm.cjs.map +1 -1
- package/dist/llm.d.cts +75 -3
- package/dist/llm.d.ts +75 -3
- package/dist/llm.js +21 -7
- package/dist/llm.js.map +1 -1
- package/dist/loading.cjs +18 -14
- package/dist/loading.cjs.map +1 -1
- package/dist/loading.d.cts +2 -2
- package/dist/loading.d.ts +2 -2
- package/dist/loading.js +19 -19
- package/dist/loading.js.map +1 -1
- package/dist/markers.cjs.map +1 -1
- package/dist/markers.js.map +1 -1
- package/dist/metadataSource.cjs +1 -5
- package/dist/metadataSource.cjs.map +1 -1
- package/dist/metadataSource.d.cts +1 -1
- package/dist/metadataSource.d.ts +1 -1
- package/dist/metadataSource.js +1 -5
- package/dist/metadataSource.js.map +1 -1
- package/dist/moduleCache.cjs +4 -1
- package/dist/moduleCache.cjs.map +1 -1
- package/dist/moduleCache.d.cts +1 -1
- package/dist/moduleCache.d.ts +1 -1
- package/dist/moduleCache.js +4 -1
- package/dist/moduleCache.js.map +1 -1
- package/dist/mountMatch.cjs.map +1 -1
- package/dist/mountMatch.js.map +1 -1
- package/dist/mounts.cjs +6 -1
- package/dist/mounts.cjs.map +1 -1
- package/dist/mounts.d.cts +2 -2
- package/dist/mounts.d.ts +2 -2
- package/dist/mounts.js +6 -1
- package/dist/mounts.js.map +1 -1
- package/dist/netFetch.cjs +3 -5
- package/dist/netFetch.cjs.map +1 -1
- package/dist/netFetch.js +3 -5
- package/dist/netFetch.js.map +1 -1
- package/dist/onFsChange.cjs.map +1 -1
- package/dist/onFsChange.js.map +1 -1
- package/dist/pathUtils.cjs +9 -12
- package/dist/pathUtils.cjs.map +1 -1
- package/dist/pathUtils.js +9 -12
- package/dist/pathUtils.js.map +1 -1
- package/dist/protocolDeadline.cjs +205 -0
- package/dist/protocolDeadline.cjs.map +1 -0
- package/dist/protocolDeadline.d.cts +146 -0
- package/dist/protocolDeadline.d.ts +146 -0
- package/dist/protocolDeadline.js +168 -0
- package/dist/protocolDeadline.js.map +1 -0
- package/dist/protocolStream.cjs +49 -6
- package/dist/protocolStream.cjs.map +1 -1
- package/dist/protocolStream.d.cts +9 -2
- package/dist/protocolStream.d.ts +9 -2
- package/dist/protocolStream.js +56 -6
- package/dist/protocolStream.js.map +1 -1
- package/dist/pushChannel.cjs +13 -9
- package/dist/pushChannel.cjs.map +1 -1
- package/dist/pushChannel.js +12 -8
- package/dist/pushChannel.js.map +1 -1
- package/dist/ready.cjs.map +1 -1
- package/dist/ready.js.map +1 -1
- package/dist/routing.cjs +3 -1
- package/dist/routing.cjs.map +1 -1
- package/dist/routing.js +3 -1
- package/dist/routing.js.map +1 -1
- package/dist/safeContent/index.cjs.map +1 -1
- package/dist/safeContent/index.js.map +1 -1
- package/dist/safeContent/parseSafeMdast.cjs.map +1 -1
- package/dist/safeContent/parseSafeMdast.js.map +1 -1
- package/dist/safeContent/renderMdast.cjs.map +1 -1
- package/dist/safeContent/renderMdast.js.map +1 -1
- package/dist/sandboxTypes.cjs.map +1 -1
- package/dist/sandboxUtils.cjs +69 -24
- package/dist/sandboxUtils.cjs.map +1 -1
- package/dist/sandboxUtils.d.cts +37 -4
- package/dist/sandboxUtils.d.ts +37 -4
- package/dist/sandboxUtils.js +74 -22
- package/dist/sandboxUtils.js.map +1 -1
- package/dist/scrollToId.cjs.map +1 -1
- package/dist/scrollToId.js.map +1 -1
- package/dist/secrets.cjs.map +1 -1
- package/dist/secrets.js.map +1 -1
- package/dist/tasks.cjs +12 -2
- package/dist/tasks.cjs.map +1 -1
- package/dist/tasks.js +12 -2
- package/dist/tasks.js.map +1 -1
- package/dist/testing.cjs +1 -3
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.js +1 -3
- package/dist/testing.js.map +1 -1
- package/dist/theme.cjs.map +1 -1
- package/dist/theme.js.map +1 -1
- package/dist/urlUtils.cjs +7 -18
- package/dist/urlUtils.cjs.map +1 -1
- package/dist/urlUtils.js +7 -18
- package/dist/urlUtils.js.map +1 -1
- package/dist/vcs.cjs +1 -3
- package/dist/vcs.cjs.map +1 -1
- package/dist/vcs.js +1 -3
- package/dist/vcs.js.map +1 -1
- package/dist/version.cjs +1 -1
- package/dist/version.cjs.map +1 -1
- package/dist/version.d.cts +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +11 -5
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './MDXProvider';\nexport * from './routing';\nexport * from './boot';\nexport * from './components/Include';\n// Only the component is public. `stripFrontmatter`/`appMountRelative` are module-level\n// exports so they can be unit-tested directly, NOT public API — the SDK's surface is\n// backwards-compatible forever, so an internal helper exported for a test's convenience is a\n// permanent commitment made for the wrong reason.\nexport { SafeInclude } from './components/SafeInclude';\nexport * from './sourceCache';\nexport * from './components/MDXComponents';\nexport * from './linkSpace';\nexport * from './components/MountImage';\nexport * from './components/Routes';\nexport * from './hooks';\n// R3-276: the supported way for a viewer app to provide its own metadata store,\n// replacing a wholesale re-provision of `TinkerableContext` in app code.\nexport * from './metadataSource';\nexport * from './auth';\nexport * from './theme';\nexport * from './editorContext';\nexport * from './editor';\nexport * from './formFactor';\nexport * from './hostAttention';\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 './vcs';\nexport * from './onFsChange';\nexport * from './fs';\nexport * from './debug';\nexport * from './tasks';\nexport * from './launch';\nexport * from './runtime';\nexport * from './irMarkers';\nexport * from './ready';\nexport * from './loading';\nexport * from './protocolStream';\nexport * from './protocolDeadline';\nexport * from './sandboxTypes';\nexport * from './safeContent';\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,0BAAc,0BAAd;AACA,0BAAc,sBADd;AAEA,0BAAc,mBAFd;AAGA,0BAAc,iCAHd;AAQA,yBAA4B;AAC5B,0BAAc,0BATd;AAUA,0BAAc,uCAVd;AAWA,0BAAc,wBAXd;AAYA,0BAAc,oCAZd;AAaA,0BAAc,gCAbd;AAcA,0BAAc,oBAdd;AAiBA,0BAAc,6BAjBd;AAkBA,0BAAc,mBAlBd;AAmBA,0BAAc,oBAnBd;AAoBA,0BAAc,4BApBd;AAqBA,0BAAc,qBArBd;AAsBA,0BAAc,yBAtBd;AAuBA,0BAAc,4BAvBd;AAwBA,0BAAc,qBAxBd;AAyBA,0BAAc,qBAzBd;AA0BA,0BAAc,yBA1Bd;AA2BA,0BAAc,sBA3Bd;AA4BA,0BAAc,kBA5Bd;AA6BA,0BAAc,kBA7Bd;AA8BA,0BAAc,uBA9Bd;AA+BA,0BAAc,sBA/Bd;AAgCA,0BAAc,kBAhCd;AAiCA,0BAAc,0BAjCd;AAkCA,0BAAc,kBAlCd;AAmCA,0BAAc,yBAnCd;AAoCA,0BAAc,iBApCd;AAqCA,0BAAc,oBArCd;AAsCA,0BAAc,oBAtCd;AAuCA,0BAAc,qBAvCd;AAwCA,0BAAc,sBAxCd;AAyCA,0BAAc,wBAzCd;AA0CA,0BAAc,oBA1Cd;AA2CA,0BAAc,sBA3Cd;AA4CA,0BAAc,6BA5Cd;AA6CA,0BAAc,+BA7Cd;AA8CA,0BAAc,2BA9Cd;AA+CA,0BAAc,0BA/Cd;","names":[]}
|
package/dist/index.d.cts
CHANGED
|
@@ -15,6 +15,7 @@ export { HostTheme, getHostTheme, onHostThemeChange, setHostTheme, useHostTheme
|
|
|
15
15
|
export { EditorContext, getEditorContext, onEditorContextChange, useEditorContext } from './editorContext.cjs';
|
|
16
16
|
export { EditTarget, EditorOpenError, EditorSessionError, EditorWriteError, RequestEditError, closeFile, createFile, createFolder, deleteEntry, openInEditor, renameEntry, requestEdit, setActiveFile, uploadFile } from './editor.cjs';
|
|
17
17
|
export { FormFactor, FormFactorClass, Orientation, getFormFactor, onFormFactorChange, useFormFactor } from './formFactor.cjs';
|
|
18
|
+
export { HostAttention, HostAttentionKind, NO_HOST_ATTENTION, getHostAttention, onHostAttentionChange, useHostAttention } from './hostAttention.cjs';
|
|
18
19
|
export { getRegion, useRegion } from './region.cjs';
|
|
19
20
|
export { Invite, MountQuery, MountRemoveReason, MountRule, RemovedMount, SandboxMount, SessionMount, SpaceError, acceptInvite, awaitMatchingMount, createSpace, declineInvite, findMount, getAppMountPath, getInvites, getMounts, getSessionMounts, importSettingsFromParent, listMyInvites, listPendingInvites, listSettingsApps, makeContentRef, mount, mountSpace, onInvitesChange, onMountsChange, onSessionMountsChange, openSettings, openSettingsOf, requestMount, requestSpace, resolveContentRef, resolveContentRefs, revokeInvite, unmountSpace, useInvites, useMounts, useSessionMounts, waitForMount } from './mounts.cjs';
|
|
20
21
|
export { ContributeMode, ContributeOptions, ContributionEvent, ContributionResult, contribute } from './contribute.cjs';
|
|
@@ -23,7 +24,7 @@ export { RegionMessage, onRegionMessage, postToRegion, revealRegion, useRegionMe
|
|
|
23
24
|
export { DraggableItem, DroppedItem, ItemDragError, cancelItemDrag, onItemDrop, startItemDrag, useDroppedItem } from './dnd.cjs';
|
|
24
25
|
export { HostFetchInit, HostFetchResponse, HostFetchStreamEvent, HostFetchStreamResult, hostFetch, hostFetchStream } from './netFetch.cjs';
|
|
25
26
|
export { SecretError, SecretGrant, SecretHints, SecretQuery, SecretType, SecretView, getSecrets, onSecretsChange, requestAddSecret, requestSecret, revokeSecret, useSecrets } from './secrets.cjs';
|
|
26
|
-
export { ChatDelta, ChatFeatures, ChatMessage, ChatProviderInfo, ChatRequest, ChatResult, ChatRole, ChatStopReason, ContentPart, ToolDef, chat, describeChat, onChatProviderChange, useChatProvider } from './llm.cjs';
|
|
27
|
+
export { ChatDelta, ChatFeatures, ChatMessage, ChatProviderInfo, ChatProviderState, ChatRequest, ChatResult, ChatRole, ChatStopReason, ContentPart, ToolDef, chat, describeChat, describeChatState, normalizeProviderInfo, onChatProviderChange, onChatProviderStateChange, useChatProvider, useChatProviderState } from './llm.cjs';
|
|
27
28
|
export { BuildError, ConsoleEntry, ConsoleLevel, Diagnostics, DiagnosticsProvenance, getDiagnostics, onDiagnosticsChange, useDiagnostics } from './diagnostics.cjs';
|
|
28
29
|
export { VcsActionError, VcsBranch, VcsChange, VcsPR, VcsState, getVcsState, onVcsStateChange, refreshDiff, refreshPRs, resetWorkingTree, useVcsState } from './vcs.cjs';
|
|
29
30
|
export { FsChange, getFsChange, onFsChange, useFsChange } from './onFsChange.cjs';
|
|
@@ -36,6 +37,7 @@ export { ForwardedMarker, IR_MARKERS, IrMarkerName, isAllowedMarkerName, isIrMar
|
|
|
36
37
|
export { ReadyState, __resetReady, __setReadyDeps, getReadyState, onReady, reportReady } from './ready.cjs';
|
|
37
38
|
export { LOADING_TIMINGS, LoadingRegion, Skeleton, SkeletonArchetype, SkeletonRow, Spinner } from './loading.cjs';
|
|
38
39
|
export { StreamError, StreamFrame, StreamTransport, consumeStream, protocolStream } from './protocolStream.cjs';
|
|
40
|
+
export { ATTENDED_FIRST_FRAME_MS, ATTENDED_TIMEOUT_MS, Attendance, BoundedCallOptions, CallBounds, DeadlineBound, NETWORK_TIMEOUT_MS, PENDING_NOTICE_MS, PendingAttention, PendingState, ProtocolCancelledError, ProtocolTimeoutError, STREAM_IDLE_TIMEOUT_MS, SuspendableDeadline, UNATTENDED_TIMEOUT_MS, attendanceOf, attendanceReason, boundsFor, createSuspendableDeadline, firstFrameBoundsFor, firstFrameTimeoutFor, timeoutFor } from './protocolDeadline.cjs';
|
|
39
41
|
export { EvaluationContext, FileQueryResult, FilesMetadata, Metadata, MetadataQueryEntry, MetadataQueryFunction, MetadataQueryRecord, MetadataQueryResult, ModuleExports } from './sandboxTypes.cjs';
|
|
40
42
|
export { SafeContent, SafeContentProps } from './safeContent/SafeContent.cjs';
|
|
41
43
|
export { RenderMdastOptions, SafeContentComponents, renderMdast } from './safeContent/renderMdast.cjs';
|
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export { HostTheme, getHostTheme, onHostThemeChange, setHostTheme, useHostTheme
|
|
|
15
15
|
export { EditorContext, getEditorContext, onEditorContextChange, useEditorContext } from './editorContext.js';
|
|
16
16
|
export { EditTarget, EditorOpenError, EditorSessionError, EditorWriteError, RequestEditError, closeFile, createFile, createFolder, deleteEntry, openInEditor, renameEntry, requestEdit, setActiveFile, uploadFile } from './editor.js';
|
|
17
17
|
export { FormFactor, FormFactorClass, Orientation, getFormFactor, onFormFactorChange, useFormFactor } from './formFactor.js';
|
|
18
|
+
export { HostAttention, HostAttentionKind, NO_HOST_ATTENTION, getHostAttention, onHostAttentionChange, useHostAttention } from './hostAttention.js';
|
|
18
19
|
export { getRegion, useRegion } from './region.js';
|
|
19
20
|
export { Invite, MountQuery, MountRemoveReason, MountRule, RemovedMount, SandboxMount, SessionMount, SpaceError, acceptInvite, awaitMatchingMount, createSpace, declineInvite, findMount, getAppMountPath, getInvites, getMounts, getSessionMounts, importSettingsFromParent, listMyInvites, listPendingInvites, listSettingsApps, makeContentRef, mount, mountSpace, onInvitesChange, onMountsChange, onSessionMountsChange, openSettings, openSettingsOf, requestMount, requestSpace, resolveContentRef, resolveContentRefs, revokeInvite, unmountSpace, useInvites, useMounts, useSessionMounts, waitForMount } from './mounts.js';
|
|
20
21
|
export { ContributeMode, ContributeOptions, ContributionEvent, ContributionResult, contribute } from './contribute.js';
|
|
@@ -23,7 +24,7 @@ export { RegionMessage, onRegionMessage, postToRegion, revealRegion, useRegionMe
|
|
|
23
24
|
export { DraggableItem, DroppedItem, ItemDragError, cancelItemDrag, onItemDrop, startItemDrag, useDroppedItem } from './dnd.js';
|
|
24
25
|
export { HostFetchInit, HostFetchResponse, HostFetchStreamEvent, HostFetchStreamResult, hostFetch, hostFetchStream } from './netFetch.js';
|
|
25
26
|
export { SecretError, SecretGrant, SecretHints, SecretQuery, SecretType, SecretView, getSecrets, onSecretsChange, requestAddSecret, requestSecret, revokeSecret, useSecrets } from './secrets.js';
|
|
26
|
-
export { ChatDelta, ChatFeatures, ChatMessage, ChatProviderInfo, ChatRequest, ChatResult, ChatRole, ChatStopReason, ContentPart, ToolDef, chat, describeChat, onChatProviderChange, useChatProvider } from './llm.js';
|
|
27
|
+
export { ChatDelta, ChatFeatures, ChatMessage, ChatProviderInfo, ChatProviderState, ChatRequest, ChatResult, ChatRole, ChatStopReason, ContentPart, ToolDef, chat, describeChat, describeChatState, normalizeProviderInfo, onChatProviderChange, onChatProviderStateChange, useChatProvider, useChatProviderState } from './llm.js';
|
|
27
28
|
export { BuildError, ConsoleEntry, ConsoleLevel, Diagnostics, DiagnosticsProvenance, getDiagnostics, onDiagnosticsChange, useDiagnostics } from './diagnostics.js';
|
|
28
29
|
export { VcsActionError, VcsBranch, VcsChange, VcsPR, VcsState, getVcsState, onVcsStateChange, refreshDiff, refreshPRs, resetWorkingTree, useVcsState } from './vcs.js';
|
|
29
30
|
export { FsChange, getFsChange, onFsChange, useFsChange } from './onFsChange.js';
|
|
@@ -36,6 +37,7 @@ export { ForwardedMarker, IR_MARKERS, IrMarkerName, isAllowedMarkerName, isIrMar
|
|
|
36
37
|
export { ReadyState, __resetReady, __setReadyDeps, getReadyState, onReady, reportReady } from './ready.js';
|
|
37
38
|
export { LOADING_TIMINGS, LoadingRegion, Skeleton, SkeletonArchetype, SkeletonRow, Spinner } from './loading.js';
|
|
38
39
|
export { StreamError, StreamFrame, StreamTransport, consumeStream, protocolStream } from './protocolStream.js';
|
|
40
|
+
export { ATTENDED_FIRST_FRAME_MS, ATTENDED_TIMEOUT_MS, Attendance, BoundedCallOptions, CallBounds, DeadlineBound, NETWORK_TIMEOUT_MS, PENDING_NOTICE_MS, PendingAttention, PendingState, ProtocolCancelledError, ProtocolTimeoutError, STREAM_IDLE_TIMEOUT_MS, SuspendableDeadline, UNATTENDED_TIMEOUT_MS, attendanceOf, attendanceReason, boundsFor, createSuspendableDeadline, firstFrameBoundsFor, firstFrameTimeoutFor, timeoutFor } from './protocolDeadline.js';
|
|
39
41
|
export { EvaluationContext, FileQueryResult, FilesMetadata, Metadata, MetadataQueryEntry, MetadataQueryFunction, MetadataQueryRecord, MetadataQueryResult, ModuleExports } from './sandboxTypes.js';
|
|
40
42
|
export { SafeContent, SafeContentProps } from './safeContent/SafeContent.js';
|
|
41
43
|
export { RenderMdastOptions, SafeContentComponents, renderMdast } from './safeContent/renderMdast.js';
|
package/dist/index.js
CHANGED
|
@@ -16,6 +16,7 @@ export * from "./theme";
|
|
|
16
16
|
export * from "./editorContext";
|
|
17
17
|
export * from "./editor";
|
|
18
18
|
export * from "./formFactor";
|
|
19
|
+
export * from "./hostAttention";
|
|
19
20
|
export * from "./region";
|
|
20
21
|
export * from "./mounts";
|
|
21
22
|
export * from "./contribute";
|
|
@@ -37,6 +38,7 @@ export * from "./irMarkers";
|
|
|
37
38
|
export * from "./ready";
|
|
38
39
|
export * from "./loading";
|
|
39
40
|
export * from "./protocolStream";
|
|
41
|
+
export * from "./protocolDeadline";
|
|
40
42
|
export * from "./sandboxTypes";
|
|
41
43
|
export * from "./safeContent";
|
|
42
44
|
export {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export * from './MDXProvider';\nexport * from './routing';\nexport * from './boot';\nexport * from './components/Include';\n// Only the component is public. `stripFrontmatter`/`appMountRelative` are module-level\n// exports so they can be unit-tested directly, NOT public API — the SDK's surface is\n// backwards-compatible forever, so an internal helper exported for a test's convenience is a\n// permanent commitment made for the wrong reason.\nexport { SafeInclude } from './components/SafeInclude';\nexport * from './sourceCache';\nexport * from './components/MDXComponents';\nexport * from './linkSpace';\nexport * from './components/MountImage';\nexport * from './components/Routes';\nexport * from './hooks';\n// R3-276: the supported way for a viewer app to provide its own metadata store,\n// replacing a wholesale re-provision of `TinkerableContext` in app code.\nexport * from './metadataSource';\nexport * from './auth';\nexport * from './theme';\nexport * from './editorContext';\nexport * from './editor';\nexport * from './formFactor';\nexport * from './hostAttention';\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 './vcs';\nexport * from './onFsChange';\nexport * from './fs';\nexport * from './debug';\nexport * from './tasks';\nexport * from './launch';\nexport * from './runtime';\nexport * from './irMarkers';\nexport * from './ready';\nexport * from './loading';\nexport * from './protocolStream';\nexport * from './protocolDeadline';\nexport * from './sandboxTypes';\nexport * from './safeContent';\n"],"mappings":";AAAA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAKd,SAAS,mBAAmB;AAC5B,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AAGd,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;AACd,cAAc;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/injectedBundler.ts"],"sourcesContent":["// Injected sandbox-bundler access (SDK_PACKAGING_SPEC §4/§8, Phase 5). The SDK was\n// historically wired straight to the injected bundler service objects\n// (`module.evaluation.module.bundler.<x>`). Phase 5 makes the SDK transport-agnostic\n// so those services can eventually be retired: every reader PREFERS the injection\n// (so the current, live path stays byte-for-byte unchanged) and FALLS BACK to the\n// §4 transport when the SDK is fetched from npm with no injection present.\n//\n// This module centralizes the `module.evaluation.module.bundler.*` reads (the same\n// philosophy as `sandboxUtils`' transport resolver) and exposes a PURE resolver for\n// the metadata-update subscription so the dual-mode decision is unit-tested without\n// a live bundler. The ambient reads themselves are thin (untestable without the\n// sandbox realm, exactly like `sandboxUtils.transport()`).\n\n/** vscode-style Event source: subscribe with a listener, get a disposable back. */\nexport type EventSource = (listener: (msg: any) => void) => { dispose(): void };\n\n/** The injected bundler's metadata emitter — fires `{type:'metadata-update', update}`\n * as files (re)compile. Absent when the SDK is npm-fetched (no injection). */\nexport interface InjectedMetadataEmitter {\n onMetadataChange: EventSource;\n /** Start the DelayedEmitter once a subscriber is attached (injected path only). */\n enable(): void;\n}\n\n/** The injected bundler object, or null when there is no injection (npm-fetched). */\nconst injectedBundler = (): any | null => {\n try {\n // @ts-ignore - `module.evaluation` is injected by the sandbox runtime\n return module?.evaluation?.module?.bundler ?? null;\n } catch {\n return null;\n }\n};\n\n/** The injected bundler's metadata emitter, or null when npm-fetched. */\nexport const getInjectedMetadataEmitter = (): InjectedMetadataEmitter | null => {\n const b = injectedBundler();\n if (b && typeof b.onMetadataChange === 'function' && b.onMetadataChangeEmitter) {\n return {\n onMetadataChange: b.onMetadataChange,\n enable: () => b.onMetadataChangeEmitter.enable(),\n };\n }\n return null;\n};\n\n/**\n * The injected bundler's synchronous metadata snapshot for the boot seed\n * (MDX_CONTENT_COLLECTIONS_SPEC §1.4). Returns the full `/app`-keyed collection the\n * bundler seeded (from the frontmatter sidecar) so the app's first render already\n * holds it — the SDK-side counterpart of the bundler seeding. Null when npm-fetched\n * (no in-realm bundler) → the SDK degrades to event-fill over the §4 transport, no\n * first-paint guarantee. The returned VALUE refs are the same objects the emitter\n * replays, so the `enable()` replay is a no-op (the §1.4 identity contract).\n */\nexport const getInjectedMetadataSnapshot = (): Record<string, Record<string, any>> | null => {\n const b = injectedBundler();\n if (b && typeof b.getMetadataSnapshot === 'function') {\n return b.getMetadataSnapshot();\n }\n return null;\n};\n\n/** What `boot` needs to subscribe to metadata updates: the `event` source to hand\n * `addListener` (the injected emitter, or `undefined` → listen over the transport)\n * and an `enable` to start the injected DelayedEmitter (a no-op off-injection). */\nexport interface MetadataSource {\n event?: EventSource;\n enable(): void;\n}\n\n/**\n * Resolve the metadata-update subscription source (PURE — the dual-mode decision).\n * With the injected emitter: use it and arm it, so the live path is byte-identical.\n * Without it (npm-fetched): return no `event`, so the caller's `addListener` falls\n * back to the §4 transport's `onMessage`, and `enable` is a no-op.\n */\nexport const resolveMetadataSource = (
|
|
1
|
+
{"version":3,"sources":["../src/injectedBundler.ts"],"sourcesContent":["// Injected sandbox-bundler access (SDK_PACKAGING_SPEC §4/§8, Phase 5). The SDK was\n// historically wired straight to the injected bundler service objects\n// (`module.evaluation.module.bundler.<x>`). Phase 5 makes the SDK transport-agnostic\n// so those services can eventually be retired: every reader PREFERS the injection\n// (so the current, live path stays byte-for-byte unchanged) and FALLS BACK to the\n// §4 transport when the SDK is fetched from npm with no injection present.\n//\n// This module centralizes the `module.evaluation.module.bundler.*` reads (the same\n// philosophy as `sandboxUtils`' transport resolver) and exposes a PURE resolver for\n// the metadata-update subscription so the dual-mode decision is unit-tested without\n// a live bundler. The ambient reads themselves are thin (untestable without the\n// sandbox realm, exactly like `sandboxUtils.transport()`).\n\n/** vscode-style Event source: subscribe with a listener, get a disposable back. */\nexport type EventSource = (listener: (msg: any) => void) => { dispose(): void };\n\n/** The injected bundler's metadata emitter — fires `{type:'metadata-update', update}`\n * as files (re)compile. Absent when the SDK is npm-fetched (no injection). */\nexport interface InjectedMetadataEmitter {\n onMetadataChange: EventSource;\n /** Start the DelayedEmitter once a subscriber is attached (injected path only). */\n enable(): void;\n}\n\n/** The injected bundler object, or null when there is no injection (npm-fetched). */\nconst injectedBundler = (): any | null => {\n try {\n // @ts-ignore - `module.evaluation` is injected by the sandbox runtime\n return module?.evaluation?.module?.bundler ?? null;\n } catch {\n return null;\n }\n};\n\n/** The injected bundler's metadata emitter, or null when npm-fetched. */\nexport const getInjectedMetadataEmitter = (): InjectedMetadataEmitter | null => {\n const b = injectedBundler();\n if (b && typeof b.onMetadataChange === 'function' && b.onMetadataChangeEmitter) {\n return {\n onMetadataChange: b.onMetadataChange,\n enable: () => b.onMetadataChangeEmitter.enable(),\n };\n }\n return null;\n};\n\n/**\n * The injected bundler's synchronous metadata snapshot for the boot seed\n * (MDX_CONTENT_COLLECTIONS_SPEC §1.4). Returns the full `/app`-keyed collection the\n * bundler seeded (from the frontmatter sidecar) so the app's first render already\n * holds it — the SDK-side counterpart of the bundler seeding. Null when npm-fetched\n * (no in-realm bundler) → the SDK degrades to event-fill over the §4 transport, no\n * first-paint guarantee. The returned VALUE refs are the same objects the emitter\n * replays, so the `enable()` replay is a no-op (the §1.4 identity contract).\n */\nexport const getInjectedMetadataSnapshot = (): Record<string, Record<string, any>> | null => {\n const b = injectedBundler();\n if (b && typeof b.getMetadataSnapshot === 'function') {\n return b.getMetadataSnapshot();\n }\n return null;\n};\n\n/** What `boot` needs to subscribe to metadata updates: the `event` source to hand\n * `addListener` (the injected emitter, or `undefined` → listen over the transport)\n * and an `enable` to start the injected DelayedEmitter (a no-op off-injection). */\nexport interface MetadataSource {\n event?: EventSource;\n enable(): void;\n}\n\n/**\n * Resolve the metadata-update subscription source (PURE — the dual-mode decision).\n * With the injected emitter: use it and arm it, so the live path is byte-identical.\n * Without it (npm-fetched): return no `event`, so the caller's `addListener` falls\n * back to the §4 transport's `onMessage`, and `enable` is a no-op.\n */\nexport const resolveMetadataSource = (injected: InjectedMetadataEmitter | null): MetadataSource =>\n injected\n ? { event: injected.onMetadataChange, enable: () => injected.enable() }\n : { event: undefined, enable: () => {} };\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyBA,MAAM,kBAAkB,MAAkB;AACxC,MAAI;AAEF,WAAO,QAAQ,YAAY,QAAQ,WAAW;AAAA,EAChD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,MAAM,6BAA6B,MAAsC;AAC9E,QAAM,IAAI,gBAAgB;AAC1B,MAAI,KAAK,OAAO,EAAE,qBAAqB,cAAc,EAAE,yBAAyB;AAC9E,WAAO;AAAA,MACL,kBAAkB,EAAE;AAAA,MACpB,QAAQ,MAAM,EAAE,wBAAwB,OAAO;AAAA,IACjD;AAAA,EACF;AACA,SAAO;AACT;AAWO,MAAM,8BAA8B,MAAkD;AAC3F,QAAM,IAAI,gBAAgB;AAC1B,MAAI,KAAK,OAAO,EAAE,wBAAwB,YAAY;AACpD,WAAO,EAAE,oBAAoB;AAAA,EAC/B;AACA,SAAO;AACT;AAgBO,MAAM,wBAAwB,CAAC,aACpC,WACI,EAAE,OAAO,SAAS,kBAAkB,QAAQ,MAAM,SAAS,OAAO,EAAE,IACpE,EAAE,OAAO,QAAW,QAAQ,MAAM;AAAC,EAAE;","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/injectedBundler.ts"],"sourcesContent":["// Injected sandbox-bundler access (SDK_PACKAGING_SPEC §4/§8, Phase 5). The SDK was\n// historically wired straight to the injected bundler service objects\n// (`module.evaluation.module.bundler.<x>`). Phase 5 makes the SDK transport-agnostic\n// so those services can eventually be retired: every reader PREFERS the injection\n// (so the current, live path stays byte-for-byte unchanged) and FALLS BACK to the\n// §4 transport when the SDK is fetched from npm with no injection present.\n//\n// This module centralizes the `module.evaluation.module.bundler.*` reads (the same\n// philosophy as `sandboxUtils`' transport resolver) and exposes a PURE resolver for\n// the metadata-update subscription so the dual-mode decision is unit-tested without\n// a live bundler. The ambient reads themselves are thin (untestable without the\n// sandbox realm, exactly like `sandboxUtils.transport()`).\n\n/** vscode-style Event source: subscribe with a listener, get a disposable back. */\nexport type EventSource = (listener: (msg: any) => void) => { dispose(): void };\n\n/** The injected bundler's metadata emitter — fires `{type:'metadata-update', update}`\n * as files (re)compile. Absent when the SDK is npm-fetched (no injection). */\nexport interface InjectedMetadataEmitter {\n onMetadataChange: EventSource;\n /** Start the DelayedEmitter once a subscriber is attached (injected path only). */\n enable(): void;\n}\n\n/** The injected bundler object, or null when there is no injection (npm-fetched). */\nconst injectedBundler = (): any | null => {\n try {\n // @ts-ignore - `module.evaluation` is injected by the sandbox runtime\n return module?.evaluation?.module?.bundler ?? null;\n } catch {\n return null;\n }\n};\n\n/** The injected bundler's metadata emitter, or null when npm-fetched. */\nexport const getInjectedMetadataEmitter = (): InjectedMetadataEmitter | null => {\n const b = injectedBundler();\n if (b && typeof b.onMetadataChange === 'function' && b.onMetadataChangeEmitter) {\n return {\n onMetadataChange: b.onMetadataChange,\n enable: () => b.onMetadataChangeEmitter.enable(),\n };\n }\n return null;\n};\n\n/**\n * The injected bundler's synchronous metadata snapshot for the boot seed\n * (MDX_CONTENT_COLLECTIONS_SPEC §1.4). Returns the full `/app`-keyed collection the\n * bundler seeded (from the frontmatter sidecar) so the app's first render already\n * holds it — the SDK-side counterpart of the bundler seeding. Null when npm-fetched\n * (no in-realm bundler) → the SDK degrades to event-fill over the §4 transport, no\n * first-paint guarantee. The returned VALUE refs are the same objects the emitter\n * replays, so the `enable()` replay is a no-op (the §1.4 identity contract).\n */\nexport const getInjectedMetadataSnapshot = (): Record<string, Record<string, any>> | null => {\n const b = injectedBundler();\n if (b && typeof b.getMetadataSnapshot === 'function') {\n return b.getMetadataSnapshot();\n }\n return null;\n};\n\n/** What `boot` needs to subscribe to metadata updates: the `event` source to hand\n * `addListener` (the injected emitter, or `undefined` → listen over the transport)\n * and an `enable` to start the injected DelayedEmitter (a no-op off-injection). */\nexport interface MetadataSource {\n event?: EventSource;\n enable(): void;\n}\n\n/**\n * Resolve the metadata-update subscription source (PURE — the dual-mode decision).\n * With the injected emitter: use it and arm it, so the live path is byte-identical.\n * Without it (npm-fetched): return no `event`, so the caller's `addListener` falls\n * back to the §4 transport's `onMessage`, and `enable` is a no-op.\n */\nexport const resolveMetadataSource = (
|
|
1
|
+
{"version":3,"sources":["../src/injectedBundler.ts"],"sourcesContent":["// Injected sandbox-bundler access (SDK_PACKAGING_SPEC §4/§8, Phase 5). The SDK was\n// historically wired straight to the injected bundler service objects\n// (`module.evaluation.module.bundler.<x>`). Phase 5 makes the SDK transport-agnostic\n// so those services can eventually be retired: every reader PREFERS the injection\n// (so the current, live path stays byte-for-byte unchanged) and FALLS BACK to the\n// §4 transport when the SDK is fetched from npm with no injection present.\n//\n// This module centralizes the `module.evaluation.module.bundler.*` reads (the same\n// philosophy as `sandboxUtils`' transport resolver) and exposes a PURE resolver for\n// the metadata-update subscription so the dual-mode decision is unit-tested without\n// a live bundler. The ambient reads themselves are thin (untestable without the\n// sandbox realm, exactly like `sandboxUtils.transport()`).\n\n/** vscode-style Event source: subscribe with a listener, get a disposable back. */\nexport type EventSource = (listener: (msg: any) => void) => { dispose(): void };\n\n/** The injected bundler's metadata emitter — fires `{type:'metadata-update', update}`\n * as files (re)compile. Absent when the SDK is npm-fetched (no injection). */\nexport interface InjectedMetadataEmitter {\n onMetadataChange: EventSource;\n /** Start the DelayedEmitter once a subscriber is attached (injected path only). */\n enable(): void;\n}\n\n/** The injected bundler object, or null when there is no injection (npm-fetched). */\nconst injectedBundler = (): any | null => {\n try {\n // @ts-ignore - `module.evaluation` is injected by the sandbox runtime\n return module?.evaluation?.module?.bundler ?? null;\n } catch {\n return null;\n }\n};\n\n/** The injected bundler's metadata emitter, or null when npm-fetched. */\nexport const getInjectedMetadataEmitter = (): InjectedMetadataEmitter | null => {\n const b = injectedBundler();\n if (b && typeof b.onMetadataChange === 'function' && b.onMetadataChangeEmitter) {\n return {\n onMetadataChange: b.onMetadataChange,\n enable: () => b.onMetadataChangeEmitter.enable(),\n };\n }\n return null;\n};\n\n/**\n * The injected bundler's synchronous metadata snapshot for the boot seed\n * (MDX_CONTENT_COLLECTIONS_SPEC §1.4). Returns the full `/app`-keyed collection the\n * bundler seeded (from the frontmatter sidecar) so the app's first render already\n * holds it — the SDK-side counterpart of the bundler seeding. Null when npm-fetched\n * (no in-realm bundler) → the SDK degrades to event-fill over the §4 transport, no\n * first-paint guarantee. The returned VALUE refs are the same objects the emitter\n * replays, so the `enable()` replay is a no-op (the §1.4 identity contract).\n */\nexport const getInjectedMetadataSnapshot = (): Record<string, Record<string, any>> | null => {\n const b = injectedBundler();\n if (b && typeof b.getMetadataSnapshot === 'function') {\n return b.getMetadataSnapshot();\n }\n return null;\n};\n\n/** What `boot` needs to subscribe to metadata updates: the `event` source to hand\n * `addListener` (the injected emitter, or `undefined` → listen over the transport)\n * and an `enable` to start the injected DelayedEmitter (a no-op off-injection). */\nexport interface MetadataSource {\n event?: EventSource;\n enable(): void;\n}\n\n/**\n * Resolve the metadata-update subscription source (PURE — the dual-mode decision).\n * With the injected emitter: use it and arm it, so the live path is byte-identical.\n * Without it (npm-fetched): return no `event`, so the caller's `addListener` falls\n * back to the §4 transport's `onMessage`, and `enable` is a no-op.\n */\nexport const resolveMetadataSource = (injected: InjectedMetadataEmitter | null): MetadataSource =>\n injected\n ? { event: injected.onMetadataChange, enable: () => injected.enable() }\n : { event: undefined, enable: () => {} };\n"],"mappings":";AAyBA,MAAM,kBAAkB,MAAkB;AACxC,MAAI;AAEF,WAAO,QAAQ,YAAY,QAAQ,WAAW;AAAA,EAChD,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGO,MAAM,6BAA6B,MAAsC;AAC9E,QAAM,IAAI,gBAAgB;AAC1B,MAAI,KAAK,OAAO,EAAE,qBAAqB,cAAc,EAAE,yBAAyB;AAC9E,WAAO;AAAA,MACL,kBAAkB,EAAE;AAAA,MACpB,QAAQ,MAAM,EAAE,wBAAwB,OAAO;AAAA,IACjD;AAAA,EACF;AACA,SAAO;AACT;AAWO,MAAM,8BAA8B,MAAkD;AAC3F,QAAM,IAAI,gBAAgB;AAC1B,MAAI,KAAK,OAAO,EAAE,wBAAwB,YAAY;AACpD,WAAO,EAAE,oBAAoB;AAAA,EAC/B;AACA,SAAO;AACT;AAgBO,MAAM,wBAAwB,CAAC,aACpC,WACI,EAAE,OAAO,SAAS,kBAAkB,QAAQ,MAAM,SAAS,OAAO,EAAE,IACpE,EAAE,OAAO,QAAW,QAAQ,MAAM;AAAC,EAAE;","names":[]}
|
package/dist/ipc.cjs
CHANGED
|
@@ -44,10 +44,7 @@ const revealRegion = async (region) => {
|
|
|
44
44
|
throw err;
|
|
45
45
|
}
|
|
46
46
|
};
|
|
47
|
-
const onRegionMessage = (listener) => (0, import_sandboxUtils.addListener)(
|
|
48
|
-
import_protocol.REGION_MESSAGE,
|
|
49
|
-
(m) => listener({ from: m.from, data: m.data })
|
|
50
|
-
);
|
|
47
|
+
const onRegionMessage = (listener) => (0, import_sandboxUtils.addListener)(import_protocol.REGION_MESSAGE, (m) => listener({ from: m.from, data: m.data }));
|
|
51
48
|
const useRegionMessage = () => {
|
|
52
49
|
const [msg, setMsg] = (0, import_react.useState)(null);
|
|
53
50
|
(0, import_react.useEffect)(() => onRegionMessage(setMsg), []);
|
package/dist/ipc.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ipc.ts"],"sourcesContent":["// Inter-app messaging (UI_AS_APPS_SPEC §5.6) — L2: chrome panels/regions talk to\n// each other across an edge BOTH sides declared in their bindings (sender `ipc.to`\n// + the `ipc` capability; receiver `ipc.accepts`). The host enforces both halves\n// and attaches an unspoofable `from`; you still treat the payload as untrusted.\nimport { useEffect, useState } from 'react';\nimport { protocolRequest, addListener } from './sandboxUtils';\nimport { PROTOCOL_IPC, REGION_MESSAGE } from './generated/protocol';\nimport { SCHEMES } from './protocolSchemes';\n\n/** A message delivered to this region. `from` is the SENDER's region, attached by\n * the host (unspoofable, T19); `data` is the sender-provided payload. */\nexport interface RegionMessage {\n from: string;\n data: unknown;\n}\n\n/**\n * Send a message to another region. Resolves when delivered; rejects (`forbidden`)\n * if the edge isn't two-sided-consented, the target isn't mounted, or this app\n * lacks the `ipc` capability.\n */\nexport const postToRegion = async (region: string, data: unknown): Promise<void> => {\n const res = (await protocolRequest(SCHEMES[PROTOCOL_IPC], 'post', [{ to: region, msg: data }])) as\n | { ok: true }\n | { ok: false; code?: string; message?: string }\n | undefined;\n if (!res || res.ok !== true) {\n const err = new Error(res?.message ?? 'ipc post failed') as Error & { code?: string };\n err.code = res?.code ?? 'unknown';\n throw err;\n }\n};\n\n/**\n * Ask the host to bring the user to `region` — the `reveal`-class method of\n * UI_AS_APPS_SPEC §4.1's focus model (R3-243). On mobile that is the column\n * transition (the panel's carousel advances to the main pane); on desktop a focus\n * move. Use it when a tap in your panel produces something to look at in another\n * column, right after the `postToRegion` that sends it there:\n *\n * ```ts\n * onClick={() => { // a REAL user gesture\n * void postToRegion('stage.conversation', { type: 'select-conversation', id });\n * void revealRegion('stage.conversation');\n * }}\n * ```\n *\n * **Call it synchronously inside a user-gesture handler.** The host reads its own\n * transient user activation and refuses without one — an app flipping columns on a\n * timer is the attention-steal primitive §4.1 is written against. It reaches only\n * regions you already have a two-sided IPC edge to, and only within the active\n * activity, so it can neither jump the user into an unrelated surface nor pull\n * focus back to the caller's own column.\n *\n * Resolves once the host has *considered* the request — deliberately NOT reporting\n * whether focus actually moved. Whether the user is moved is the host's call\n * (activation, rate limit, already-there), and an app that could observe the\n * refusal would be tempted to retry, which is the behaviour the gate exists to\n * stop. Rejects only on a genuine authorization failure (`forbidden`): no `ipc`\n * capability, or no declared edge to `region`.\n */\nexport const revealRegion = async (region: string): Promise<void> => {\n const res = (await protocolRequest(SCHEMES[PROTOCOL_IPC], 'reveal', [{ to: region }])) as\n | { ok: true }\n | { ok: false; code?: string; message?: string }\n | undefined;\n if (!res || res.ok !== true) {\n const err = new Error(res?.message ?? 'ipc reveal failed') as Error & { code?: string };\n err.code = res?.code ?? 'unknown';\n throw err;\n }\n};\n\n/** Subscribe to inbound region messages. Returns an unsubscribe fn. */\nexport const onRegionMessage = (listener: (msg: RegionMessage) => void): (() => void) =>\n addListener(REGION_MESSAGE, (m: { from: string; data: unknown })
|
|
1
|
+
{"version":3,"sources":["../src/ipc.ts"],"sourcesContent":["// Inter-app messaging (UI_AS_APPS_SPEC §5.6) — L2: chrome panels/regions talk to\n// each other across an edge BOTH sides declared in their bindings (sender `ipc.to`\n// + the `ipc` capability; receiver `ipc.accepts`). The host enforces both halves\n// and attaches an unspoofable `from`; you still treat the payload as untrusted.\nimport { useEffect, useState } from 'react';\nimport { protocolRequest, addListener } from './sandboxUtils';\nimport { PROTOCOL_IPC, REGION_MESSAGE } from './generated/protocol';\nimport { SCHEMES } from './protocolSchemes';\n\n/** A message delivered to this region. `from` is the SENDER's region, attached by\n * the host (unspoofable, T19); `data` is the sender-provided payload. */\nexport interface RegionMessage {\n from: string;\n data: unknown;\n}\n\n/**\n * Send a message to another region. Resolves when delivered; rejects (`forbidden`)\n * if the edge isn't two-sided-consented, the target isn't mounted, or this app\n * lacks the `ipc` capability.\n */\nexport const postToRegion = async (region: string, data: unknown): Promise<void> => {\n const res = (await protocolRequest(SCHEMES[PROTOCOL_IPC], 'post', [{ to: region, msg: data }])) as\n | { ok: true }\n | { ok: false; code?: string; message?: string }\n | undefined;\n if (!res || res.ok !== true) {\n const err = new Error(res?.message ?? 'ipc post failed') as Error & { code?: string };\n err.code = res?.code ?? 'unknown';\n throw err;\n }\n};\n\n/**\n * Ask the host to bring the user to `region` — the `reveal`-class method of\n * UI_AS_APPS_SPEC §4.1's focus model (R3-243). On mobile that is the column\n * transition (the panel's carousel advances to the main pane); on desktop a focus\n * move. Use it when a tap in your panel produces something to look at in another\n * column, right after the `postToRegion` that sends it there:\n *\n * ```ts\n * onClick={() => { // a REAL user gesture\n * void postToRegion('stage.conversation', { type: 'select-conversation', id });\n * void revealRegion('stage.conversation');\n * }}\n * ```\n *\n * **Call it synchronously inside a user-gesture handler.** The host reads its own\n * transient user activation and refuses without one — an app flipping columns on a\n * timer is the attention-steal primitive §4.1 is written against. It reaches only\n * regions you already have a two-sided IPC edge to, and only within the active\n * activity, so it can neither jump the user into an unrelated surface nor pull\n * focus back to the caller's own column.\n *\n * Resolves once the host has *considered* the request — deliberately NOT reporting\n * whether focus actually moved. Whether the user is moved is the host's call\n * (activation, rate limit, already-there), and an app that could observe the\n * refusal would be tempted to retry, which is the behaviour the gate exists to\n * stop. Rejects only on a genuine authorization failure (`forbidden`): no `ipc`\n * capability, or no declared edge to `region`.\n */\nexport const revealRegion = async (region: string): Promise<void> => {\n const res = (await protocolRequest(SCHEMES[PROTOCOL_IPC], 'reveal', [{ to: region }])) as\n | { ok: true }\n | { ok: false; code?: string; message?: string }\n | undefined;\n if (!res || res.ok !== true) {\n const err = new Error(res?.message ?? 'ipc reveal failed') as Error & { code?: string };\n err.code = res?.code ?? 'unknown';\n throw err;\n }\n};\n\n/** Subscribe to inbound region messages. Returns an unsubscribe fn. */\nexport const onRegionMessage = (listener: (msg: RegionMessage) => void): (() => void) =>\n addListener(REGION_MESSAGE, (m: { from: string; data: unknown }) => listener({ from: m.from, data: m.data }));\n\n/** React hook: the most recent inbound region message (or `null`). */\nexport const useRegionMessage = (): RegionMessage | null => {\n const [msg, setMsg] = useState<RegionMessage | null>(null);\n useEffect(() => onRegionMessage(setMsg), []);\n return msg;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAIA,mBAAoC;AACpC,0BAA6C;AAC7C,sBAA6C;AAC7C,6BAAwB;AAcjB,MAAM,eAAe,OAAO,QAAgB,SAAiC;AAClF,QAAM,MAAO,UAAM,qCAAgB,+BAAQ,4BAAY,GAAG,QAAQ,CAAC,EAAE,IAAI,QAAQ,KAAK,KAAK,CAAC,CAAC;AAI7F,MAAI,CAAC,OAAO,IAAI,OAAO,MAAM;AAC3B,UAAM,MAAM,IAAI,MAAM,KAAK,WAAW,iBAAiB;AACvD,QAAI,OAAO,KAAK,QAAQ;AACxB,UAAM;AAAA,EACR;AACF;AA8BO,MAAM,eAAe,OAAO,WAAkC;AACnE,QAAM,MAAO,UAAM,qCAAgB,+BAAQ,4BAAY,GAAG,UAAU,CAAC,EAAE,IAAI,OAAO,CAAC,CAAC;AAIpF,MAAI,CAAC,OAAO,IAAI,OAAO,MAAM;AAC3B,UAAM,MAAM,IAAI,MAAM,KAAK,WAAW,mBAAmB;AACzD,QAAI,OAAO,KAAK,QAAQ;AACxB,UAAM;AAAA,EACR;AACF;AAGO,MAAM,kBAAkB,CAAC,iBAC9B,iCAAY,gCAAgB,CAAC,MAAuC,SAAS,EAAE,MAAM,EAAE,MAAM,MAAM,EAAE,KAAK,CAAC,CAAC;AAGvG,MAAM,mBAAmB,MAA4B;AAC1D,QAAM,CAAC,KAAK,MAAM,QAAI,uBAA+B,IAAI;AACzD,8BAAU,MAAM,gBAAgB,MAAM,GAAG,CAAC,CAAC;AAC3C,SAAO;AACT;","names":[]}
|
package/dist/ipc.js
CHANGED
|
@@ -19,10 +19,7 @@ const revealRegion = async (region) => {
|
|
|
19
19
|
throw err;
|
|
20
20
|
}
|
|
21
21
|
};
|
|
22
|
-
const onRegionMessage = (listener) => addListener(
|
|
23
|
-
REGION_MESSAGE,
|
|
24
|
-
(m) => listener({ from: m.from, data: m.data })
|
|
25
|
-
);
|
|
22
|
+
const onRegionMessage = (listener) => addListener(REGION_MESSAGE, (m) => listener({ from: m.from, data: m.data }));
|
|
26
23
|
const useRegionMessage = () => {
|
|
27
24
|
const [msg, setMsg] = useState(null);
|
|
28
25
|
useEffect(() => onRegionMessage(setMsg), []);
|
package/dist/ipc.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/ipc.ts"],"sourcesContent":["// Inter-app messaging (UI_AS_APPS_SPEC §5.6) — L2: chrome panels/regions talk to\n// each other across an edge BOTH sides declared in their bindings (sender `ipc.to`\n// + the `ipc` capability; receiver `ipc.accepts`). The host enforces both halves\n// and attaches an unspoofable `from`; you still treat the payload as untrusted.\nimport { useEffect, useState } from 'react';\nimport { protocolRequest, addListener } from './sandboxUtils';\nimport { PROTOCOL_IPC, REGION_MESSAGE } from './generated/protocol';\nimport { SCHEMES } from './protocolSchemes';\n\n/** A message delivered to this region. `from` is the SENDER's region, attached by\n * the host (unspoofable, T19); `data` is the sender-provided payload. */\nexport interface RegionMessage {\n from: string;\n data: unknown;\n}\n\n/**\n * Send a message to another region. Resolves when delivered; rejects (`forbidden`)\n * if the edge isn't two-sided-consented, the target isn't mounted, or this app\n * lacks the `ipc` capability.\n */\nexport const postToRegion = async (region: string, data: unknown): Promise<void> => {\n const res = (await protocolRequest(SCHEMES[PROTOCOL_IPC], 'post', [{ to: region, msg: data }])) as\n | { ok: true }\n | { ok: false; code?: string; message?: string }\n | undefined;\n if (!res || res.ok !== true) {\n const err = new Error(res?.message ?? 'ipc post failed') as Error & { code?: string };\n err.code = res?.code ?? 'unknown';\n throw err;\n }\n};\n\n/**\n * Ask the host to bring the user to `region` — the `reveal`-class method of\n * UI_AS_APPS_SPEC §4.1's focus model (R3-243). On mobile that is the column\n * transition (the panel's carousel advances to the main pane); on desktop a focus\n * move. Use it when a tap in your panel produces something to look at in another\n * column, right after the `postToRegion` that sends it there:\n *\n * ```ts\n * onClick={() => { // a REAL user gesture\n * void postToRegion('stage.conversation', { type: 'select-conversation', id });\n * void revealRegion('stage.conversation');\n * }}\n * ```\n *\n * **Call it synchronously inside a user-gesture handler.** The host reads its own\n * transient user activation and refuses without one — an app flipping columns on a\n * timer is the attention-steal primitive §4.1 is written against. It reaches only\n * regions you already have a two-sided IPC edge to, and only within the active\n * activity, so it can neither jump the user into an unrelated surface nor pull\n * focus back to the caller's own column.\n *\n * Resolves once the host has *considered* the request — deliberately NOT reporting\n * whether focus actually moved. Whether the user is moved is the host's call\n * (activation, rate limit, already-there), and an app that could observe the\n * refusal would be tempted to retry, which is the behaviour the gate exists to\n * stop. Rejects only on a genuine authorization failure (`forbidden`): no `ipc`\n * capability, or no declared edge to `region`.\n */\nexport const revealRegion = async (region: string): Promise<void> => {\n const res = (await protocolRequest(SCHEMES[PROTOCOL_IPC], 'reveal', [{ to: region }])) as\n | { ok: true }\n | { ok: false; code?: string; message?: string }\n | undefined;\n if (!res || res.ok !== true) {\n const err = new Error(res?.message ?? 'ipc reveal failed') as Error & { code?: string };\n err.code = res?.code ?? 'unknown';\n throw err;\n }\n};\n\n/** Subscribe to inbound region messages. Returns an unsubscribe fn. */\nexport const onRegionMessage = (listener: (msg: RegionMessage) => void): (() => void) =>\n addListener(REGION_MESSAGE, (m: { from: string; data: unknown })
|
|
1
|
+
{"version":3,"sources":["../src/ipc.ts"],"sourcesContent":["// Inter-app messaging (UI_AS_APPS_SPEC §5.6) — L2: chrome panels/regions talk to\n// each other across an edge BOTH sides declared in their bindings (sender `ipc.to`\n// + the `ipc` capability; receiver `ipc.accepts`). The host enforces both halves\n// and attaches an unspoofable `from`; you still treat the payload as untrusted.\nimport { useEffect, useState } from 'react';\nimport { protocolRequest, addListener } from './sandboxUtils';\nimport { PROTOCOL_IPC, REGION_MESSAGE } from './generated/protocol';\nimport { SCHEMES } from './protocolSchemes';\n\n/** A message delivered to this region. `from` is the SENDER's region, attached by\n * the host (unspoofable, T19); `data` is the sender-provided payload. */\nexport interface RegionMessage {\n from: string;\n data: unknown;\n}\n\n/**\n * Send a message to another region. Resolves when delivered; rejects (`forbidden`)\n * if the edge isn't two-sided-consented, the target isn't mounted, or this app\n * lacks the `ipc` capability.\n */\nexport const postToRegion = async (region: string, data: unknown): Promise<void> => {\n const res = (await protocolRequest(SCHEMES[PROTOCOL_IPC], 'post', [{ to: region, msg: data }])) as\n | { ok: true }\n | { ok: false; code?: string; message?: string }\n | undefined;\n if (!res || res.ok !== true) {\n const err = new Error(res?.message ?? 'ipc post failed') as Error & { code?: string };\n err.code = res?.code ?? 'unknown';\n throw err;\n }\n};\n\n/**\n * Ask the host to bring the user to `region` — the `reveal`-class method of\n * UI_AS_APPS_SPEC §4.1's focus model (R3-243). On mobile that is the column\n * transition (the panel's carousel advances to the main pane); on desktop a focus\n * move. Use it when a tap in your panel produces something to look at in another\n * column, right after the `postToRegion` that sends it there:\n *\n * ```ts\n * onClick={() => { // a REAL user gesture\n * void postToRegion('stage.conversation', { type: 'select-conversation', id });\n * void revealRegion('stage.conversation');\n * }}\n * ```\n *\n * **Call it synchronously inside a user-gesture handler.** The host reads its own\n * transient user activation and refuses without one — an app flipping columns on a\n * timer is the attention-steal primitive §4.1 is written against. It reaches only\n * regions you already have a two-sided IPC edge to, and only within the active\n * activity, so it can neither jump the user into an unrelated surface nor pull\n * focus back to the caller's own column.\n *\n * Resolves once the host has *considered* the request — deliberately NOT reporting\n * whether focus actually moved. Whether the user is moved is the host's call\n * (activation, rate limit, already-there), and an app that could observe the\n * refusal would be tempted to retry, which is the behaviour the gate exists to\n * stop. Rejects only on a genuine authorization failure (`forbidden`): no `ipc`\n * capability, or no declared edge to `region`.\n */\nexport const revealRegion = async (region: string): Promise<void> => {\n const res = (await protocolRequest(SCHEMES[PROTOCOL_IPC], 'reveal', [{ to: region }])) as\n | { ok: true }\n | { ok: false; code?: string; message?: string }\n | undefined;\n if (!res || res.ok !== true) {\n const err = new Error(res?.message ?? 'ipc reveal failed') as Error & { code?: string };\n err.code = res?.code ?? 'unknown';\n throw err;\n }\n};\n\n/** Subscribe to inbound region messages. Returns an unsubscribe fn. */\nexport const onRegionMessage = (listener: (msg: RegionMessage) => void): (() => void) =>\n addListener(REGION_MESSAGE, (m: { from: string; data: unknown }) => listener({ from: m.from, data: m.data }));\n\n/** React hook: the most recent inbound region message (or `null`). */\nexport const useRegionMessage = (): RegionMessage | null => {\n const [msg, setMsg] = useState<RegionMessage | null>(null);\n useEffect(() => onRegionMessage(setMsg), []);\n return msg;\n};\n"],"mappings":";AAIA,SAAS,WAAW,gBAAgB;AACpC,SAAS,iBAAiB,mBAAmB;AAC7C,SAAS,cAAc,sBAAsB;AAC7C,SAAS,eAAe;AAcjB,MAAM,eAAe,OAAO,QAAgB,SAAiC;AAClF,QAAM,MAAO,MAAM,gBAAgB,QAAQ,YAAY,GAAG,QAAQ,CAAC,EAAE,IAAI,QAAQ,KAAK,KAAK,CAAC,CAAC;AAI7F,MAAI,CAAC,OAAO,IAAI,OAAO,MAAM;AAC3B,UAAM,MAAM,IAAI,MAAM,KAAK,WAAW,iBAAiB;AACvD,QAAI,OAAO,KAAK,QAAQ;AACxB,UAAM;AAAA,EACR;AACF;AA8BO,MAAM,eAAe,OAAO,WAAkC;AACnE,QAAM,MAAO,MAAM,gBAAgB,QAAQ,YAAY,GAAG,UAAU,CAAC,EAAE,IAAI,OAAO,CAAC,CAAC;AAIpF,MAAI,CAAC,OAAO,IAAI,OAAO,MAAM;AAC3B,UAAM,MAAM,IAAI,MAAM,KAAK,WAAW,mBAAmB;AACzD,QAAI,OAAO,KAAK,QAAQ;AACxB,UAAM;AAAA,EACR;AACF;AAGO,MAAM,kBAAkB,CAAC,aAC9B,YAAY,gBAAgB,CAAC,MAAuC,SAAS,EAAE,MAAM,EAAE,MAAM,MAAM,EAAE,KAAK,CAAC,CAAC;AAGvG,MAAM,mBAAmB,MAA4B;AAC1D,QAAM,CAAC,KAAK,MAAM,IAAI,SAA+B,IAAI;AACzD,YAAU,MAAM,gBAAgB,MAAM,GAAG,CAAC,CAAC;AAC3C,SAAO;AACT;","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
|
|
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 => 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,SAA0B,eAAe,IAAI,KAAK,WAAW,KAAK,IAAI;AAoBnG,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
|
@@ -5,17 +5,17 @@
|
|
|
5
5
|
* array means "no attributes" (a bare mark).
|
|
6
6
|
*/
|
|
7
7
|
declare const IR_MARKERS: {
|
|
8
|
-
readonly
|
|
9
|
-
readonly
|
|
10
|
-
readonly
|
|
11
|
-
readonly
|
|
12
|
-
readonly
|
|
13
|
-
readonly
|
|
14
|
-
readonly
|
|
15
|
-
readonly
|
|
16
|
-
readonly
|
|
17
|
-
readonly
|
|
18
|
-
readonly
|
|
8
|
+
readonly 'ir.open': readonly ["url", "provider", "ns", "repo", "ref", "refKind"];
|
|
9
|
+
readonly 'ir.fetch': readonly ["source", "bytes", "requestCount", "cacheHit", "httpStatus"];
|
|
10
|
+
readonly 'ir.mount': readonly ["phantomCount", "writablePrimed"];
|
|
11
|
+
readonly 'ir.sandbox.boot': readonly [];
|
|
12
|
+
readonly 'ir.transpile': readonly ["moduleCount", "cacheHit", "bytesIn", "bytesOut"];
|
|
13
|
+
readonly 'ir.deps': readonly ["depCount", "bytes", "requestCount", "cacheHit", "cdn"];
|
|
14
|
+
readonly 'ir.eval': readonly ["moduleCount"];
|
|
15
|
+
readonly 'ir.fmp': readonly [];
|
|
16
|
+
readonly 'ir.interactive': readonly ["cold"];
|
|
17
|
+
readonly 'ir.verify': readonly ["result", "blocking"];
|
|
18
|
+
readonly 'ir.refresh': readonly ["bytes"];
|
|
19
19
|
};
|
|
20
20
|
/** A canonical `ir.*` load-profiling marker name (a key of {@link IR_MARKERS}). */
|
|
21
21
|
type IrMarkerName = keyof typeof IR_MARKERS;
|
package/dist/irMarkers.d.ts
CHANGED
|
@@ -5,17 +5,17 @@
|
|
|
5
5
|
* array means "no attributes" (a bare mark).
|
|
6
6
|
*/
|
|
7
7
|
declare const IR_MARKERS: {
|
|
8
|
-
readonly
|
|
9
|
-
readonly
|
|
10
|
-
readonly
|
|
11
|
-
readonly
|
|
12
|
-
readonly
|
|
13
|
-
readonly
|
|
14
|
-
readonly
|
|
15
|
-
readonly
|
|
16
|
-
readonly
|
|
17
|
-
readonly
|
|
18
|
-
readonly
|
|
8
|
+
readonly 'ir.open': readonly ["url", "provider", "ns", "repo", "ref", "refKind"];
|
|
9
|
+
readonly 'ir.fetch': readonly ["source", "bytes", "requestCount", "cacheHit", "httpStatus"];
|
|
10
|
+
readonly 'ir.mount': readonly ["phantomCount", "writablePrimed"];
|
|
11
|
+
readonly 'ir.sandbox.boot': readonly [];
|
|
12
|
+
readonly 'ir.transpile': readonly ["moduleCount", "cacheHit", "bytesIn", "bytesOut"];
|
|
13
|
+
readonly 'ir.deps': readonly ["depCount", "bytes", "requestCount", "cacheHit", "cdn"];
|
|
14
|
+
readonly 'ir.eval': readonly ["moduleCount"];
|
|
15
|
+
readonly 'ir.fmp': readonly [];
|
|
16
|
+
readonly 'ir.interactive': readonly ["cold"];
|
|
17
|
+
readonly 'ir.verify': readonly ["result", "blocking"];
|
|
18
|
+
readonly 'ir.refresh': readonly ["bytes"];
|
|
19
19
|
};
|
|
20
20
|
/** A canonical `ir.*` load-profiling marker name (a key of {@link IR_MARKERS}). */
|
|
21
21
|
type IrMarkerName = keyof typeof IR_MARKERS;
|
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
|
|
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 => 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,SAA0B,eAAe,IAAI,KAAK,WAAW,KAAK,IAAI;AAoBnG,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/launch.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/launch.ts"],"sourcesContent":["// Launch — the TO-RUN twin of `invokeTask` (STANDING_APP_LIFECYCLE_SPEC §1–§6).\n// Where `invokeTask` is FOR-RESULT (await one value, callee exits), `launch` is\n// TO-RUN (non-blocking, standing): it starts a bound program RUNNING in a region\n// and hands back a HANDLE, never a typed value (R-SAL-1). The launched app earns\n// its OWN grants — the only launcher→launched data path is the explicitly\n// delegated `capDir`/`capFile` in `input` (§5), attenuated + tainted + gated host-\n// side. The host owns the whole lifecycle (visibility, budget, revocation,\n// teardown); the launcher only OBSERVES via the handle.\n//\n// A caller that wants a typed value back uses `invokeTask` instead — the two are\n// siblings, not a replacement.\nimport { protocolRequest, sendMessage, addListener } from './sandboxUtils';\nimport { LAUNCH_DISMISS, LAUNCH_ENDED, PROTOCOL_LAUNCH } from './generated/protocol';\nimport { SCHEMES } from './protocolSchemes';\n\n/** Where a launched program runs (§6). `overlay` covers the caller's own region\n * with opaque host chrome; `stage` replaces the focal app (the elevated into-\n * stage surface, §7 — refused above the stage-principal ceiling). */\nexport type LaunchRegion = 'overlay' | 'stage';\n\n/**\n * What to launch (§3) — binding-resolved, NEVER a caller-named app. Exactly one of:\n * - `entryPoint`: a sibling entry point of the caller's OWN repo (the mini-app\n * case, `AGENT_AUTHORING §5`) — **rejected `forbidden` until program-identity\n * `appKey` lands** (R-SAL-2a);\n * - `task`: a task contract (`open-project`, …), resolved through the user-\n * overridable `task.<name>` binding to whichever app the user bound (§3 kind 2).\n */\nexport interface LaunchTarget {\n /** Kind 1 — a sibling entry point of the caller's own repo (mini-app overlay). */\n entryPoint?: string;\n /** Kind 2 — a task contract name (the host resolves the bound provider). */\n task?: string;\n /** Optional accepted contract version for a `task` target (semver, e.g. `^1`). */\n version?: string;\n}\n\nexport interface LaunchOptions {\n /** Where it runs (§6). */\n region: LaunchRegion;\n /**\n * Delegations + plain data handed to the launched app. `$cap:'dir'|'file'`\n * markers (`capDir`/`capFile`, exported from `./tasks`) are resolved against the\n * launcher's OWN grants and minted as attenuated, `ro`-by-default chroots (§5) —\n * you can only delegate what you already hold. Everything else is plain data.\n */\n input?: Record<string, unknown>;\n}\n\n/** The live state of a launch (§2). Terminal states (`dismissed`/`revoked`/\n * `failed`) are reached identically for self-exit, user dismiss, and host revoke —\n * the host debounces so the launcher gets no timing oracle (R-SAL-1 / §6.4). */\nexport type LaunchStatus = 'running' | 'dismissed' | 'revoked' | 'failed';\n\n/** Machine codes a refused `launch` resolves with (§8). */\nexport type LaunchErrorCode =\n | 'forbidden' // undeclared target; sibling (pre-AA-01); self-resolving target; stage cap absent\n | 'unsupported' // no provider bound / entry point absent / unknown contract version\n | 'budget' // over an R-SAL-8 concurrency/rate limit\n | 'revoked' // a delegated mount was revoked during the create-and-bind window\n | 'cancelled' // the user dismissed the host launch affordance (into-stage only)\n | 'invalid-params' // a malformed `capDir`/`capFile` (`..`/absolute/mode-escalation)\n | 'unknown';\n\n/**\n * The control channel back to a launch — the ONLY thing a launcher gets (§2).\n * There is no typed return value (R-SAL-1); `status`/`onDismiss` are debounced so\n * they cannot time-distinguish a self-exit from a user dismiss.\n */\nexport interface LaunchHandle {\n /** Host-assigned id for this launch. */\n readonly launchId: string;\n /** The current lifecycle state (§2). */\n readonly status: LaunchStatus;\n /** Ask the host to tear this launch down. Idempotent (double-dismiss is a no-op). */\n dismiss(): void;\n /**\n * Observe the launch ending (self-exit / user dismiss / host revoke — fired\n * identically, R-SAL-1). Returns an unsubscribe fn. Fires at most once; if the\n * launch has already ended it fires on the next tick.\n */\n onDismiss(cb: () => void): () => void;\n}\n\ninterface LaunchEndedMessage {\n launchId: string;\n status: Exclude<LaunchStatus, 'running'>;\n}\n\n/** Live handles awaiting their terminal `launch-ended` message, keyed by launchId. */\nconst liveHandles = new Map<string, LaunchHandleImpl>();\n\n// The host delivers ONE `launch-ended` message per launch when it tears down —\n// the SAME message shape for self-exit, dismiss, and revoke (the host debounces\n// so the timing is not an oracle, §6.4). We fan it out to the matching handle.\naddListener(LAUNCH_ENDED, (m: LaunchEndedMessage) => {\n const h = liveHandles.get(m.launchId);\n if (h) h._end(m.status);\n});\n\nclass LaunchHandleImpl implements LaunchHandle {\n #status: LaunchStatus = 'running';\n #dismissListeners = new Set<() => void>();\n #ended = false;\n\n constructor(readonly launchId: string) {\n liveHandles.set(launchId, this);\n }\n\n get status(): LaunchStatus {\n return this.#status;\n }\n\n dismiss(): void {\n if (this.#ended) return;\n // Fire-and-forget: the host owns teardown and answers with `launch-ended`,\n // which drives `_end` (so status/onDismiss are host-authoritative, never\n // optimistically local — a dismiss the host refuses would otherwise desync).\n sendMessage(LAUNCH_DISMISS, { launchId: this.launchId });\n }\n\n onDismiss(cb: () => void): () => void {\n if (this.#ended) {\n // Already ended: fire on the next tick so the contract (\"returns an\n // unsubscribe\") holds and the callback never runs synchronously mid-register.\n queueMicrotask(cb);\n return () => {};\n }\n this.#dismissListeners.add(cb);\n return () => {\n this.#dismissListeners.delete(cb);\n };\n }\n\n /** Host-driven terminal transition — the only writer of `status`. Idempotent. */\n _end(status: Exclude<LaunchStatus, 'running'>): void {\n if (this.#ended) return;\n this.#ended = true;\n this.#status = status;\n liveHandles.delete(this.launchId);\n const listeners = [...this.#dismissListeners];\n this.#dismissListeners.clear();\n for (const l of listeners) {\n try {\n l();\n } catch {\n /* a launcher's own callback must never wedge teardown */\n }\n }\n }\n}\n\n/**\n * Launch a bound program to RUN in a region (§2). Non-blocking: resolves once the\n * frame is created and bound, with a {@link LaunchHandle} — or a typed\n * `{ ok:false, code }` on refusal (§8), NEVER a throw for an ordinary refusal (so\n * a launcher branches on `code` without a try/catch). The launched app runs under\n * its OWN grants; the launcher's authority does not flow to it (R-SAL-4).\n *\n * const h = await launch({ task: 'open-project' }, {\n * region: 'stage',\n * input: { dir: capDir({ mountId: 'space:abc', relPath: 'proj' }, { mode: 'ro' }) },\n * });\n * if ('ok' in h && h.ok === false) { ...handle h.code... }\n * else { h.onDismiss(() => ...); }\n */\nexport const launch = async (\n target: LaunchTarget,\n opts: LaunchOptions,\n): Promise<LaunchHandle | { ok: false; code: LaunchErrorCode }> => {\n // The host wraps a successful handler return as `{ ok:true, data }` (the same\n // Recipe-B framing `invokeTask` uses); a refusal is `{ ok:false, code }`.\n const res = (await protocolRequest(SCHEMES[PROTOCOL_LAUNCH], 'create', [{ target, opts }])) as\n | { ok: true; data: { launchId: string } }\n | { ok: false; code?: LaunchErrorCode }\n | undefined;\n if (!res || res.ok !== true || !res.data?.launchId) {\n const code = res && res.ok === false ?
|
|
1
|
+
{"version":3,"sources":["../src/launch.ts"],"sourcesContent":["// Launch — the TO-RUN twin of `invokeTask` (STANDING_APP_LIFECYCLE_SPEC §1–§6).\n// Where `invokeTask` is FOR-RESULT (await one value, callee exits), `launch` is\n// TO-RUN (non-blocking, standing): it starts a bound program RUNNING in a region\n// and hands back a HANDLE, never a typed value (R-SAL-1). The launched app earns\n// its OWN grants — the only launcher→launched data path is the explicitly\n// delegated `capDir`/`capFile` in `input` (§5), attenuated + tainted + gated host-\n// side. The host owns the whole lifecycle (visibility, budget, revocation,\n// teardown); the launcher only OBSERVES via the handle.\n//\n// A caller that wants a typed value back uses `invokeTask` instead — the two are\n// siblings, not a replacement.\nimport { protocolRequest, sendMessage, addListener } from './sandboxUtils';\nimport { LAUNCH_DISMISS, LAUNCH_ENDED, PROTOCOL_LAUNCH } from './generated/protocol';\nimport { SCHEMES } from './protocolSchemes';\n\n/** Where a launched program runs (§6). `overlay` covers the caller's own region\n * with opaque host chrome; `stage` replaces the focal app (the elevated into-\n * stage surface, §7 — refused above the stage-principal ceiling). */\nexport type LaunchRegion = 'overlay' | 'stage';\n\n/**\n * What to launch (§3) — binding-resolved, NEVER a caller-named app. Exactly one of:\n * - `entryPoint`: a sibling entry point of the caller's OWN repo (the mini-app\n * case, `AGENT_AUTHORING §5`) — **rejected `forbidden` until program-identity\n * `appKey` lands** (R-SAL-2a);\n * - `task`: a task contract (`open-project`, …), resolved through the user-\n * overridable `task.<name>` binding to whichever app the user bound (§3 kind 2).\n */\nexport interface LaunchTarget {\n /** Kind 1 — a sibling entry point of the caller's own repo (mini-app overlay). */\n entryPoint?: string;\n /** Kind 2 — a task contract name (the host resolves the bound provider). */\n task?: string;\n /** Optional accepted contract version for a `task` target (semver, e.g. `^1`). */\n version?: string;\n}\n\nexport interface LaunchOptions {\n /** Where it runs (§6). */\n region: LaunchRegion;\n /**\n * Delegations + plain data handed to the launched app. `$cap:'dir'|'file'`\n * markers (`capDir`/`capFile`, exported from `./tasks`) are resolved against the\n * launcher's OWN grants and minted as attenuated, `ro`-by-default chroots (§5) —\n * you can only delegate what you already hold. Everything else is plain data.\n */\n input?: Record<string, unknown>;\n}\n\n/** The live state of a launch (§2). Terminal states (`dismissed`/`revoked`/\n * `failed`) are reached identically for self-exit, user dismiss, and host revoke —\n * the host debounces so the launcher gets no timing oracle (R-SAL-1 / §6.4). */\nexport type LaunchStatus = 'running' | 'dismissed' | 'revoked' | 'failed';\n\n/** Machine codes a refused `launch` resolves with (§8). */\nexport type LaunchErrorCode =\n | 'forbidden' // undeclared target; sibling (pre-AA-01); self-resolving target; stage cap absent\n | 'unsupported' // no provider bound / entry point absent / unknown contract version\n | 'budget' // over an R-SAL-8 concurrency/rate limit\n | 'revoked' // a delegated mount was revoked during the create-and-bind window\n | 'cancelled' // the user dismissed the host launch affordance (into-stage only)\n | 'invalid-params' // a malformed `capDir`/`capFile` (`..`/absolute/mode-escalation)\n | 'unknown';\n\n/**\n * The control channel back to a launch — the ONLY thing a launcher gets (§2).\n * There is no typed return value (R-SAL-1); `status`/`onDismiss` are debounced so\n * they cannot time-distinguish a self-exit from a user dismiss.\n */\nexport interface LaunchHandle {\n /** Host-assigned id for this launch. */\n readonly launchId: string;\n /** The current lifecycle state (§2). */\n readonly status: LaunchStatus;\n /** Ask the host to tear this launch down. Idempotent (double-dismiss is a no-op). */\n dismiss(): void;\n /**\n * Observe the launch ending (self-exit / user dismiss / host revoke — fired\n * identically, R-SAL-1). Returns an unsubscribe fn. Fires at most once; if the\n * launch has already ended it fires on the next tick.\n */\n onDismiss(cb: () => void): () => void;\n}\n\ninterface LaunchEndedMessage {\n launchId: string;\n status: Exclude<LaunchStatus, 'running'>;\n}\n\n/** Live handles awaiting their terminal `launch-ended` message, keyed by launchId. */\nconst liveHandles = new Map<string, LaunchHandleImpl>();\n\n// The host delivers ONE `launch-ended` message per launch when it tears down —\n// the SAME message shape for self-exit, dismiss, and revoke (the host debounces\n// so the timing is not an oracle, §6.4). We fan it out to the matching handle.\naddListener(LAUNCH_ENDED, (m: LaunchEndedMessage) => {\n const h = liveHandles.get(m.launchId);\n if (h) h._end(m.status);\n});\n\nclass LaunchHandleImpl implements LaunchHandle {\n #status: LaunchStatus = 'running';\n #dismissListeners = new Set<() => void>();\n #ended = false;\n\n constructor(readonly launchId: string) {\n liveHandles.set(launchId, this);\n }\n\n get status(): LaunchStatus {\n return this.#status;\n }\n\n dismiss(): void {\n if (this.#ended) return;\n // Fire-and-forget: the host owns teardown and answers with `launch-ended`,\n // which drives `_end` (so status/onDismiss are host-authoritative, never\n // optimistically local — a dismiss the host refuses would otherwise desync).\n sendMessage(LAUNCH_DISMISS, { launchId: this.launchId });\n }\n\n onDismiss(cb: () => void): () => void {\n if (this.#ended) {\n // Already ended: fire on the next tick so the contract (\"returns an\n // unsubscribe\") holds and the callback never runs synchronously mid-register.\n queueMicrotask(cb);\n return () => {};\n }\n this.#dismissListeners.add(cb);\n return () => {\n this.#dismissListeners.delete(cb);\n };\n }\n\n /** Host-driven terminal transition — the only writer of `status`. Idempotent. */\n _end(status: Exclude<LaunchStatus, 'running'>): void {\n if (this.#ended) return;\n this.#ended = true;\n this.#status = status;\n liveHandles.delete(this.launchId);\n const listeners = [...this.#dismissListeners];\n this.#dismissListeners.clear();\n for (const l of listeners) {\n try {\n l();\n } catch {\n /* a launcher's own callback must never wedge teardown */\n }\n }\n }\n}\n\n/**\n * Launch a bound program to RUN in a region (§2). Non-blocking: resolves once the\n * frame is created and bound, with a {@link LaunchHandle} — or a typed\n * `{ ok:false, code }` on refusal (§8), NEVER a throw for an ordinary refusal (so\n * a launcher branches on `code` without a try/catch). The launched app runs under\n * its OWN grants; the launcher's authority does not flow to it (R-SAL-4).\n *\n * const h = await launch({ task: 'open-project' }, {\n * region: 'stage',\n * input: { dir: capDir({ mountId: 'space:abc', relPath: 'proj' }, { mode: 'ro' }) },\n * });\n * if ('ok' in h && h.ok === false) { ...handle h.code... }\n * else { h.onDismiss(() => ...); }\n */\nexport const launch = async (\n target: LaunchTarget,\n opts: LaunchOptions,\n): Promise<LaunchHandle | { ok: false; code: LaunchErrorCode }> => {\n // The host wraps a successful handler return as `{ ok:true, data }` (the same\n // Recipe-B framing `invokeTask` uses); a refusal is `{ ok:false, code }`.\n const res = (await protocolRequest(SCHEMES[PROTOCOL_LAUNCH], 'create', [{ target, opts }])) as\n | { ok: true; data: { launchId: string } }\n | { ok: false; code?: LaunchErrorCode }\n | undefined;\n if (!res || res.ok !== true || !res.data?.launchId) {\n const code = res && res.ok === false ? res.code ?? 'unknown' : 'unknown';\n return { ok: false, code };\n }\n return new LaunchHandleImpl(res.data.launchId);\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAWA,0BAA0D;AAC1D,sBAA8D;AAC9D,6BAAwB;AAbxB;AA0FA,MAAM,cAAc,oBAAI,IAA8B;AAAA,IAKtD,iCAAY,8BAAc,CAAC,MAA0B;AACnD,QAAM,IAAI,YAAY,IAAI,EAAE,QAAQ;AACpC,MAAI,EAAG,GAAE,KAAK,EAAE,MAAM;AACxB,CAAC;AAED,MAAM,iBAAyC;AAAA,EAK7C,YAAqB,UAAkB;AAAlB;AAJrB,gCAAwB;AACxB,0CAAoB,oBAAI,IAAgB;AACxC,+BAAS;AAGP,gBAAY,IAAI,UAAU,IAAI;AAAA,EAChC;AAAA,EAEA,IAAI,SAAuB;AACzB,WAAO,mBAAK;AAAA,EACd;AAAA,EAEA,UAAgB;AACd,QAAI,mBAAK,QAAQ;AAIjB,yCAAY,gCAAgB,EAAE,UAAU,KAAK,SAAS,CAAC;AAAA,EACzD;AAAA,EAEA,UAAU,IAA4B;AACpC,QAAI,mBAAK,SAAQ;AAGf,qBAAe,EAAE;AACjB,aAAO,MAAM;AAAA,MAAC;AAAA,IAChB;AACA,uBAAK,mBAAkB,IAAI,EAAE;AAC7B,WAAO,MAAM;AACX,yBAAK,mBAAkB,OAAO,EAAE;AAAA,IAClC;AAAA,EACF;AAAA;AAAA,EAGA,KAAK,QAAgD;AACnD,QAAI,mBAAK,QAAQ;AACjB,uBAAK,QAAS;AACd,uBAAK,SAAU;AACf,gBAAY,OAAO,KAAK,QAAQ;AAChC,UAAM,YAAY,CAAC,GAAG,mBAAK,kBAAiB;AAC5C,uBAAK,mBAAkB,MAAM;AAC7B,eAAW,KAAK,WAAW;AACzB,UAAI;AACF,UAAE;AAAA,MACJ,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;AAjDE;AACA;AACA;AA+DK,MAAM,SAAS,OACpB,QACA,SACiE;AAGjE,QAAM,MAAO,UAAM,qCAAgB,+BAAQ,+BAAe,GAAG,UAAU,CAAC,EAAE,QAAQ,KAAK,CAAC,CAAC;AAIzF,MAAI,CAAC,OAAO,IAAI,OAAO,QAAQ,CAAC,IAAI,MAAM,UAAU;AAClD,UAAM,OAAO,OAAO,IAAI,OAAO,QAAQ,IAAI,QAAQ,YAAY;AAC/D,WAAO,EAAE,IAAI,OAAO,KAAK;AAAA,EAC3B;AACA,SAAO,IAAI,iBAAiB,IAAI,KAAK,QAAQ;AAC/C;","names":[]}
|
package/dist/launch.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/launch.ts"],"sourcesContent":["// Launch — the TO-RUN twin of `invokeTask` (STANDING_APP_LIFECYCLE_SPEC §1–§6).\n// Where `invokeTask` is FOR-RESULT (await one value, callee exits), `launch` is\n// TO-RUN (non-blocking, standing): it starts a bound program RUNNING in a region\n// and hands back a HANDLE, never a typed value (R-SAL-1). The launched app earns\n// its OWN grants — the only launcher→launched data path is the explicitly\n// delegated `capDir`/`capFile` in `input` (§5), attenuated + tainted + gated host-\n// side. The host owns the whole lifecycle (visibility, budget, revocation,\n// teardown); the launcher only OBSERVES via the handle.\n//\n// A caller that wants a typed value back uses `invokeTask` instead — the two are\n// siblings, not a replacement.\nimport { protocolRequest, sendMessage, addListener } from './sandboxUtils';\nimport { LAUNCH_DISMISS, LAUNCH_ENDED, PROTOCOL_LAUNCH } from './generated/protocol';\nimport { SCHEMES } from './protocolSchemes';\n\n/** Where a launched program runs (§6). `overlay` covers the caller's own region\n * with opaque host chrome; `stage` replaces the focal app (the elevated into-\n * stage surface, §7 — refused above the stage-principal ceiling). */\nexport type LaunchRegion = 'overlay' | 'stage';\n\n/**\n * What to launch (§3) — binding-resolved, NEVER a caller-named app. Exactly one of:\n * - `entryPoint`: a sibling entry point of the caller's OWN repo (the mini-app\n * case, `AGENT_AUTHORING §5`) — **rejected `forbidden` until program-identity\n * `appKey` lands** (R-SAL-2a);\n * - `task`: a task contract (`open-project`, …), resolved through the user-\n * overridable `task.<name>` binding to whichever app the user bound (§3 kind 2).\n */\nexport interface LaunchTarget {\n /** Kind 1 — a sibling entry point of the caller's own repo (mini-app overlay). */\n entryPoint?: string;\n /** Kind 2 — a task contract name (the host resolves the bound provider). */\n task?: string;\n /** Optional accepted contract version for a `task` target (semver, e.g. `^1`). */\n version?: string;\n}\n\nexport interface LaunchOptions {\n /** Where it runs (§6). */\n region: LaunchRegion;\n /**\n * Delegations + plain data handed to the launched app. `$cap:'dir'|'file'`\n * markers (`capDir`/`capFile`, exported from `./tasks`) are resolved against the\n * launcher's OWN grants and minted as attenuated, `ro`-by-default chroots (§5) —\n * you can only delegate what you already hold. Everything else is plain data.\n */\n input?: Record<string, unknown>;\n}\n\n/** The live state of a launch (§2). Terminal states (`dismissed`/`revoked`/\n * `failed`) are reached identically for self-exit, user dismiss, and host revoke —\n * the host debounces so the launcher gets no timing oracle (R-SAL-1 / §6.4). */\nexport type LaunchStatus = 'running' | 'dismissed' | 'revoked' | 'failed';\n\n/** Machine codes a refused `launch` resolves with (§8). */\nexport type LaunchErrorCode =\n | 'forbidden' // undeclared target; sibling (pre-AA-01); self-resolving target; stage cap absent\n | 'unsupported' // no provider bound / entry point absent / unknown contract version\n | 'budget' // over an R-SAL-8 concurrency/rate limit\n | 'revoked' // a delegated mount was revoked during the create-and-bind window\n | 'cancelled' // the user dismissed the host launch affordance (into-stage only)\n | 'invalid-params' // a malformed `capDir`/`capFile` (`..`/absolute/mode-escalation)\n | 'unknown';\n\n/**\n * The control channel back to a launch — the ONLY thing a launcher gets (§2).\n * There is no typed return value (R-SAL-1); `status`/`onDismiss` are debounced so\n * they cannot time-distinguish a self-exit from a user dismiss.\n */\nexport interface LaunchHandle {\n /** Host-assigned id for this launch. */\n readonly launchId: string;\n /** The current lifecycle state (§2). */\n readonly status: LaunchStatus;\n /** Ask the host to tear this launch down. Idempotent (double-dismiss is a no-op). */\n dismiss(): void;\n /**\n * Observe the launch ending (self-exit / user dismiss / host revoke — fired\n * identically, R-SAL-1). Returns an unsubscribe fn. Fires at most once; if the\n * launch has already ended it fires on the next tick.\n */\n onDismiss(cb: () => void): () => void;\n}\n\ninterface LaunchEndedMessage {\n launchId: string;\n status: Exclude<LaunchStatus, 'running'>;\n}\n\n/** Live handles awaiting their terminal `launch-ended` message, keyed by launchId. */\nconst liveHandles = new Map<string, LaunchHandleImpl>();\n\n// The host delivers ONE `launch-ended` message per launch when it tears down —\n// the SAME message shape for self-exit, dismiss, and revoke (the host debounces\n// so the timing is not an oracle, §6.4). We fan it out to the matching handle.\naddListener(LAUNCH_ENDED, (m: LaunchEndedMessage) => {\n const h = liveHandles.get(m.launchId);\n if (h) h._end(m.status);\n});\n\nclass LaunchHandleImpl implements LaunchHandle {\n #status: LaunchStatus = 'running';\n #dismissListeners = new Set<() => void>();\n #ended = false;\n\n constructor(readonly launchId: string) {\n liveHandles.set(launchId, this);\n }\n\n get status(): LaunchStatus {\n return this.#status;\n }\n\n dismiss(): void {\n if (this.#ended) return;\n // Fire-and-forget: the host owns teardown and answers with `launch-ended`,\n // which drives `_end` (so status/onDismiss are host-authoritative, never\n // optimistically local — a dismiss the host refuses would otherwise desync).\n sendMessage(LAUNCH_DISMISS, { launchId: this.launchId });\n }\n\n onDismiss(cb: () => void): () => void {\n if (this.#ended) {\n // Already ended: fire on the next tick so the contract (\"returns an\n // unsubscribe\") holds and the callback never runs synchronously mid-register.\n queueMicrotask(cb);\n return () => {};\n }\n this.#dismissListeners.add(cb);\n return () => {\n this.#dismissListeners.delete(cb);\n };\n }\n\n /** Host-driven terminal transition — the only writer of `status`. Idempotent. */\n _end(status: Exclude<LaunchStatus, 'running'>): void {\n if (this.#ended) return;\n this.#ended = true;\n this.#status = status;\n liveHandles.delete(this.launchId);\n const listeners = [...this.#dismissListeners];\n this.#dismissListeners.clear();\n for (const l of listeners) {\n try {\n l();\n } catch {\n /* a launcher's own callback must never wedge teardown */\n }\n }\n }\n}\n\n/**\n * Launch a bound program to RUN in a region (§2). Non-blocking: resolves once the\n * frame is created and bound, with a {@link LaunchHandle} — or a typed\n * `{ ok:false, code }` on refusal (§8), NEVER a throw for an ordinary refusal (so\n * a launcher branches on `code` without a try/catch). The launched app runs under\n * its OWN grants; the launcher's authority does not flow to it (R-SAL-4).\n *\n * const h = await launch({ task: 'open-project' }, {\n * region: 'stage',\n * input: { dir: capDir({ mountId: 'space:abc', relPath: 'proj' }, { mode: 'ro' }) },\n * });\n * if ('ok' in h && h.ok === false) { ...handle h.code... }\n * else { h.onDismiss(() => ...); }\n */\nexport const launch = async (\n target: LaunchTarget,\n opts: LaunchOptions,\n): Promise<LaunchHandle | { ok: false; code: LaunchErrorCode }> => {\n // The host wraps a successful handler return as `{ ok:true, data }` (the same\n // Recipe-B framing `invokeTask` uses); a refusal is `{ ok:false, code }`.\n const res = (await protocolRequest(SCHEMES[PROTOCOL_LAUNCH], 'create', [{ target, opts }])) as\n | { ok: true; data: { launchId: string } }\n | { ok: false; code?: LaunchErrorCode }\n | undefined;\n if (!res || res.ok !== true || !res.data?.launchId) {\n const code = res && res.ok === false ?
|
|
1
|
+
{"version":3,"sources":["../src/launch.ts"],"sourcesContent":["// Launch — the TO-RUN twin of `invokeTask` (STANDING_APP_LIFECYCLE_SPEC §1–§6).\n// Where `invokeTask` is FOR-RESULT (await one value, callee exits), `launch` is\n// TO-RUN (non-blocking, standing): it starts a bound program RUNNING in a region\n// and hands back a HANDLE, never a typed value (R-SAL-1). The launched app earns\n// its OWN grants — the only launcher→launched data path is the explicitly\n// delegated `capDir`/`capFile` in `input` (§5), attenuated + tainted + gated host-\n// side. The host owns the whole lifecycle (visibility, budget, revocation,\n// teardown); the launcher only OBSERVES via the handle.\n//\n// A caller that wants a typed value back uses `invokeTask` instead — the two are\n// siblings, not a replacement.\nimport { protocolRequest, sendMessage, addListener } from './sandboxUtils';\nimport { LAUNCH_DISMISS, LAUNCH_ENDED, PROTOCOL_LAUNCH } from './generated/protocol';\nimport { SCHEMES } from './protocolSchemes';\n\n/** Where a launched program runs (§6). `overlay` covers the caller's own region\n * with opaque host chrome; `stage` replaces the focal app (the elevated into-\n * stage surface, §7 — refused above the stage-principal ceiling). */\nexport type LaunchRegion = 'overlay' | 'stage';\n\n/**\n * What to launch (§3) — binding-resolved, NEVER a caller-named app. Exactly one of:\n * - `entryPoint`: a sibling entry point of the caller's OWN repo (the mini-app\n * case, `AGENT_AUTHORING §5`) — **rejected `forbidden` until program-identity\n * `appKey` lands** (R-SAL-2a);\n * - `task`: a task contract (`open-project`, …), resolved through the user-\n * overridable `task.<name>` binding to whichever app the user bound (§3 kind 2).\n */\nexport interface LaunchTarget {\n /** Kind 1 — a sibling entry point of the caller's own repo (mini-app overlay). */\n entryPoint?: string;\n /** Kind 2 — a task contract name (the host resolves the bound provider). */\n task?: string;\n /** Optional accepted contract version for a `task` target (semver, e.g. `^1`). */\n version?: string;\n}\n\nexport interface LaunchOptions {\n /** Where it runs (§6). */\n region: LaunchRegion;\n /**\n * Delegations + plain data handed to the launched app. `$cap:'dir'|'file'`\n * markers (`capDir`/`capFile`, exported from `./tasks`) are resolved against the\n * launcher's OWN grants and minted as attenuated, `ro`-by-default chroots (§5) —\n * you can only delegate what you already hold. Everything else is plain data.\n */\n input?: Record<string, unknown>;\n}\n\n/** The live state of a launch (§2). Terminal states (`dismissed`/`revoked`/\n * `failed`) are reached identically for self-exit, user dismiss, and host revoke —\n * the host debounces so the launcher gets no timing oracle (R-SAL-1 / §6.4). */\nexport type LaunchStatus = 'running' | 'dismissed' | 'revoked' | 'failed';\n\n/** Machine codes a refused `launch` resolves with (§8). */\nexport type LaunchErrorCode =\n | 'forbidden' // undeclared target; sibling (pre-AA-01); self-resolving target; stage cap absent\n | 'unsupported' // no provider bound / entry point absent / unknown contract version\n | 'budget' // over an R-SAL-8 concurrency/rate limit\n | 'revoked' // a delegated mount was revoked during the create-and-bind window\n | 'cancelled' // the user dismissed the host launch affordance (into-stage only)\n | 'invalid-params' // a malformed `capDir`/`capFile` (`..`/absolute/mode-escalation)\n | 'unknown';\n\n/**\n * The control channel back to a launch — the ONLY thing a launcher gets (§2).\n * There is no typed return value (R-SAL-1); `status`/`onDismiss` are debounced so\n * they cannot time-distinguish a self-exit from a user dismiss.\n */\nexport interface LaunchHandle {\n /** Host-assigned id for this launch. */\n readonly launchId: string;\n /** The current lifecycle state (§2). */\n readonly status: LaunchStatus;\n /** Ask the host to tear this launch down. Idempotent (double-dismiss is a no-op). */\n dismiss(): void;\n /**\n * Observe the launch ending (self-exit / user dismiss / host revoke — fired\n * identically, R-SAL-1). Returns an unsubscribe fn. Fires at most once; if the\n * launch has already ended it fires on the next tick.\n */\n onDismiss(cb: () => void): () => void;\n}\n\ninterface LaunchEndedMessage {\n launchId: string;\n status: Exclude<LaunchStatus, 'running'>;\n}\n\n/** Live handles awaiting their terminal `launch-ended` message, keyed by launchId. */\nconst liveHandles = new Map<string, LaunchHandleImpl>();\n\n// The host delivers ONE `launch-ended` message per launch when it tears down —\n// the SAME message shape for self-exit, dismiss, and revoke (the host debounces\n// so the timing is not an oracle, §6.4). We fan it out to the matching handle.\naddListener(LAUNCH_ENDED, (m: LaunchEndedMessage) => {\n const h = liveHandles.get(m.launchId);\n if (h) h._end(m.status);\n});\n\nclass LaunchHandleImpl implements LaunchHandle {\n #status: LaunchStatus = 'running';\n #dismissListeners = new Set<() => void>();\n #ended = false;\n\n constructor(readonly launchId: string) {\n liveHandles.set(launchId, this);\n }\n\n get status(): LaunchStatus {\n return this.#status;\n }\n\n dismiss(): void {\n if (this.#ended) return;\n // Fire-and-forget: the host owns teardown and answers with `launch-ended`,\n // which drives `_end` (so status/onDismiss are host-authoritative, never\n // optimistically local — a dismiss the host refuses would otherwise desync).\n sendMessage(LAUNCH_DISMISS, { launchId: this.launchId });\n }\n\n onDismiss(cb: () => void): () => void {\n if (this.#ended) {\n // Already ended: fire on the next tick so the contract (\"returns an\n // unsubscribe\") holds and the callback never runs synchronously mid-register.\n queueMicrotask(cb);\n return () => {};\n }\n this.#dismissListeners.add(cb);\n return () => {\n this.#dismissListeners.delete(cb);\n };\n }\n\n /** Host-driven terminal transition — the only writer of `status`. Idempotent. */\n _end(status: Exclude<LaunchStatus, 'running'>): void {\n if (this.#ended) return;\n this.#ended = true;\n this.#status = status;\n liveHandles.delete(this.launchId);\n const listeners = [...this.#dismissListeners];\n this.#dismissListeners.clear();\n for (const l of listeners) {\n try {\n l();\n } catch {\n /* a launcher's own callback must never wedge teardown */\n }\n }\n }\n}\n\n/**\n * Launch a bound program to RUN in a region (§2). Non-blocking: resolves once the\n * frame is created and bound, with a {@link LaunchHandle} — or a typed\n * `{ ok:false, code }` on refusal (§8), NEVER a throw for an ordinary refusal (so\n * a launcher branches on `code` without a try/catch). The launched app runs under\n * its OWN grants; the launcher's authority does not flow to it (R-SAL-4).\n *\n * const h = await launch({ task: 'open-project' }, {\n * region: 'stage',\n * input: { dir: capDir({ mountId: 'space:abc', relPath: 'proj' }, { mode: 'ro' }) },\n * });\n * if ('ok' in h && h.ok === false) { ...handle h.code... }\n * else { h.onDismiss(() => ...); }\n */\nexport const launch = async (\n target: LaunchTarget,\n opts: LaunchOptions,\n): Promise<LaunchHandle | { ok: false; code: LaunchErrorCode }> => {\n // The host wraps a successful handler return as `{ ok:true, data }` (the same\n // Recipe-B framing `invokeTask` uses); a refusal is `{ ok:false, code }`.\n const res = (await protocolRequest(SCHEMES[PROTOCOL_LAUNCH], 'create', [{ target, opts }])) as\n | { ok: true; data: { launchId: string } }\n | { ok: false; code?: LaunchErrorCode }\n | undefined;\n if (!res || res.ok !== true || !res.data?.launchId) {\n const code = res && res.ok === false ? res.code ?? 'unknown' : 'unknown';\n return { ok: false, code };\n }\n return new LaunchHandleImpl(res.data.launchId);\n};\n"],"mappings":";;;;;AAAA;AAWA,SAAS,iBAAiB,aAAa,mBAAmB;AAC1D,SAAS,gBAAgB,cAAc,uBAAuB;AAC9D,SAAS,eAAe;AA6ExB,MAAM,cAAc,oBAAI,IAA8B;AAKtD,YAAY,cAAc,CAAC,MAA0B;AACnD,QAAM,IAAI,YAAY,IAAI,EAAE,QAAQ;AACpC,MAAI,EAAG,GAAE,KAAK,EAAE,MAAM;AACxB,CAAC;AAED,MAAM,iBAAyC;AAAA,EAK7C,YAAqB,UAAkB;AAAlB;AAJrB,gCAAwB;AACxB,0CAAoB,oBAAI,IAAgB;AACxC,+BAAS;AAGP,gBAAY,IAAI,UAAU,IAAI;AAAA,EAChC;AAAA,EAEA,IAAI,SAAuB;AACzB,WAAO,mBAAK;AAAA,EACd;AAAA,EAEA,UAAgB;AACd,QAAI,mBAAK,QAAQ;AAIjB,gBAAY,gBAAgB,EAAE,UAAU,KAAK,SAAS,CAAC;AAAA,EACzD;AAAA,EAEA,UAAU,IAA4B;AACpC,QAAI,mBAAK,SAAQ;AAGf,qBAAe,EAAE;AACjB,aAAO,MAAM;AAAA,MAAC;AAAA,IAChB;AACA,uBAAK,mBAAkB,IAAI,EAAE;AAC7B,WAAO,MAAM;AACX,yBAAK,mBAAkB,OAAO,EAAE;AAAA,IAClC;AAAA,EACF;AAAA;AAAA,EAGA,KAAK,QAAgD;AACnD,QAAI,mBAAK,QAAQ;AACjB,uBAAK,QAAS;AACd,uBAAK,SAAU;AACf,gBAAY,OAAO,KAAK,QAAQ;AAChC,UAAM,YAAY,CAAC,GAAG,mBAAK,kBAAiB;AAC5C,uBAAK,mBAAkB,MAAM;AAC7B,eAAW,KAAK,WAAW;AACzB,UAAI;AACF,UAAE;AAAA,MACJ,QAAQ;AAAA,MAER;AAAA,IACF;AAAA,EACF;AACF;AAjDE;AACA;AACA;AA+DK,MAAM,SAAS,OACpB,QACA,SACiE;AAGjE,QAAM,MAAO,MAAM,gBAAgB,QAAQ,eAAe,GAAG,UAAU,CAAC,EAAE,QAAQ,KAAK,CAAC,CAAC;AAIzF,MAAI,CAAC,OAAO,IAAI,OAAO,QAAQ,CAAC,IAAI,MAAM,UAAU;AAClD,UAAM,OAAO,OAAO,IAAI,OAAO,QAAQ,IAAI,QAAQ,YAAY;AAC/D,WAAO,EAAE,IAAI,OAAO,KAAK;AAAA,EAC3B;AACA,SAAO,IAAI,iBAAiB,IAAI,KAAK,QAAQ;AAC/C;","names":[]}
|
package/dist/linkSpace.cjs
CHANGED
|
@@ -26,7 +26,10 @@ __export(linkSpace_exports, {
|
|
|
26
26
|
module.exports = __toCommonJS(linkSpace_exports);
|
|
27
27
|
var import_react = require("react");
|
|
28
28
|
const FS_PREFIX = "$fs:";
|
|
29
|
-
const LinkSpaceContext = (0, import_react.createContext)({
|
|
29
|
+
const LinkSpaceContext = (0, import_react.createContext)({
|
|
30
|
+
corpusRoot: null,
|
|
31
|
+
bundleChrooted: false
|
|
32
|
+
});
|
|
30
33
|
const normalizeAbsolute = (path) => {
|
|
31
34
|
const out = [];
|
|
32
35
|
for (const seg of path.split("/")) {
|
|
@@ -36,16 +39,22 @@ const normalizeAbsolute = (path) => {
|
|
|
36
39
|
}
|
|
37
40
|
return "/" + out.join("/");
|
|
38
41
|
};
|
|
42
|
+
function resolveCorpusAbsolute(path, corpusRoot) {
|
|
43
|
+
const inner = normalizeAbsolute(path);
|
|
44
|
+
if (corpusRoot === null || corpusRoot === "/") return { state: "resolved", path: inner };
|
|
45
|
+
return { state: "resolved", path: normalizeAbsolute(corpusRoot + inner) };
|
|
46
|
+
}
|
|
39
47
|
function resolveLinkTarget(raw, opts = {}) {
|
|
40
48
|
if (raw.startsWith(FS_PREFIX)) {
|
|
41
49
|
const rest = raw.slice(FS_PREFIX.length);
|
|
42
50
|
if (!rest.startsWith("/")) return { state: "invalid" };
|
|
51
|
+
if (opts.bundleChrooted) return resolveCorpusAbsolute(rest, opts.corpusRoot ?? null);
|
|
43
52
|
return { state: "resolved", path: normalizeAbsolute(rest) };
|
|
44
53
|
}
|
|
45
54
|
if (raw.startsWith("/")) {
|
|
46
55
|
const corpusRoot = opts.corpusRoot ?? null;
|
|
47
56
|
if (corpusRoot !== null) {
|
|
48
|
-
return
|
|
57
|
+
return resolveCorpusAbsolute(raw, corpusRoot);
|
|
49
58
|
}
|
|
50
59
|
return { state: "resolved", path: normalizeAbsolute(raw) };
|
|
51
60
|
}
|
package/dist/linkSpace.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/linkSpace.ts"],"sourcesContent":["// Link path spaces (R3-273; REPO_CONTENT_DISPATCH_SPEC §9 decision, 2026-08-17).\n//\n// A document link's target resolves in one of two spaces:\n//\n// - DEFAULT — the enclosing corpus's virtual filesystem. RELATIVE targets resolve\n// against the authoring file (identical in both spaces); ABSOLUTE targets\n// (`/x/y.mdx`) resolve from the corpus root when an enclosing `LinkSpaceContext`\n// declares one, else from the filesystem root. A non-corpus app declares nothing\n// and keeps today's behavior bit-for-bit (its fs root IS its only root).\n//\n// - `$fs:` — the explicit filesystem space: `$fs:/content/x.mdx` resolves from the\n// root of the filesystem the app reads, escaping corpus-relative addressing.\n//\n// `$fs:` changes ADDRESSING, never REACH: resolution is pure path arithmetic and\n// existence is checked against the same in-mount metadata the default space uses —\n// nothing is fetched, and a link can never name what the app cannot already read.\n// A malformed `$fs:` target (anything not mount-absolute — which also catches\n// scheme smuggling like `$fs:javascript:…`) is INVALID and must render as a broken\n// link, never an anchor.\n//\n// Corpus nesting: `LinkSpaceContext` providers nest, and the NEAREST one wins —\n// which is exactly the innermost-enclosing-corpus rule (bundle encapsulation): a\n// document rendered inside a nested corpus resolves against the nested corpus.\n\nimport { createContext } from 'react';\n\nexport const FS_PREFIX = '$fs:';\n\nexport interface LinkSpace {\n /** Absolute filesystem path of the enclosing corpus's root (e.g. `/app/content`),\n * or `null` when the document is not corpus-hosted (default). */\n corpusRoot: string | null;\n}\n\n/** Ambient link space. A corpus-rendering app wraps its document tree in\n * `<LinkSpaceContext value={{ corpusRoot }}>`; nesting a second provider inside a\n * rendered sub-corpus makes the innermost root win. */\nexport const LinkSpaceContext = createContext<LinkSpace>({
|
|
1
|
+
{"version":3,"sources":["../src/linkSpace.ts"],"sourcesContent":["// Link path spaces (R3-273; REPO_CONTENT_DISPATCH_SPEC §9 decision, 2026-08-17).\n//\n// A document link's target resolves in one of two spaces:\n//\n// - DEFAULT — the enclosing corpus's virtual filesystem. RELATIVE targets resolve\n// against the authoring file (identical in both spaces); ABSOLUTE targets\n// (`/x/y.mdx`) resolve from the corpus root when an enclosing `LinkSpaceContext`\n// declares one, else from the filesystem root. A non-corpus app declares nothing\n// and keeps today's behavior bit-for-bit (its fs root IS its only root).\n//\n// - `$fs:` — the explicit filesystem space: `$fs:/content/x.mdx` resolves from the\n// root of the filesystem the app reads, escaping corpus-relative addressing.\n//\n// `$fs:` changes ADDRESSING, never REACH: resolution is pure path arithmetic and\n// existence is checked against the same in-mount metadata the default space uses —\n// nothing is fetched, and a link can never name what the app cannot already read.\n// A malformed `$fs:` target (anything not mount-absolute — which also catches\n// scheme smuggling like `$fs:javascript:…`) is INVALID and must render as a broken\n// link, never an anchor.\n//\n// Corpus nesting: `LinkSpaceContext` providers nest, and the NEAREST one wins —\n// which is exactly the innermost-enclosing-corpus rule (bundle encapsulation): a\n// document rendered inside a nested corpus resolves against the nested corpus.\n\nimport { createContext } from 'react';\n\nexport const FS_PREFIX = '$fs:';\n\nexport interface LinkSpace {\n /** Absolute filesystem path of the enclosing corpus's root (e.g. `/app/content`),\n * or `null` when the document is not corpus-hosted (default). */\n corpusRoot: string | null;\n /**\n * True when the filesystem this document resolves against is **chroot'd to the\n * bundle** — i.e. the port the app holds was scoped to the bundle's subtree, so\n * the mount root and the bundle root are the same directory\n * (`BUNDLE_LAYERS_SPEC §9`; the `T2`/`T4` wrapper, R3-319 / BL-2).\n *\n * Under that grant `$fs:` **collapses to the scoped root**: `$fs:/p` and `/p`\n * name the same byte, because there is no longer any \"mount-absolute\" space\n * outside the bundle for `$fs:` to reach into. Without this flag the resolver\n * would hand back a mount-absolute path that the chroot then re-roots anyway —\n * a link that renders as valid and resolves somewhere the author did not mean.\n *\n * **This is an invariant to CREATE, not one to inherit** (`BUNDLE_LAYERS_SPEC\n * §11`): the shipped resolver reads `{currentFile, corpusRoot}` and nothing\n * else, so `$fs:` is bundle-anchored only if something says so. It lives here,\n * in the resolver, rather than as a rule each caller applies by passing\n * `corpusRoot: '/'` — an invariant the arithmetic carries cannot be forgotten\n * at one call site out of five.\n */\n bundleChrooted?: boolean;\n}\n\n/** Ambient link space. A corpus-rendering app wraps its document tree in\n * `<LinkSpaceContext value={{ corpusRoot }}>`; nesting a second provider inside a\n * rendered sub-corpus makes the innermost root win. */\nexport const LinkSpaceContext = createContext<LinkSpace>({\n corpusRoot: null,\n bundleChrooted: false,\n});\n\n/** Collapse `.`/`..`/empty segments into a clean absolute path. `..` can never\n * climb above the root — a (virtual) root's parent is itself, which is what keeps\n * both the mount space and the corpus space closed under traversal. */\nexport const normalizeAbsolute = (path: string): string => {\n const out: string[] = [];\n for (const seg of path.split('/')) {\n if (seg === '' || seg === '.') continue;\n if (seg === '..') out.pop();\n else out.push(seg);\n }\n return '/' + out.join('/');\n};\n\nexport type ResolvedLinkTarget =\n /** Resolved to an absolute filesystem path (existence NOT checked here). */\n | { state: 'resolved'; path: string }\n /** A relative target with no known authoring file — the caller may route\n * optimistically (it cannot check existence or self-ness generically). */\n | { state: 'unresolvable' }\n /** A malformed `$fs:` target (not mount-absolute; includes scheme smuggling).\n * Callers MUST render this broken/inert — never as an anchor. */\n | { state: 'invalid' };\n\n/** Anchor a corpus-absolute path at `corpusRoot`, clamping the corpus-relative\n * half FIRST so the virtual corpus space stays closed under traversal. Shared by\n * the `/p` branch and — under a bundle chroot — the `$fs:/p` branch, so the two\n * spellings cannot drift into resolving differently. */\nfunction resolveCorpusAbsolute(path: string, corpusRoot: string | null): ResolvedLinkTarget {\n const inner = normalizeAbsolute(path);\n if (corpusRoot === null || corpusRoot === '/') return { state: 'resolved', path: inner };\n return { state: 'resolved', path: normalizeAbsolute(corpusRoot + inner) };\n}\n\n/**\n * Resolve a raw link target (a wikilink target or an in-app href's path half —\n * fragment already split off) to an absolute filesystem path. THE shared resolver:\n * the default `WikiLink`, the markdown `a` override, and safe-content consumers\n * all route through this one function so the two render pipelines cannot drift.\n */\nexport function resolveLinkTarget(\n raw: string,\n opts: {\n currentFile?: string;\n corpusRoot?: string | null;\n /** See `LinkSpace.bundleChrooted`. Under a bundle-chroot'd grant `$fs:`\n * resolves in the corpus space, because they are the same space. */\n bundleChrooted?: boolean;\n } = {},\n): ResolvedLinkTarget {\n if (raw.startsWith(FS_PREFIX)) {\n const rest = raw.slice(FS_PREFIX.length);\n // Must be mount-absolute. This single rule also fails `$fs:javascript:…`,\n // `$fs:https://…`, and every other smuggled scheme closed.\n if (!rest.startsWith('/')) return { state: 'invalid' };\n // Under a bundle chroot the mount root IS the bundle root, so `$fs:` has\n // nowhere outside to name: it takes the corpus-absolute branch below and the\n // two spellings collapse. `normalizeAbsolute` clamps `..` at the root either\n // way, so neither spelling can climb out — the collapse changes WHERE a\n // `$fs:` link points, never whether it can escape.\n if (opts.bundleChrooted) return resolveCorpusAbsolute(rest, opts.corpusRoot ?? null);\n return { state: 'resolved', path: normalizeAbsolute(rest) };\n }\n if (raw.startsWith('/')) {\n const corpusRoot = opts.corpusRoot ?? null;\n if (corpusRoot !== null) {\n // Clamp the corpus-relative half FIRST (the virtual FS is closed — `/../x`\n // stays inside the corpus), THEN anchor it at the corpus root.\n return resolveCorpusAbsolute(raw, corpusRoot);\n }\n return { state: 'resolved', path: normalizeAbsolute(raw) };\n }\n // Relative: against the authoring file's directory — the same in both spaces.\n if (!opts.currentFile) return { state: 'unresolvable' };\n const dir = opts.currentFile.slice(0, opts.currentFile.lastIndexOf('/'));\n return { state: 'resolved', path: normalizeAbsolute(`${dir}/${raw}`) };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwBA,mBAA8B;AAEvB,MAAM,YAAY;AA+BlB,MAAM,uBAAmB,4BAAyB;AAAA,EACvD,YAAY;AAAA,EACZ,gBAAgB;AAClB,CAAC;AAKM,MAAM,oBAAoB,CAAC,SAAyB;AACzD,QAAM,MAAgB,CAAC;AACvB,aAAW,OAAO,KAAK,MAAM,GAAG,GAAG;AACjC,QAAI,QAAQ,MAAM,QAAQ,IAAK;AAC/B,QAAI,QAAQ,KAAM,KAAI,IAAI;AAAA,QACrB,KAAI,KAAK,GAAG;AAAA,EACnB;AACA,SAAO,MAAM,IAAI,KAAK,GAAG;AAC3B;AAgBA,SAAS,sBAAsB,MAAc,YAA+C;AAC1F,QAAM,QAAQ,kBAAkB,IAAI;AACpC,MAAI,eAAe,QAAQ,eAAe,IAAK,QAAO,EAAE,OAAO,YAAY,MAAM,MAAM;AACvF,SAAO,EAAE,OAAO,YAAY,MAAM,kBAAkB,aAAa,KAAK,EAAE;AAC1E;AAQO,SAAS,kBACd,KACA,OAMI,CAAC,GACe;AACpB,MAAI,IAAI,WAAW,SAAS,GAAG;AAC7B,UAAM,OAAO,IAAI,MAAM,UAAU,MAAM;AAGvC,QAAI,CAAC,KAAK,WAAW,GAAG,EAAG,QAAO,EAAE,OAAO,UAAU;AAMrD,QAAI,KAAK,eAAgB,QAAO,sBAAsB,MAAM,KAAK,cAAc,IAAI;AACnF,WAAO,EAAE,OAAO,YAAY,MAAM,kBAAkB,IAAI,EAAE;AAAA,EAC5D;AACA,MAAI,IAAI,WAAW,GAAG,GAAG;AACvB,UAAM,aAAa,KAAK,cAAc;AACtC,QAAI,eAAe,MAAM;AAGvB,aAAO,sBAAsB,KAAK,UAAU;AAAA,IAC9C;AACA,WAAO,EAAE,OAAO,YAAY,MAAM,kBAAkB,GAAG,EAAE;AAAA,EAC3D;AAEA,MAAI,CAAC,KAAK,YAAa,QAAO,EAAE,OAAO,eAAe;AACtD,QAAM,MAAM,KAAK,YAAY,MAAM,GAAG,KAAK,YAAY,YAAY,GAAG,CAAC;AACvE,SAAO,EAAE,OAAO,YAAY,MAAM,kBAAkB,GAAG,GAAG,IAAI,GAAG,EAAE,EAAE;AACvE;","names":[]}
|