@lincyaw/kernel 0.0.0-stage → 1.0.0-rc.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +32 -2
- package/package.json +41 -3
- package/src/access.ts +18 -0
- package/src/anchors.ts +58 -0
- package/src/authoring.ts +56 -0
- package/src/blocks.ts +19 -0
- package/src/checks.ts +35 -0
- package/src/component-code.ts +6 -0
- package/src/contracts/agent.ts +76 -0
- package/src/contracts/anchors.ts +46 -0
- package/src/contracts/authoring-actions.ts +60 -0
- package/src/contracts/authoring-clients.ts +213 -0
- package/src/contracts/authoring.ts +180 -0
- package/src/contracts/checks.ts +29 -0
- package/src/contracts/classroom.ts +49 -0
- package/src/contracts/diagnostics.ts +37 -0
- package/src/contracts/document-audio.ts +150 -0
- package/src/contracts/document-comments.ts +31 -0
- package/src/contracts/document-history.ts +63 -0
- package/src/contracts/document-image.ts +32 -0
- package/src/contracts/document-reads.ts +57 -0
- package/src/contracts/document-voice.ts +44 -0
- package/src/contracts/document.ts +240 -0
- package/src/contracts/execution.ts +118 -0
- package/src/contracts/locale.ts +3 -0
- package/src/contracts/offline.ts +2 -0
- package/src/contracts/operations.ts +8 -0
- package/src/contracts/plugin.ts +183 -0
- package/src/contracts/presenter-panels.ts +17 -0
- package/src/contracts/preview.ts +386 -0
- package/src/contracts/production-assets.ts +61 -0
- package/src/contracts/production.ts +109 -0
- package/src/contracts/projects.ts +59 -0
- package/src/contracts/server.ts +108 -0
- package/src/contracts/service.ts +8 -0
- package/src/contracts/speech-settings.ts +16 -0
- package/src/contracts/speech.ts +149 -0
- package/src/contracts/steps.ts +15 -0
- package/src/contracts/transcription-adoption.ts +79 -0
- package/src/contracts/transcription.ts +112 -0
- package/src/contracts/voice.ts +97 -0
- package/src/contracts/voiceover-execution.ts +68 -0
- package/src/contracts/workspace.ts +458 -0
- package/src/contracts.ts +35 -0
- package/src/diagnostics.ts +7 -0
- package/src/document-authoring-actions.ts +47 -0
- package/src/document-caption-timings.ts +64 -0
- package/src/document-colors.ts +25 -0
- package/src/document-comments.ts +118 -0
- package/src/document-components.ts +251 -0
- package/src/document-operations.ts +132 -0
- package/src/document-pages.ts +215 -0
- package/src/document-reads.ts +81 -0
- package/src/document-schema.ts +85 -0
- package/src/document-voices.ts +153 -0
- package/src/document.ts +252 -0
- package/src/durations.ts +7 -0
- package/src/errors.ts +15 -0
- package/src/events.ts +32 -0
- package/src/execution.ts +25 -0
- package/src/fingerprint.ts +31 -0
- package/src/follow.ts +33 -0
- package/src/git.ts +12 -0
- package/src/html.ts +41 -0
- package/src/index.ts +82 -0
- package/src/ir.ts +367 -0
- package/src/jupyter-kernel.ts +9 -0
- package/src/kernel.ts +190 -0
- package/src/keyboard.ts +43 -0
- package/src/locale.ts +13 -0
- package/src/lock.ts +81 -0
- package/src/logging.ts +11 -0
- package/src/lower-myst.ts +312 -0
- package/src/node/assets.ts +23 -0
- package/src/node/authoring-actions.ts +50 -0
- package/src/node/authoring-clients.ts +14 -0
- package/src/node/document-audio.ts +22 -0
- package/src/node/document-history.ts +11 -0
- package/src/node/document-image.ts +8 -0
- package/src/node/document-reads.ts +10 -0
- package/src/node/document-voice.ts +7 -0
- package/src/node/ffmpeg.ts +29 -0
- package/src/node/file-version.ts +6 -0
- package/src/node/fonts.ts +22 -0
- package/src/node/index.ts +29 -0
- package/src/node/installed-packages.ts +11 -0
- package/src/node/lines.ts +24 -0
- package/src/node/locale.ts +7 -0
- package/src/node/network.ts +11 -0
- package/src/node/operator-speech.ts +91 -0
- package/src/node/packages.ts +74 -0
- package/src/node/paths.ts +7 -0
- package/src/node/projects.ts +7 -0
- package/src/node/resources.ts +86 -0
- package/src/node/revision.ts +6 -0
- package/src/node/run-directory.ts +70 -0
- package/src/node/strip-types.mjs +15 -0
- package/src/node/transcription-adoption.ts +9 -0
- package/src/node/transcription.ts +10 -0
- package/src/node/voiceover-routes.ts +73 -0
- package/src/node/voiceover.ts +199 -0
- package/src/page-source.ts +359 -0
- package/src/parameters.ts +81 -0
- package/src/preview-edit.ts +18 -0
- package/src/preview-events.ts +57 -0
- package/src/preview-frame.ts +1220 -0
- package/src/props.ts +17 -0
- package/src/published.ts +11 -0
- package/src/push-stream.ts +67 -0
- package/src/reading.ts +96 -0
- package/src/recording.ts +38 -0
- package/src/renderer-components.ts +30 -0
- package/src/requests.ts +28 -0
- package/src/responses.ts +49 -0
- package/src/run-outputs.ts +15 -0
- package/src/runner.py +454 -0
- package/src/services.ts +146 -0
- package/src/slides.ts +36 -0
- package/src/speech-words.ts +76 -0
- package/src/speech.ts +13 -0
- package/src/step-keys.ts +38 -0
- package/src/steps.ts +7 -0
- package/src/transcription-adoption.ts +105 -0
- package/src/uri-decoding.ts +8 -0
- package/src/urls.ts +13 -0
- package/src/views.ts +47 -0
- package/src/voice.ts +86 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { DocumentCaptionTimingAction } from '../document-caption-timings.ts'
|
|
2
|
+
import type { DocumentCommentAction, DocumentCommentEffect } from './document-comments.ts'
|
|
3
|
+
import type { JsonValue, VoiceSource } from './document.ts'
|
|
4
|
+
import type { DocumentVoiceImpact, DocumentVoiceScope } from '../document-voices.ts'
|
|
5
|
+
import type { CanonicalSource } from '../ir.ts'
|
|
6
|
+
import type { DocumentChange, DocumentChangeAnswer, RevisionOrigin } from './workspace.ts'
|
|
7
|
+
import type { DocumentChangePlan } from './authoring.ts'
|
|
8
|
+
|
|
9
|
+
export const DOCUMENT_ACTION_MAX_REQUEST_CHARS = 4096
|
|
10
|
+
export const DOCUMENT_ACTION_MAX_RESPONSE_CHARS = 7168
|
|
11
|
+
export const DOCUMENT_AUTHORING_ACTION_TYPES = ['comment.create', 'comment.reply', 'comment.update', 'comment.react', 'comment.resolve', 'comment.reopen', 'comment.delete', 'voice.set', 'component.parameters.patch', 'component.step-parameters.patch', 'component.step-parameters.reset', 'component.definition.use', 'caption.timing.set', 'caption.timing.reset'] as const
|
|
12
|
+
export const DOCUMENT_PARAMETER_MAX_CHANGES = 32
|
|
13
|
+
export const DOCUMENT_PARAMETER_MAX_PATH_DEPTH = 16
|
|
14
|
+
/** Paths belong only to this component's parameters; set replaces an array item or appends at its length. */
|
|
15
|
+
export type DocumentParameterChange = { op: 'set', path: string[], value: JsonValue } | { op: 'remove', path: string[] }
|
|
16
|
+
export type DocumentComponentAction =
|
|
17
|
+
| { type: 'component.parameters.patch', blockId: string, changes: DocumentParameterChange[] }
|
|
18
|
+
| { type: 'component.step-parameters.patch', blockId: string, stepId: string, changes: DocumentParameterChange[] }
|
|
19
|
+
| { type: 'component.step-parameters.reset', blockId: string, stepId: string }
|
|
20
|
+
| { type: 'component.definition.use', blockId: string, definitionId: string, changes?: DocumentParameterChange[] }
|
|
21
|
+
export interface DocumentComponentEffect {
|
|
22
|
+
blockId: string
|
|
23
|
+
sectionId: string
|
|
24
|
+
layer: 'base' | 'step' | 'definition'
|
|
25
|
+
stepId?: string
|
|
26
|
+
definitionId: string
|
|
27
|
+
previousDefinitionId?: string
|
|
28
|
+
changed: boolean
|
|
29
|
+
paths: string[][]
|
|
30
|
+
affected: { blocks: string[], steps: string[], pages: string[] }
|
|
31
|
+
overrides: { preserved: string[], materialized: string[], reset: string[] }
|
|
32
|
+
references: { before: string[], after: string[] }
|
|
33
|
+
}
|
|
34
|
+
export const DOCUMENT_COMPONENT_IMPACT_COLLECTIONS = ['affected.blocks', 'affected.steps', 'affected.pages', 'overrides.preserved', 'overrides.materialized', 'overrides.reset', 'references.before', 'references.after'] as const
|
|
35
|
+
export type DocumentComponentImpactCollection = typeof DOCUMENT_COMPONENT_IMPACT_COLLECTIONS[number]
|
|
36
|
+
export interface DocumentComponentImpactQuery extends DocumentAuthoringActionRead { collection: DocumentComponentImpactCollection, offset?: number }
|
|
37
|
+
export interface DocumentComponentImpactAnswer {
|
|
38
|
+
planId: string, digest: string, source: CanonicalSource, currentSource: CanonicalSource, state: 'current' | 'applied' | 'old'
|
|
39
|
+
collection: DocumentComponentImpactCollection, offset: number, total: number, entries: string[], nextOffset?: number, complete: boolean
|
|
40
|
+
}
|
|
41
|
+
/** New recording adoption belongs to the real-media service, never a raw settings action. */
|
|
42
|
+
export type DocumentAuthoringAction = DocumentCommentAction | DocumentComponentAction | DocumentCaptionTimingAction | { type: 'voice.set', scope: DocumentVoiceScope, source: Exclude<VoiceSource, { type: 'recording' }> | null, replaceOverrides?: boolean }
|
|
43
|
+
export interface DocumentAuthoringActionRequest { id: string, source: CanonicalSource, action: DocumentAuthoringAction }
|
|
44
|
+
export type DocumentAuthoringActionEffect = { kind: 'caption', value: { stepId: string, segments: number, changed: boolean, mode: 'manual' | 'automatic' } } | { kind: 'comment', value: DocumentCommentEffect } | { kind: 'voice', scope: DocumentVoiceScope['kind'], value: DocumentVoiceImpact, recordings: { preserved: number, removed: number } } | { kind: 'component', value: DocumentComponentEffect }
|
|
45
|
+
export interface DocumentAuthoringActionPlan extends DocumentChangePlan {
|
|
46
|
+
action: DocumentAuthoringAction
|
|
47
|
+
effect: DocumentAuthoringActionEffect
|
|
48
|
+
change: DocumentChange
|
|
49
|
+
}
|
|
50
|
+
export interface DocumentAuthoringActionReference { planId: string, source: CanonicalSource, digest: string, confirmDeletion?: true }
|
|
51
|
+
export interface DocumentAuthoringActionRead { planId: string, digest: string }
|
|
52
|
+
export interface DocumentAuthoringActionInspection { plan: DocumentAuthoringActionPlan, currentSource: CanonicalSource, state: 'current' | 'applied' | 'old' }
|
|
53
|
+
export interface AppliedDocumentAuthoringAction extends Omit<DocumentChangeAnswer, 'content' | 'issues' | 'commit'> {
|
|
54
|
+
commit?: { id: string, origin: RevisionOrigin['kind'] }
|
|
55
|
+
planId: string
|
|
56
|
+
digest: string
|
|
57
|
+
action: DocumentAuthoringAction['type']
|
|
58
|
+
effect: DocumentAuthoringActionEffect
|
|
59
|
+
verification: { saved: true, source: CanonicalSource, plannedVersion: string, actionStateVerified: true, compiled: 'not-requested', runtime: 'not-requested' }
|
|
60
|
+
}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import type { Schema } from '@cfworker/json-schema'
|
|
2
|
+
import type { CanonicalSource } from '../ir.ts'
|
|
3
|
+
import type { Member } from './server.ts'
|
|
4
|
+
import type { ProductionBinding, ProductionPlaybackState } from './production.ts'
|
|
5
|
+
import { PRODUCTION_PLAYBACK_RATES } from './production.ts'
|
|
6
|
+
import { DOCUMENT_PAGE_DUPLICATION_SCHEMA } from './authoring.ts'
|
|
7
|
+
|
|
8
|
+
export const AUTHORING_CLIENT_MAX_REQUEST_CHARS = 4096
|
|
9
|
+
export const AUTHORING_CLIENT_MAX_RESPONSE_CHARS = 7168
|
|
10
|
+
export const AUTHORING_CLIENT_MAX_CLIENTS = 16
|
|
11
|
+
export const AUTHORING_CLIENT_MAX_REQUESTS = 64
|
|
12
|
+
export const AUTHORING_CLIENT_REQUEST_TIMEOUT_MS = 30000
|
|
13
|
+
export const AUTHORING_CLIENT_ACK_MAX_BYTES = 32768
|
|
14
|
+
export const AUTHORING_CLIENT_EVENT_MAX_BYTES = 8 << 10
|
|
15
|
+
export const AUTHORING_CLIENT_ACTIONS = ['state.read', 'source.flush', 'preview.prepare', 'comment-drafts.inspect', 'comment-drafts.read', 'comment-drafts.restore', 'comment-drafts.discard', 'component-drafts.inspect', 'component-drafts.read', 'component-drafts.restore', 'component-drafts.discard', 'playback.pause', 'position.step', 'playback.play', 'playback.seek', 'playback.rate'] as const
|
|
16
|
+
export type AuthoringClientActionType = typeof AUTHORING_CLIENT_ACTIONS[number]
|
|
17
|
+
export type AuthoringClientAction =
|
|
18
|
+
| { type: 'state.read' }
|
|
19
|
+
| { type: 'source.flush' }
|
|
20
|
+
| { type: 'preview.prepare' }
|
|
21
|
+
| { type: 'comment-drafts.inspect', cursor?: { after: string, token: string }, limit?: number }
|
|
22
|
+
| { type: 'comment-drafts.read', draft: ClientCommentDraftReference, offset: number, maxChars?: number }
|
|
23
|
+
| { type: 'comment-drafts.restore', draft: ClientCommentDraftReference, composerId: string, expectedCurrent: ClientCommentDraftReference | null }
|
|
24
|
+
| { type: 'comment-drafts.discard', draft: ClientCommentDraftReference }
|
|
25
|
+
| { type: 'component-drafts.inspect', cursor?: { after: string, token: string }, limit?: number }
|
|
26
|
+
| { type: 'component-drafts.read', draft: ClientComponentDraftReference, offset: number, maxChars?: number }
|
|
27
|
+
| { type: 'component-drafts.restore', draft: ClientComponentDraftReference, expectedCurrent: ClientComponentDraftReference | null }
|
|
28
|
+
| { type: 'component-drafts.discard', draft: ClientComponentDraftReference, current: boolean }
|
|
29
|
+
| { type: 'playback.pause' }
|
|
30
|
+
| { type: 'position.step', sectionId: string, stepId: string }
|
|
31
|
+
| { type: 'playback.play', binding: ProductionBinding }
|
|
32
|
+
| { type: 'playback.seek', binding: ProductionBinding, timeMs: number }
|
|
33
|
+
| { type: 'playback.rate', binding: ProductionBinding, rate: number }
|
|
34
|
+
|
|
35
|
+
export interface ClientCommentDraftReference { id: string, revision: number, token: string }
|
|
36
|
+
export interface ClientComponentDraftReference { id: string, revision: number, token: string, blockId: string, stepId?: string }
|
|
37
|
+
/** No parameter values, baselines, workspace identity or member IDs enter this metadata. */
|
|
38
|
+
export interface ClientComponentDraftMetadata {
|
|
39
|
+
draft: ClientComponentDraftReference
|
|
40
|
+
definitionId: string
|
|
41
|
+
current: boolean
|
|
42
|
+
storage: 'durable' | 'session'
|
|
43
|
+
status: 'available' | 'pending' | 'session-only'
|
|
44
|
+
updatedAt: number
|
|
45
|
+
baseVersion: string
|
|
46
|
+
sourceCurrent: boolean
|
|
47
|
+
composing: boolean
|
|
48
|
+
reset: boolean
|
|
49
|
+
}
|
|
50
|
+
/** Explicit inspection deliberately excludes content, source signatures, comment baselines and pending values. */
|
|
51
|
+
export interface ClientCommentDraftMetadata {
|
|
52
|
+
draft: ClientCommentDraftReference
|
|
53
|
+
kind: 'create' | 'reply' | 'edit'
|
|
54
|
+
storage: 'durable' | 'session'
|
|
55
|
+
updatedAt: number
|
|
56
|
+
baseVersion: string
|
|
57
|
+
composing: boolean
|
|
58
|
+
pendingSubmission: boolean
|
|
59
|
+
ownersKnown: boolean
|
|
60
|
+
active: boolean
|
|
61
|
+
discardable: boolean
|
|
62
|
+
restore?: { composerId: string, expectedCurrent: ClientCommentDraftReference | null }
|
|
63
|
+
threadId?: string
|
|
64
|
+
commentId?: string
|
|
65
|
+
}
|
|
66
|
+
export type AuthoringClientActionResult =
|
|
67
|
+
| { type: 'comment-drafts.inspect', family: 'comment', coverage: 'stored-and-session' | 'session-only', skippedInvalid: number, token: string, drafts: ClientCommentDraftMetadata[], next?: { after: string, token: string } }
|
|
68
|
+
| { type: 'comment-drafts.read', family: 'comment', draft: ClientCommentDraftReference, storage: 'durable' | 'session', format: 'presmium-rich-text-json-v1', offset: number, endOffset: number, totalUtf16: number, totalUtf8: number, sha256: string, jsonText: string }
|
|
69
|
+
| { type: 'comment-drafts.restore', family: 'comment', draft: ClientCommentDraftReference, composerId: string, restored: ClientCommentDraftReference, preservedCurrent: ClientCommentDraftReference | null, targetState: 'current' | 'changed' }
|
|
70
|
+
| { type: 'comment-drafts.discard', family: 'comment', draft: ClientCommentDraftReference, discarded: true }
|
|
71
|
+
| { type: 'component-drafts.inspect', family: 'component', coverage: 'stored-and-session' | 'session-only', skippedInvalid: number, token: string, drafts: ClientComponentDraftMetadata[], next?: { after: string, token: string } }
|
|
72
|
+
| { type: 'component-drafts.read', family: 'component', draft: ClientComponentDraftReference, storage: 'durable' | 'session', format: 'presmium-component-parameters-json-v1', offset: number, endOffset: number, totalUtf16: number, totalUtf8: number, sha256: string, jsonText: string }
|
|
73
|
+
| { type: 'component-drafts.restore', family: 'component', draft: ClientComponentDraftReference, restored: ClientComponentDraftMetadata, preservedCurrent: ClientComponentDraftReference | null }
|
|
74
|
+
| { type: 'component-drafts.discard', family: 'component', draft: ClientComponentDraftReference, current: boolean, discarded: true }
|
|
75
|
+
|
|
76
|
+
/** Reports contain no editor text. Time is an observed sample, never a server-estimated playhead. */
|
|
77
|
+
export interface AuthoringClientSnapshot {
|
|
78
|
+
revision: number
|
|
79
|
+
source: CanonicalSource
|
|
80
|
+
view: 'document' | 'present'
|
|
81
|
+
editing: { dirty: boolean, saving: boolean, composing: boolean, conflicted: boolean, locked: boolean, loading: boolean }
|
|
82
|
+
interaction: string
|
|
83
|
+
position?: { sectionId: string, stepId?: string }
|
|
84
|
+
binding?: ProductionBinding
|
|
85
|
+
preview?: { status: 'unprepared' | 'preparing' | 'prepared' | 'unavailable', source?: CanonicalSource, irRevision?: string, projectRevision?: number }
|
|
86
|
+
playback?: ProductionPlaybackState
|
|
87
|
+
actions: AuthoringClientActionType[]
|
|
88
|
+
unsubmittedContent: 'not-inspected'
|
|
89
|
+
}
|
|
90
|
+
export interface AuthoringClientConnection { clientId: string, epoch: string, connectionId: string }
|
|
91
|
+
/** This credential belongs to the host-side transport; it is never returned by MCP client/state reads. */
|
|
92
|
+
export interface AuthoringClientLease extends AuthoringClientConnection { credential: string }
|
|
93
|
+
export interface AuthoringClientOpen { clientId: string, epoch: string, snapshot: AuthoringClientSnapshot }
|
|
94
|
+
export interface AuthoringClientDescriptor extends AuthoringClientConnection {
|
|
95
|
+
document: string
|
|
96
|
+
connected: boolean
|
|
97
|
+
observedAt: string
|
|
98
|
+
verification: 'reported-state'
|
|
99
|
+
snapshot: AuthoringClientSnapshot
|
|
100
|
+
}
|
|
101
|
+
export interface AuthoringClientList { document: string, clients: AuthoringClientDescriptor[], nextAfter?: string, unavailable: readonly string[] }
|
|
102
|
+
export interface AuthoringClientRequest {
|
|
103
|
+
clientId: string
|
|
104
|
+
epoch: string
|
|
105
|
+
requestId: string
|
|
106
|
+
source: CanonicalSource
|
|
107
|
+
expectedClientState: { connectionId: string, revision: number }
|
|
108
|
+
action: AuthoringClientAction
|
|
109
|
+
}
|
|
110
|
+
export type AuthoringClientRequestState = 'sent' | 'executing' | 'done' | 'refused' | 'failed' | 'expired'
|
|
111
|
+
export interface AuthoringClientRequestResult extends AuthoringClientConnection {
|
|
112
|
+
document: string
|
|
113
|
+
requestId: string
|
|
114
|
+
action: AuthoringClientActionType
|
|
115
|
+
source: CanonicalSource
|
|
116
|
+
state: AuthoringClientRequestState
|
|
117
|
+
admittedAt: string
|
|
118
|
+
expiresAt: string
|
|
119
|
+
observedAt?: string
|
|
120
|
+
snapshot?: AuthoringClientSnapshot
|
|
121
|
+
result?: AuthoringClientActionResult
|
|
122
|
+
error?: { code: string }
|
|
123
|
+
verification: 'not-acknowledged' | 'browser-acknowledged'
|
|
124
|
+
current: boolean
|
|
125
|
+
}
|
|
126
|
+
export type AuthoringClientEvent =
|
|
127
|
+
| { type: 'connected', connectionId: string }
|
|
128
|
+
| { type: 'request', request: AuthoringClientRequest, expiresAt: string }
|
|
129
|
+
| { type: 'cancel', requestId: string, connectionId: string }
|
|
130
|
+
export interface AuthoringClientReport { sequence: number, snapshot: AuthoringClientSnapshot }
|
|
131
|
+
export interface AuthoringClientAcknowledgement extends AuthoringClientReport {
|
|
132
|
+
requestId: string
|
|
133
|
+
state: 'executing' | 'done' | 'refused' | 'failed'
|
|
134
|
+
error?: { code: string }
|
|
135
|
+
result?: AuthoringClientActionResult
|
|
136
|
+
}
|
|
137
|
+
export interface AuthoringClients {
|
|
138
|
+
list(document: string, query: { clientId?: string, after?: string }, member: Member): Promise<AuthoringClientList>
|
|
139
|
+
request(document: string, request: AuthoringClientRequest, member: Member): Promise<AuthoringClientRequestResult>
|
|
140
|
+
get(document: string, query: { clientId: string, epoch: string, requestId: string }, member: Member): Promise<AuthoringClientRequestResult>
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/** Reserve the complete receipt and the host-only lease, rather than budgeting only the text fragment. */
|
|
144
|
+
export function authoringClientResultFits(context: { request: AuthoringClientRequest, connection: AuthoringClientConnection, document: string, snapshot: AuthoringClientSnapshot }, result: AuthoringClientActionResult): boolean {
|
|
145
|
+
const { request, document, snapshot } = context, time = '2000-01-01T00:00:00.000Z'
|
|
146
|
+
const connection = { clientId: context.connection.clientId, epoch: context.connection.epoch, connectionId: context.connection.connectionId }
|
|
147
|
+
const receipt: AuthoringClientRequestResult = { ...connection, document, requestId: request.requestId, action: request.action.type, source: request.source,
|
|
148
|
+
state: 'done', admittedAt: time, expiresAt: time, observedAt: time, snapshot, result, verification: 'browser-acknowledged', current: false }
|
|
149
|
+
const body = { connection: { ...connection, credential: '0'.repeat(100) }, report: { sequence: Number.MAX_SAFE_INTEGER, snapshot, result, requestId: request.requestId, state: 'done' } }
|
|
150
|
+
const wire = JSON.stringify(body)
|
|
151
|
+
const frame = JSON.stringify({ type: 'authoring-client-publish', id: '0'.repeat(80), connectionId: connection.connectionId, sequence: Number.MAX_SAFE_INTEGER, kind: 'ack', value: body.report })
|
|
152
|
+
// Clock samples can change without changing the client revision; recheck the actual snapshot before sending.
|
|
153
|
+
return JSON.stringify(receipt, null, 2).length <= AUTHORING_CLIENT_MAX_RESPONSE_CHARS - 128
|
|
154
|
+
&& wire.length <= AUTHORING_CLIENT_MAX_RESPONSE_CHARS - 128 && new TextEncoder().encode(wire).byteLength <= AUTHORING_CLIENT_ACK_MAX_BYTES
|
|
155
|
+
&& new TextEncoder().encode(frame).byteLength <= AUTHORING_CLIENT_EVENT_MAX_BYTES - 128
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const id: Schema = { type: 'string', minLength: 1, maxLength: 80, pattern: '^[a-zA-Z0-9.-]+$' }
|
|
159
|
+
const entity: Schema = { type: 'string', minLength: 1, maxLength: 200 }
|
|
160
|
+
const integer: Schema = { type: 'integer', minimum: 0, maximum: Number.MAX_SAFE_INTEGER }
|
|
161
|
+
const number: Schema = { type: 'number', minimum: 0 }
|
|
162
|
+
const bool: Schema = { type: 'boolean' }
|
|
163
|
+
const object = (properties: Record<string, Schema>, required = Object.keys(properties)): Schema => ({ type: 'object', additionalProperties: false, properties, required })
|
|
164
|
+
const digest: Schema = { type: 'string', pattern: '^[a-f0-9]{64}$' }
|
|
165
|
+
const draftReference = object({ id: entity, revision: { type: 'integer', minimum: 1, maximum: Number.MAX_SAFE_INTEGER }, token: digest })
|
|
166
|
+
const componentDraftReference = object({ id: entity, revision: { type: 'integer', minimum: 1, maximum: Number.MAX_SAFE_INTEGER }, token: digest, blockId: entity, stepId: entity }, ['id', 'revision', 'token', 'blockId'])
|
|
167
|
+
const componentMetadata = object({ draft: componentDraftReference, definitionId: entity, current: bool, storage: { enum: ['durable', 'session'] }, status: { enum: ['available', 'pending', 'session-only'] }, updatedAt: integer, baseVersion: entity, sourceCurrent: bool, composing: bool, reset: bool })
|
|
168
|
+
const nullableComponentReference: Schema = { oneOf: [componentDraftReference, { type: 'null' }] }
|
|
169
|
+
const nullableCommentReference: Schema = { oneOf: [draftReference, { type: 'null' }] }
|
|
170
|
+
const commentRestore = object({ composerId: id, expectedCurrent: nullableCommentReference })
|
|
171
|
+
const cursor = object({ after: entity, token: digest })
|
|
172
|
+
export const AUTHORING_CLIENT_ACTION_RESULT_SCHEMA: Schema = { oneOf: [
|
|
173
|
+
object({ type: { const: 'comment-drafts.inspect' }, family: { const: 'comment' }, coverage: { enum: ['stored-and-session', 'session-only'] }, skippedInvalid: integer,
|
|
174
|
+
token: digest, drafts: { type: 'array', maxItems: 10, items: object({ draft: draftReference, kind: { enum: ['create', 'reply', 'edit'] }, storage: { enum: ['durable', 'session'] }, updatedAt: integer, baseVersion: entity, composing: bool, pendingSubmission: bool, ownersKnown: bool, active: bool, discardable: bool, restore: commentRestore, threadId: entity, commentId: entity }, ['draft', 'kind', 'storage', 'updatedAt', 'baseVersion', 'composing', 'pendingSubmission', 'ownersKnown', 'active', 'discardable']) }, next: cursor }, ['type', 'family', 'coverage', 'skippedInvalid', 'token', 'drafts']),
|
|
175
|
+
object({ type: { const: 'comment-drafts.read' }, family: { const: 'comment' }, draft: draftReference, storage: { enum: ['durable', 'session'] }, format: { const: 'presmium-rich-text-json-v1' }, offset: integer, endOffset: integer, totalUtf16: integer, totalUtf8: integer, sha256: digest, jsonText: { type: 'string', maxLength: 2048 } }),
|
|
176
|
+
object({ type: { const: 'comment-drafts.restore' }, family: { const: 'comment' }, draft: draftReference, composerId: id, restored: draftReference, preservedCurrent: nullableCommentReference, targetState: { enum: ['current', 'changed'] } }),
|
|
177
|
+
object({ type: { const: 'comment-drafts.discard' }, family: { const: 'comment' }, draft: draftReference, discarded: { const: true } }),
|
|
178
|
+
object({ type: { const: 'component-drafts.inspect' }, family: { const: 'component' }, coverage: { enum: ['stored-and-session', 'session-only'] }, skippedInvalid: integer,
|
|
179
|
+
token: digest, drafts: { type: 'array', maxItems: 10, items: componentMetadata }, next: cursor }, ['type', 'family', 'coverage', 'skippedInvalid', 'token', 'drafts']),
|
|
180
|
+
object({ type: { const: 'component-drafts.read' }, family: { const: 'component' }, draft: componentDraftReference, storage: { enum: ['durable', 'session'] }, format: { const: 'presmium-component-parameters-json-v1' }, offset: integer, endOffset: integer, totalUtf16: integer, totalUtf8: integer, sha256: digest, jsonText: { type: 'string', maxLength: 2048 } }),
|
|
181
|
+
object({ type: { const: 'component-drafts.restore' }, family: { const: 'component' }, draft: componentDraftReference, restored: componentMetadata, preservedCurrent: nullableComponentReference }),
|
|
182
|
+
object({ type: { const: 'component-drafts.discard' }, family: { const: 'component' }, draft: componentDraftReference, current: bool, discarded: { const: true } }),
|
|
183
|
+
] }
|
|
184
|
+
const source = DOCUMENT_PAGE_DUPLICATION_SCHEMA.properties.source as unknown as Schema
|
|
185
|
+
const binding = object({ source, irRevision: entity, planId: entity })
|
|
186
|
+
const position = object({ timeMs: number, stepTimeMs: number, sectionId: entity, stepId: entity, durationMs: number, playing: bool, rate: { type: 'number', exclusiveMinimum: 0, maximum: 16 }, trying: bool, waitingFor: entity }, ['timeMs', 'durationMs', 'playing', 'rate', 'trying'])
|
|
187
|
+
const playback = object({ binding, status: { enum: ['loading', 'ready', 'estimated', 'unavailable', 'failed'] }, position, error: { type: 'string', maxLength: 300 } }, ['binding', 'status'])
|
|
188
|
+
export const AUTHORING_CLIENT_SNAPSHOT_SCHEMA: Schema = object({
|
|
189
|
+
revision: integer, source, view: { enum: ['document', 'present'] }, editing: object({ dirty: bool, saving: bool, composing: bool, conflicted: bool, locked: bool, loading: bool }),
|
|
190
|
+
interaction: { type: 'string', maxLength: 200 }, position: object({ sectionId: entity, stepId: entity }, ['sectionId']), binding, playback,
|
|
191
|
+
preview: { oneOf: [object({ status: { enum: ['unprepared', 'preparing', 'unavailable'] } }), object({ status: { const: 'prepared' }, source, irRevision: entity, projectRevision: integer })] },
|
|
192
|
+
actions: { type: 'array', items: { enum: [...AUTHORING_CLIENT_ACTIONS] }, maxItems: AUTHORING_CLIENT_ACTIONS.length, uniqueItems: true }, unsubmittedContent: { const: 'not-inspected' },
|
|
193
|
+
}, ['revision', 'source', 'view', 'editing', 'interaction', 'actions', 'unsubmittedContent'])
|
|
194
|
+
const action = (type: string, properties: Record<string, Schema> = {}): Schema => object({ type: { const: type }, ...properties })
|
|
195
|
+
export const AUTHORING_CLIENT_ACTION_SCHEMA: Schema = { oneOf: [
|
|
196
|
+
action('state.read'), action('source.flush'), action('preview.prepare'), action('playback.pause'), action('position.step', { sectionId: entity, stepId: entity }),
|
|
197
|
+
object({ type: { const: 'comment-drafts.inspect' }, cursor, limit: { type: 'integer', minimum: 1, maximum: 10 } }, ['type']),
|
|
198
|
+
object({ type: { const: 'comment-drafts.read' }, draft: draftReference, offset: integer, maxChars: { type: 'integer', minimum: 2, maximum: 2048 } }, ['type', 'draft', 'offset']),
|
|
199
|
+
action('comment-drafts.restore', { draft: draftReference, composerId: id, expectedCurrent: nullableCommentReference }),
|
|
200
|
+
action('comment-drafts.discard', { draft: draftReference }),
|
|
201
|
+
object({ type: { const: 'component-drafts.inspect' }, cursor, limit: { type: 'integer', minimum: 1, maximum: 10 } }, ['type']),
|
|
202
|
+
object({ type: { const: 'component-drafts.read' }, draft: componentDraftReference, offset: integer, maxChars: { type: 'integer', minimum: 2, maximum: 2048 } }, ['type', 'draft', 'offset']),
|
|
203
|
+
action('component-drafts.restore', { draft: componentDraftReference, expectedCurrent: nullableComponentReference }),
|
|
204
|
+
action('component-drafts.discard', { draft: componentDraftReference, current: bool }),
|
|
205
|
+
action('playback.play', { binding }), action('playback.seek', { binding, timeMs: number }), action('playback.rate', { binding, rate: { enum: [...PRODUCTION_PLAYBACK_RATES] } }),
|
|
206
|
+
] }
|
|
207
|
+
export const AUTHORING_CLIENT_REQUEST_SCHEMA: Schema = object({ clientId: id, epoch: id, requestId: id, source, expectedClientState: object({ connectionId: id, revision: integer }), action: AUTHORING_CLIENT_ACTION_SCHEMA })
|
|
208
|
+
export const AUTHORING_CLIENT_QUERY_SCHEMA: Schema = object({ clientId: id, after: id }, [])
|
|
209
|
+
export const AUTHORING_CLIENT_REQUEST_QUERY_SCHEMA: Schema = object({ clientId: id, epoch: id, requestId: id })
|
|
210
|
+
export const AUTHORING_CLIENT_OPEN_SCHEMA: Schema = object({ clientId: id, epoch: id, snapshot: AUTHORING_CLIENT_SNAPSHOT_SCHEMA })
|
|
211
|
+
export const AUTHORING_CLIENT_REPORT_SCHEMA: Schema = object({ sequence: integer, snapshot: AUTHORING_CLIENT_SNAPSHOT_SCHEMA })
|
|
212
|
+
export const AUTHORING_CLIENT_ACK_SCHEMA: Schema = object({ sequence: integer, snapshot: AUTHORING_CLIENT_SNAPSHOT_SCHEMA, requestId: id, state: { enum: ['executing', 'done', 'refused', 'failed'] }, error: object({ code: { type: 'string', minLength: 1, maxLength: 160 } }), result: AUTHORING_CLIENT_ACTION_RESULT_SCHEMA }, ['sequence', 'snapshot', 'requestId', 'state'])
|
|
213
|
+
export const AUTHORING_CLIENT_CONNECTION_SCHEMA: Schema = object({ clientId: id, epoch: id, connectionId: id, credential: { type: 'string', minLength: 32, maxLength: 100 } })
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import type { CanonicalSource } from '../ir.ts'
|
|
2
|
+
import type { DocumentEditOperation, DocumentIssue, TeachingDocument } from './document.ts'
|
|
3
|
+
import type { Member } from './server.ts'
|
|
4
|
+
import type { DocumentAnswer, DocumentChange, DocumentChangeAnswer, RevisionOrigin } from './workspace.ts'
|
|
5
|
+
import type { DocumentOutlineAnswer, DocumentOutlineQuery, DocumentPartAnswer, DocumentPartQuery } from './document-reads.ts'
|
|
6
|
+
import type { AppliedDocumentAuthoringAction, DocumentAuthoringActionInspection, DocumentAuthoringActionPlan, DocumentAuthoringActionRead, DocumentAuthoringActionReference, DocumentAuthoringActionRequest, DocumentAuthoringAction, DocumentComponentImpactAnswer, DocumentComponentImpactQuery } from './authoring-actions.ts'
|
|
7
|
+
|
|
8
|
+
export const AUTHORING_API_VERSION = 1 as const
|
|
9
|
+
export const DOCUMENT_OPERATION_TYPES = ['insertBlock', 'moveBlock', 'deleteBlock', 'updateText', 'updateSection', 'setStepState', 'setNarration', 'setTiming', 'replaceAsset'] as const satisfies readonly DocumentEditOperation['type'][]
|
|
10
|
+
|
|
11
|
+
export interface AuthoringCapabilities {
|
|
12
|
+
apiVersion: typeof AUTHORING_API_VERSION
|
|
13
|
+
documentSchemaVersion: number
|
|
14
|
+
available: boolean
|
|
15
|
+
reason?: 'host-unavailable' | 'permission-denied'
|
|
16
|
+
actions: { read: boolean, outline: boolean, readPart: boolean, plan: boolean, apply: boolean, planPageDuplication: boolean, applyPageDuplication: boolean, planPageChange: boolean, applyPageChange: boolean, planAction: boolean, applyAction: boolean }
|
|
17
|
+
applyUnavailableReason?: 'review-required' | 'operation-disabled'
|
|
18
|
+
readLimits?: { maxRequestChars: number, maxResponseChars: number, responseSerialization: 'JSON.stringify(value,null,2)', offsetUnit: 'utf16' }
|
|
19
|
+
pageLimits?: { maxRequestChars: number, maxResponseChars: number, responseSerialization: 'JSON.stringify(value,null,2)' }
|
|
20
|
+
actionLimits?: { maxRequestChars: number, maxResponseChars: number, responseSerialization: 'JSON.stringify(value,null,2)' }
|
|
21
|
+
semanticActions?: readonly DocumentAuthoringAction['type'][]
|
|
22
|
+
components?: { parameters: true, stepParameters: true, restoreStepParameters: true, useExistingDefinition: true, impactRead: true, maxChanges: number, maxPathDepth: number, independentCopy: { available: false, reason: 'implementation-closure-unavailable' } }
|
|
23
|
+
operations: readonly DocumentEditOperation['type'][]
|
|
24
|
+
replacement: boolean
|
|
25
|
+
fileChanges: boolean
|
|
26
|
+
limits?: { maxFiles: number, maxTextBytes: number, maxOperations: number, planLifetimeMs: number, maxPlans: number, maxReceipts: number }
|
|
27
|
+
idempotency: 'process-current-source' | 'unavailable'
|
|
28
|
+
review: 'required-unsupported' | 'locks-and-history' | 'locks-only' | 'unavailable'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** A pure local preflight. Files include lecture.json and their predicted post-save versions. */
|
|
32
|
+
export interface DocumentChangePlan {
|
|
33
|
+
apiVersion: typeof AUTHORING_API_VERSION
|
|
34
|
+
planId: string
|
|
35
|
+
changeId: string
|
|
36
|
+
document: string
|
|
37
|
+
source: CanonicalSource
|
|
38
|
+
digest: string
|
|
39
|
+
expiresAt: string
|
|
40
|
+
content: TeachingDocument
|
|
41
|
+
issues: DocumentIssue[]
|
|
42
|
+
files: { path: string, base: string | null, version: string }[]
|
|
43
|
+
impact: { sections: string[], blocks: string[], steps: string[], narrations: string[], assets: string[], definitions: string[], sectionOrder: boolean, production: boolean, comments: boolean, metadata: boolean }
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface ApplyDocumentChange { change: DocumentChange, planId: string }
|
|
47
|
+
export interface DocumentPageDuplicationRequest { id: string, source: CanonicalSource, sectionId: string, title: string }
|
|
48
|
+
export type DocumentPageAction =
|
|
49
|
+
| { type: 'create', title: string, after?: string | null }
|
|
50
|
+
| { type: 'rename', sectionId: string, title: string }
|
|
51
|
+
| { type: 'move', sectionId: string, before: string | null }
|
|
52
|
+
| { type: 'delete', sectionId: string }
|
|
53
|
+
export interface DocumentPageIds { sectionId: string, blockId: string, nodeId: string, stepId: string }
|
|
54
|
+
export interface DocumentPageDeletionImpact {
|
|
55
|
+
sectionId: string
|
|
56
|
+
retainedEmpty: boolean
|
|
57
|
+
blocks: string[]
|
|
58
|
+
steps: string[]
|
|
59
|
+
narrations: string[]
|
|
60
|
+
retainedNarrations: string[]
|
|
61
|
+
cues: string[]
|
|
62
|
+
clips: string[]
|
|
63
|
+
detachedComments: string[]
|
|
64
|
+
references: ({ type: 'cue', narrationId: string, cueId: string, blockId: string, stepIds: string[] } | { type: 'inheritance', stepId: string, inheritedStepId: string })[]
|
|
65
|
+
}
|
|
66
|
+
export interface DocumentPageChangeRequest { id: string, source: CanonicalSource, action: DocumentPageAction }
|
|
67
|
+
export interface DocumentPageChangePlan extends DocumentChangePlan {
|
|
68
|
+
action: DocumentPageAction
|
|
69
|
+
sectionId: string
|
|
70
|
+
created?: DocumentPageIds
|
|
71
|
+
deletion?: DocumentPageDeletionImpact
|
|
72
|
+
change: DocumentChange
|
|
73
|
+
}
|
|
74
|
+
export interface DocumentPageChangeReference extends DocumentPageDuplicationReference { confirmDeletion?: true }
|
|
75
|
+
export interface DocumentPageChangeInspection { plan: DocumentPageChangePlan, currentSource: CanonicalSource, state: 'current' | 'applied' | 'old' }
|
|
76
|
+
export interface AppliedDocumentPageChange extends Omit<AppliedDocumentChange, 'content' | 'issues' | 'verification' | 'commit'> {
|
|
77
|
+
commit?: { id: string, origin: RevisionOrigin['kind'] }
|
|
78
|
+
planId: string
|
|
79
|
+
digest: string
|
|
80
|
+
action: DocumentPageAction['type']
|
|
81
|
+
sectionId: string
|
|
82
|
+
retainedEmpty: boolean
|
|
83
|
+
changed: boolean
|
|
84
|
+
impact: Record<keyof DocumentChangePlan['impact'], number | boolean>
|
|
85
|
+
verification: AppliedDocumentChange['verification'] & { plannedVersion: string, pageStateVerified: true }
|
|
86
|
+
}
|
|
87
|
+
export interface DocumentPageDuplicationPlan extends DocumentChangePlan {
|
|
88
|
+
action: 'duplicatePage'
|
|
89
|
+
section: { from: string, to: string }
|
|
90
|
+
/** Only copied declared identities; assets, definitions, opaque payloads and comments retain their references. */
|
|
91
|
+
mapping: Record<string, string>
|
|
92
|
+
/** Pass this exact candidate with planId to the existing apply operation. */
|
|
93
|
+
change: DocumentChange
|
|
94
|
+
}
|
|
95
|
+
export interface DocumentPageDuplicationReference { planId: string, source: CanonicalSource, digest: string }
|
|
96
|
+
export interface DocumentPageDuplicationRead { planId: string, digest: string }
|
|
97
|
+
export interface DocumentPageDuplicationInspection {
|
|
98
|
+
plan: DocumentPageDuplicationPlan
|
|
99
|
+
currentSource: CanonicalSource
|
|
100
|
+
state: 'current' | 'applied' | 'old'
|
|
101
|
+
}
|
|
102
|
+
export interface AppliedDocumentPageDuplication extends Omit<AppliedDocumentChange, 'content' | 'issues' | 'verification'> {
|
|
103
|
+
planId: string
|
|
104
|
+
digest: string
|
|
105
|
+
section: DocumentPageDuplicationPlan['section']
|
|
106
|
+
copied: { identities: number, sections: number, blocks: number, steps: number, narrations: number }
|
|
107
|
+
verification: AppliedDocumentChange['verification'] & { plannedVersion: string, pagePresent: true }
|
|
108
|
+
}
|
|
109
|
+
export const DOCUMENT_PAGE_DUPLICATION_SCHEMA = {
|
|
110
|
+
type: 'object', additionalProperties: false, required: ['id', 'source', 'sectionId', 'title'],
|
|
111
|
+
properties: {
|
|
112
|
+
id: { type: 'string', minLength: 1, maxLength: 200 },
|
|
113
|
+
source: { type: 'object', additionalProperties: false, required: ['documentId', 'version', 'fingerprint'], properties: {
|
|
114
|
+
documentId: { type: 'string', minLength: 1, maxLength: 200 }, version: { type: 'string', minLength: 1, maxLength: 128 }, fingerprint: { type: 'string', minLength: 1, maxLength: 128 },
|
|
115
|
+
} },
|
|
116
|
+
sectionId: { type: 'string', minLength: 1, maxLength: 200 }, title: { type: 'string', minLength: 1, maxLength: 1000 },
|
|
117
|
+
},
|
|
118
|
+
} as const
|
|
119
|
+
export const DOCUMENT_PAGE_DUPLICATION_APPLY_SCHEMA = {
|
|
120
|
+
type: 'object', additionalProperties: false, required: ['planId', 'source', 'digest'], properties: {
|
|
121
|
+
planId: { type: 'string', minLength: 1, maxLength: 200 },
|
|
122
|
+
source: DOCUMENT_PAGE_DUPLICATION_SCHEMA.properties.source,
|
|
123
|
+
digest: { type: 'string', pattern: '^[a-f0-9]{64}$' },
|
|
124
|
+
},
|
|
125
|
+
} as const
|
|
126
|
+
export const DOCUMENT_PAGE_CHANGE_SCHEMA = {
|
|
127
|
+
type: 'object', additionalProperties: false, required: ['id', 'source', 'action'], properties: {
|
|
128
|
+
id: DOCUMENT_PAGE_DUPLICATION_SCHEMA.properties.id,
|
|
129
|
+
source: DOCUMENT_PAGE_DUPLICATION_SCHEMA.properties.source,
|
|
130
|
+
action: { oneOf: [
|
|
131
|
+
{ type: 'object', additionalProperties: false, required: ['type', 'title'], properties: { type: { const: 'create' }, title: DOCUMENT_PAGE_DUPLICATION_SCHEMA.properties.title, after: { type: ['string', 'null'], minLength: 1, maxLength: 200 } } },
|
|
132
|
+
{ type: 'object', additionalProperties: false, required: ['type', 'sectionId', 'title'], properties: { type: { const: 'rename' }, sectionId: DOCUMENT_PAGE_DUPLICATION_SCHEMA.properties.sectionId, title: DOCUMENT_PAGE_DUPLICATION_SCHEMA.properties.title } },
|
|
133
|
+
{ type: 'object', additionalProperties: false, required: ['type', 'sectionId', 'before'], properties: { type: { const: 'move' }, sectionId: DOCUMENT_PAGE_DUPLICATION_SCHEMA.properties.sectionId, before: { type: ['string', 'null'], minLength: 1, maxLength: 200 } } },
|
|
134
|
+
{ type: 'object', additionalProperties: false, required: ['type', 'sectionId'], properties: { type: { const: 'delete' }, sectionId: DOCUMENT_PAGE_DUPLICATION_SCHEMA.properties.sectionId } },
|
|
135
|
+
] },
|
|
136
|
+
},
|
|
137
|
+
} as const
|
|
138
|
+
export const DOCUMENT_PAGE_CHANGE_APPLY_SCHEMA = {
|
|
139
|
+
...DOCUMENT_PAGE_DUPLICATION_APPLY_SCHEMA,
|
|
140
|
+
properties: { ...DOCUMENT_PAGE_DUPLICATION_APPLY_SCHEMA.properties, confirmDeletion: { const: true } },
|
|
141
|
+
} as const
|
|
142
|
+
export interface AppliedDocumentChange extends DocumentChangeAnswer {
|
|
143
|
+
verification: { saved: true, source: CanonicalSource, compiled: 'not-requested', runtime: 'not-requested' }
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Injected by an authoring host. Neither MCP nor compilation owns file-writing semantics.
|
|
148
|
+
* Each `check*` runs its apply's checks (plan reference, review, current source or a replayable receipt, the dry-run transaction) without writing,
|
|
149
|
+
* so a doomed request is refused before the author is asked; the apply checks everything again.
|
|
150
|
+
*/
|
|
151
|
+
export interface DocumentAuthoring {
|
|
152
|
+
capabilities(document: string, member: Member): AuthoringCapabilities
|
|
153
|
+
read(document: string, member: Member): Promise<DocumentAnswer>
|
|
154
|
+
outline(document: string, query: DocumentOutlineQuery, member: Member): Promise<DocumentOutlineAnswer>
|
|
155
|
+
readPart(document: string, query: DocumentPartQuery, member: Member): Promise<DocumentPartAnswer>
|
|
156
|
+
plan(document: string, change: DocumentChange, member: Member): Promise<DocumentChangePlan>
|
|
157
|
+
planPageDuplication(document: string, request: DocumentPageDuplicationRequest, member: Member): Promise<DocumentPageDuplicationPlan>
|
|
158
|
+
readPageDuplicationPlan(document: string, reference: DocumentPageDuplicationRead, member: Member): Promise<DocumentPageDuplicationInspection>
|
|
159
|
+
checkPageDuplication(document: string, reference: DocumentPageDuplicationReference, member: Member): Promise<void>
|
|
160
|
+
applyPageDuplication(document: string, reference: DocumentPageDuplicationReference, member: Member): Promise<AppliedDocumentPageDuplication>
|
|
161
|
+
planPageChange(document: string, request: DocumentPageChangeRequest, member: Member): Promise<DocumentPageChangePlan>
|
|
162
|
+
readPageChangePlan(document: string, reference: DocumentPageDuplicationRead, member: Member): Promise<DocumentPageChangeInspection>
|
|
163
|
+
checkPageChange(document: string, reference: DocumentPageChangeReference, member: Member): Promise<void>
|
|
164
|
+
applyPageChange(document: string, reference: DocumentPageChangeReference, member: Member): Promise<AppliedDocumentPageChange>
|
|
165
|
+
planAction(document: string, request: DocumentAuthoringActionRequest, member: Member): Promise<DocumentAuthoringActionPlan>
|
|
166
|
+
readActionPlan(document: string, reference: DocumentAuthoringActionRead, member: Member): Promise<DocumentAuthoringActionInspection>
|
|
167
|
+
readActionImpact(document: string, query: DocumentComponentImpactQuery, member: Member): Promise<DocumentComponentImpactAnswer>
|
|
168
|
+
checkAction(document: string, reference: DocumentAuthoringActionReference, member: Member): Promise<void>
|
|
169
|
+
applyAction(document: string, reference: DocumentAuthoringActionReference, member: Member): Promise<AppliedDocumentAuthoringAction>
|
|
170
|
+
checkApply(document: string, request: ApplyDocumentChange, member: Member): Promise<void>
|
|
171
|
+
apply(document: string, request: ApplyDocumentChange, member: Member): Promise<AppliedDocumentChange>
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export interface AuthoringFailure {
|
|
175
|
+
code: string
|
|
176
|
+
message: string
|
|
177
|
+
status: number
|
|
178
|
+
retryable: boolean
|
|
179
|
+
detail?: Record<string, unknown>
|
|
180
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { IRDocument } from '../ir.ts'
|
|
2
|
+
import type { AnchorLockFile, AnchorResolverRegistry, SourceReader } from './anchors.ts'
|
|
3
|
+
import type { ServiceToken } from './service.ts'
|
|
4
|
+
|
|
5
|
+
export type Severity = 'error' | 'warning' | 'info'
|
|
6
|
+
|
|
7
|
+
export interface Finding {
|
|
8
|
+
checkId: string
|
|
9
|
+
severity: Severity
|
|
10
|
+
message: string
|
|
11
|
+
source?: { file: string; line?: number }
|
|
12
|
+
hint?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface CheckContext {
|
|
16
|
+
deckDir: string
|
|
17
|
+
workspaceRoot: string
|
|
18
|
+
headmatter: Record<string, unknown>
|
|
19
|
+
src: SourceReader
|
|
20
|
+
resolvers: AnchorResolverRegistry
|
|
21
|
+
lock: AnchorLockFile
|
|
22
|
+
ir: IRDocument
|
|
23
|
+
service?<T>(token: ServiceToken<T>): T | undefined
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface Check {
|
|
27
|
+
id: string
|
|
28
|
+
run(ctx: CheckContext): Promise<Finding[]>
|
|
29
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { Unsubscribe } from './plugin.ts'
|
|
2
|
+
import { defineService } from './service.ts'
|
|
3
|
+
import type { Cursor } from './steps.ts'
|
|
4
|
+
|
|
5
|
+
export type ParticipantRole = 'presenter' | 'learner'
|
|
6
|
+
|
|
7
|
+
export interface Participant {
|
|
8
|
+
id: string
|
|
9
|
+
name: string
|
|
10
|
+
role: ParticipantRole
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type SessionStatus = 'idle' | 'active' | 'ended'
|
|
14
|
+
|
|
15
|
+
export interface SessionSnapshot {
|
|
16
|
+
sessionId?: string
|
|
17
|
+
status: SessionStatus
|
|
18
|
+
me?: Participant
|
|
19
|
+
participants: Participant[]
|
|
20
|
+
presenterCursor?: Cursor
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface SessionService {
|
|
24
|
+
snapshot(): SessionSnapshot
|
|
25
|
+
subscribe(listener: (snapshot: SessionSnapshot) => void): Unsubscribe
|
|
26
|
+
create(name: string): Promise<string>
|
|
27
|
+
join(sessionId: string, name: string): Promise<void>
|
|
28
|
+
end(): Promise<void>
|
|
29
|
+
leave(): void
|
|
30
|
+
archive?(): Promise<string>
|
|
31
|
+
readonly personal?: ClassroomStore
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export const SESSION = defineService<SessionService>('presmium.session')
|
|
35
|
+
|
|
36
|
+
export interface StoredItem<T> {
|
|
37
|
+
id: string
|
|
38
|
+
value: T
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface ClassroomStore {
|
|
42
|
+
list<T>(collection: string): StoredItem<T>[]
|
|
43
|
+
get<T>(collection: string, id: string): T | undefined
|
|
44
|
+
put<T>(collection: string, id: string, value: T): void
|
|
45
|
+
remove(collection: string, id: string): void
|
|
46
|
+
subscribe(collection: string, listener: () => void): Unsubscribe
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export const CLASSROOM_STORE = defineService<ClassroomStore>('presmium.classroomStore')
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import type { Finding } from './checks.ts'
|
|
2
|
+
import type { CanonicalSource } from '../ir.ts'
|
|
3
|
+
|
|
4
|
+
export type DiagnosticOrigin = 'compile' | 'check' | 'preview' | 'runtime' | 'server' | 'authoring'
|
|
5
|
+
|
|
6
|
+
/** A current problem. Source paths are relative to the workspace root. */
|
|
7
|
+
export interface Diagnostic extends Finding {
|
|
8
|
+
id: string
|
|
9
|
+
document: string
|
|
10
|
+
origin: DiagnosticOrigin
|
|
11
|
+
renderer?: 'web' | 'slides'
|
|
12
|
+
revision?: string
|
|
13
|
+
/** Saved authoring baseline when this issue was observed, never a compiled IR revision. */
|
|
14
|
+
authoringSource?: CanonicalSource
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/** Replaces the previous snapshot; it is not a history of diagnostic events. */
|
|
18
|
+
export interface DiagnosticSnapshot {
|
|
19
|
+
version: number
|
|
20
|
+
diagnostics: Diagnostic[]
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** The current problems reported by one embedded preview. */
|
|
24
|
+
export interface PreviewDiagnostics {
|
|
25
|
+
document: string
|
|
26
|
+
renderer: 'web' | 'slides'
|
|
27
|
+
instance: string
|
|
28
|
+
revision: string
|
|
29
|
+
findings: Finding[]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** A selected author's window can report a failure before any runtime has compiled. */
|
|
33
|
+
export interface AuthoringDiagnostics {
|
|
34
|
+
document: string
|
|
35
|
+
instance: string
|
|
36
|
+
findings: { checkId: string, severity: 'error', message: string, authoringSource?: CanonicalSource }[]
|
|
37
|
+
}
|