@neta-art/cohub 8.24.0 → 9.0.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/board/animation.d.ts +57 -181
- package/dist/board/animation.js +374 -220
- package/dist/board/core/arrow-geometry.d.ts +33 -18
- package/dist/board/core/arrow-geometry.js +283 -52
- package/dist/board/core/draw-geometry.d.ts +22 -36
- package/dist/board/core/draw-geometry.js +148 -85
- package/dist/board/core/export-assets.d.ts +3 -6
- package/dist/board/core/export-assets.js +0 -2
- package/dist/board/core/export-plan.d.ts +12 -63
- package/dist/board/core/export-plan.js +55 -94
- package/dist/board/core/palette.d.ts +4 -21
- package/dist/board/core/palette.js +8 -13
- package/dist/board/core/scene.d.ts +33 -0
- package/dist/board/core/scene.js +302 -0
- package/dist/board/core/shape-definition.d.ts +12 -24
- package/dist/board/core/shape-definition.js +0 -19
- package/dist/board/core/shape-types.d.ts +6 -120
- package/dist/board/core/shape-types.js +5 -5
- package/dist/board/core/text-metrics.d.ts +8 -11
- package/dist/board/core/text-metrics.js +5 -34
- package/dist/board/core/tool-styles.d.ts +6 -16
- package/dist/board/core/tool-styles.js +17 -25
- package/dist/board/export/index.d.ts +10 -17
- package/dist/board/export/index.js +22 -21
- package/dist/board/export/scene.d.ts +10 -16
- package/dist/board/export/scene.js +17 -30
- package/dist/board/geometry.d.ts +27 -56
- package/dist/board/geometry.js +7 -67
- package/dist/board/headless/index.d.ts +6 -27
- package/dist/board/headless/index.js +4 -19
- package/dist/board/headless/sketch.d.ts +19 -0
- package/dist/board/headless/sketch.js +146 -0
- package/dist/board/image-key.d.ts +2 -12
- package/dist/board/image-key.js +3 -13
- package/dist/board/index.d.ts +18 -20
- package/dist/board/index.js +17 -17
- package/dist/board/media-playback.d.ts +3 -6
- package/dist/board/media-playback.js +6 -6
- package/dist/board/presets.d.ts +150 -0
- package/dist/board/presets.js +226 -0
- package/dist/board/render/board-background.d.ts +5 -3
- package/dist/board/render/board-background.js +8 -7
- package/dist/board/render/clip.d.ts +14 -0
- package/dist/board/render/clip.js +47 -0
- package/dist/board/render/index.d.ts +8 -5
- package/dist/board/render/index.js +7 -4
- package/dist/board/render/media-interaction.d.ts +4 -5
- package/dist/board/render/media-interaction.js +1 -2
- package/dist/board/render/palette.d.ts +6 -2
- package/dist/board/render/palette.js +7 -1
- package/dist/board/render/renderers/arrow-card-renderer.js +69 -71
- package/dist/board/render/renderers/audio-card-renderer.js +3 -3
- package/dist/board/render/renderers/base-card-renderer.d.ts +6 -17
- package/dist/board/render/renderers/base-card-renderer.js +1 -24
- package/dist/board/render/renderers/board-renderer-registry.d.ts +20 -50
- package/dist/board/render/renderers/board-renderer-registry.js +6 -7
- package/dist/board/render/renderers/draw-card-renderer.js +89 -56
- package/dist/board/render/renderers/effect-card-renderer.d.ts +6 -0
- package/dist/board/render/renderers/effect-card-renderer.js +197 -0
- package/dist/board/render/renderers/far-plate.d.ts +4 -21
- package/dist/board/render/renderers/far-plate.js +15 -34
- package/dist/board/render/renderers/file-card-renderer.d.ts +4 -25
- package/dist/board/render/renderers/file-card-renderer.js +8 -58
- package/dist/board/render/renderers/frame-card-renderer.js +28 -28
- package/dist/board/render/renderers/image-card-renderer.d.ts +4 -3
- package/dist/board/render/renderers/image-card-renderer.js +0 -9
- package/dist/board/render/renderers/shape-card-renderer.d.ts +9 -0
- package/dist/board/render/renderers/shape-card-renderer.js +240 -0
- package/dist/board/render/renderers/sketch-card-renderer.d.ts +5 -0
- package/dist/board/render/renderers/sketch-card-renderer.js +98 -0
- package/dist/board/render/renderers/task-card-renderer.d.ts +0 -1
- package/dist/board/render/renderers/task-card-renderer.js +14 -16
- package/dist/board/render/renderers/text-card-renderer.d.ts +1 -3
- package/dist/board/render/renderers/text-card-renderer.js +56 -46
- package/dist/board/render/renderers/unknown-card-renderer.js +4 -10
- package/dist/board/render/renderers/video-card-renderer.d.ts +4 -2
- package/dist/board/render/renderers/video-card-renderer.js +2 -1
- package/dist/board/render/sketch-host.d.ts +14 -0
- package/dist/board/render/sketch-host.js +216 -0
- package/dist/board/render/stroke.d.ts +12 -0
- package/dist/board/render/stroke.js +90 -0
- package/dist/board/render/text-measurement.d.ts +6 -26
- package/dist/board/render/text-measurement.js +11 -30
- package/dist/board/replay.d.ts +6 -31
- package/dist/board/replay.js +45 -203
- package/dist/board/runtime/board-player.d.ts +24 -0
- package/dist/board/runtime/board-player.js +161 -0
- package/dist/board/task.d.ts +2 -10
- package/dist/board/task.js +1 -10
- package/dist/chunks/environment.d.ts +2291 -3864
- package/dist/chunks/host-bridge.d.ts +253 -0
- package/dist/chunks/http.d.ts +233 -58
- package/dist/chunks/http.js +1046 -1028
- package/dist/chunks/transport.js +113 -59
- package/dist/chunks/websocket.d.ts +1 -1
- package/dist/chunks/websocket.js +1 -1
- package/dist/host-bridge.d.ts +54 -0
- package/dist/host-bridge.js +322 -0
- package/dist/http.d.ts +3 -3
- package/dist/index.d.ts +74 -191
- package/dist/index.js +2032 -1186
- package/dist/protocol/dist/board-constants.d.ts +1 -29
- package/dist/protocol/dist/board-constants.js +2 -115
- package/dist/protocol/dist/board-layout.d.ts +59 -0
- package/dist/protocol/dist/board-layout.js +482 -0
- package/dist/protocol/dist/board-model.d.ts +2052 -0
- package/dist/protocol/dist/board-model.js +592 -0
- package/dist/protocol/dist/board-patch.d.ts +86 -0
- package/dist/protocol/dist/board-patch.js +734 -0
- package/dist/protocol/dist/board-schema.d.ts +33 -0
- package/dist/protocol/dist/board-schema.js +59 -0
- package/dist/protocol/dist/board-upgrade.d.ts +1 -0
- package/dist/protocol/dist/board-url.d.ts +1 -3
- package/dist/protocol/dist/board-url.js +2 -2
- package/dist/protocol/dist/board.d.ts +37 -232
- package/dist/protocol/dist/board.js +62 -160
- package/dist/protocol/dist/display/actions.d.ts +1 -0
- package/dist/protocol/dist/display/index.d.ts +2 -0
- package/dist/protocol/dist/index.d.ts +11 -12
- package/dist/protocol/dist/model/metrics.d.ts +2 -0
- package/dist/protocol/dist/model/session.d.ts +2 -1
- package/dist/protocol/dist/realtime/types.d.ts +2 -2
- package/dist/protocol/dist/runtime/index.d.ts +1 -0
- package/dist/space-picker.d.ts +1 -0
- package/dist/space-picker.js +1 -0
- package/dist/types.d.ts +2 -5
- package/package.json +9 -1
- package/dist/board/core/connections.d.ts +0 -108
- package/dist/board/core/connections.js +0 -455
- package/dist/board/mutation.d.ts +0 -6
- package/dist/board/mutation.js +0 -14
- package/dist/board/render/connection-layer.d.ts +0 -53
- package/dist/board/render/connection-layer.js +0 -375
- package/dist/board/render/renderers/geo-card-renderer.d.ts +0 -5
- package/dist/board/render/renderers/geo-card-renderer.js +0 -127
- package/dist/board/semantic-document.d.ts +0 -37
- package/dist/board/semantic-document.js +0 -288
- package/dist/board/semantic-mutation.d.ts +0 -10
- package/dist/board/semantic-mutation.js +0 -209
- package/dist/protocol/dist/board-animation.js +0 -34
- package/dist/protocol/dist/board-authoring.d.ts +0 -1326
- package/dist/protocol/dist/board-authoring.js +0 -302
- package/dist/protocol/dist/board-capability-registry.d.ts +0 -2
- package/dist/protocol/dist/board-capability-registry.js +0 -123
- package/dist/protocol/dist/board-codec.d.ts +0 -3
- package/dist/protocol/dist/board-codec.js +0 -383
- package/dist/protocol/dist/board-composition.d.ts +0 -383
- package/dist/protocol/dist/board-composition.js +0 -310
- package/dist/protocol/dist/board-connection.d.ts +0 -317
- package/dist/protocol/dist/board-connection.js +0 -219
- package/dist/protocol/dist/board-document.d.ts +0 -1566
- package/dist/protocol/dist/board-document.js +0 -456
- package/dist/protocol/dist/board-effect.d.ts +0 -96
- package/dist/protocol/dist/board-effect.js +0 -54
- package/dist/protocol/dist/board-geometry.d.ts +0 -15
- package/dist/protocol/dist/board-geometry.js +0 -137
- package/dist/protocol/dist/board-node.d.ts +0 -8
- package/dist/protocol/dist/board-node.js +0 -252
- /package/dist/protocol/dist/{board-animation.d.ts → host-bridge.d.ts} +0 -0
package/dist/chunks/http.js
CHANGED
|
@@ -1,894 +1,397 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { A as isUuid, f as getRealtimeBoardRoom, n as HttpTransport, p as getRealtimeSpaceRoom, t as HttpError } from "./transport.js";
|
|
2
2
|
import { l as resolveExecutionToken, o as resolveApiBaseUrl } from "./environment.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
-
//#region ../protocol/dist/
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
function
|
|
64
|
-
|
|
65
|
-
case "effects.deal": return { params: {
|
|
66
|
-
lift: { unit: "board" },
|
|
67
|
-
swing: { unit: "board" },
|
|
68
|
-
curve: { unit: "board" },
|
|
69
|
-
tilt: { unit: "degree" },
|
|
70
|
-
scale: { unit: "ratio" },
|
|
71
|
-
duration: { unit: "ms" },
|
|
72
|
-
landing: { unit: "ms" }
|
|
73
|
-
} };
|
|
74
|
-
default: return;
|
|
75
|
-
}
|
|
4
|
+
//#region ../protocol/dist/model/metrics.js
|
|
5
|
+
const count = z.number().finite().nonnegative();
|
|
6
|
+
const usageSchema = z.object({
|
|
7
|
+
input: count.optional(),
|
|
8
|
+
output: count.optional(),
|
|
9
|
+
cacheRead: count.optional(),
|
|
10
|
+
cacheWrite: count.optional(),
|
|
11
|
+
totalTokens: count.optional(),
|
|
12
|
+
cost: z.object({
|
|
13
|
+
input: count.optional(),
|
|
14
|
+
output: count.optional(),
|
|
15
|
+
cacheRead: count.optional(),
|
|
16
|
+
cacheWrite: count.optional(),
|
|
17
|
+
total: count.optional()
|
|
18
|
+
}).nullable().optional()
|
|
19
|
+
});
|
|
20
|
+
/** One provider attempt, independent of whether its response becomes a chat message. */
|
|
21
|
+
const requestMetricSchema = z.object({
|
|
22
|
+
id: z.string(),
|
|
23
|
+
provider: z.string(),
|
|
24
|
+
model: z.string(),
|
|
25
|
+
startedAt: count,
|
|
26
|
+
completedAt: count.optional(),
|
|
27
|
+
durationMs: count.optional(),
|
|
28
|
+
firstTokenMs: count.optional(),
|
|
29
|
+
outputDurationMs: count.optional(),
|
|
30
|
+
status: z.enum([
|
|
31
|
+
"running",
|
|
32
|
+
"completed",
|
|
33
|
+
"failed",
|
|
34
|
+
"interrupted"
|
|
35
|
+
]),
|
|
36
|
+
usage: usageSchema.nullable().optional(),
|
|
37
|
+
omitted: z.boolean().optional(),
|
|
38
|
+
imageToText: z.object({
|
|
39
|
+
calls: count,
|
|
40
|
+
durationMs: count,
|
|
41
|
+
usage: usageSchema.nullable(),
|
|
42
|
+
unknownUsageCalls: count
|
|
43
|
+
}).optional()
|
|
44
|
+
});
|
|
45
|
+
const toolMetricsSchema = z.object({
|
|
46
|
+
calls: count,
|
|
47
|
+
errors: count,
|
|
48
|
+
timedCalls: count,
|
|
49
|
+
activeMs: count.nullable()
|
|
50
|
+
});
|
|
51
|
+
z.object({
|
|
52
|
+
version: z.literal(1),
|
|
53
|
+
executionStartedAt: count.optional(),
|
|
54
|
+
requests: z.record(z.string(), requestMetricSchema).optional(),
|
|
55
|
+
retryWaitMs: count.optional(),
|
|
56
|
+
retryCount: count.optional(),
|
|
57
|
+
tools: toolMetricsSchema.optional(),
|
|
58
|
+
imageToText: z.object({
|
|
59
|
+
durationMs: count.nullable(),
|
|
60
|
+
timedCalls: count
|
|
61
|
+
}).optional()
|
|
62
|
+
});
|
|
63
|
+
function metricsRecord(value) {
|
|
64
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
76
65
|
}
|
|
77
|
-
const
|
|
78
|
-
|
|
66
|
+
const optionalCount = count.nullable();
|
|
67
|
+
/** Additive facts only. Rates are computed from matching samples, never averaged averages. */
|
|
68
|
+
const executionStatsSchema = z.object({
|
|
69
|
+
turns: count,
|
|
70
|
+
generations: count,
|
|
71
|
+
calls: optionalCount,
|
|
72
|
+
failedCalls: optionalCount,
|
|
73
|
+
unknownUsageCalls: count,
|
|
74
|
+
toolCalls: optionalCount,
|
|
75
|
+
toolErrors: optionalCount,
|
|
76
|
+
elapsedMs: optionalCount,
|
|
77
|
+
queueMs: optionalCount,
|
|
78
|
+
modelMs: optionalCount,
|
|
79
|
+
toolMs: optionalCount,
|
|
80
|
+
retryCount: optionalCount,
|
|
81
|
+
retryWaitMs: optionalCount,
|
|
82
|
+
ttftMs: count,
|
|
83
|
+
ttftSamples: count,
|
|
84
|
+
outputMs: count,
|
|
85
|
+
timedOutputTokens: count,
|
|
86
|
+
compactions: count,
|
|
87
|
+
compactionMs: optionalCount,
|
|
88
|
+
compactionUsage: usageSchema.nullable(),
|
|
89
|
+
imageToTextCalls: count,
|
|
90
|
+
imageToTextMs: optionalCount,
|
|
91
|
+
usage: usageSchema.nullable(),
|
|
92
|
+
modelCostUsd: optionalCount,
|
|
93
|
+
generationCostUsd: optionalCount,
|
|
94
|
+
/** Shared statistics never expose actual charges. */
|
|
95
|
+
chargedCostUsd: z.null().default(null),
|
|
96
|
+
partial: z.boolean()
|
|
97
|
+
});
|
|
98
|
+
const sessionStatsSchema = z.object({
|
|
99
|
+
version: z.literal(1),
|
|
100
|
+
updatedAt: z.string(),
|
|
101
|
+
revision: count,
|
|
102
|
+
own: executionStatsSchema,
|
|
103
|
+
inherited: executionStatsSchema,
|
|
104
|
+
auxiliaryUsage: usageSchema.nullable()
|
|
105
|
+
});
|
|
106
|
+
const normalizeExecutionStatsAliases = (value) => {
|
|
107
|
+
const record = metricsRecord(value);
|
|
108
|
+
const modelAlias = !Object.hasOwn(record, "modelCostUsd") && Object.hasOwn(record, "estimatedCostUsd");
|
|
109
|
+
const generationAlias = !Object.hasOwn(record, "generationCostUsd") && Object.hasOwn(record, "providerCostUsd");
|
|
110
|
+
if (!modelAlias && !generationAlias) return value;
|
|
79
111
|
return {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
renderers: ["webgpu", "webgl"],
|
|
84
|
-
...schema ? { schema } : {}
|
|
112
|
+
...record,
|
|
113
|
+
...modelAlias ? { modelCostUsd: record.estimatedCostUsd } : {},
|
|
114
|
+
...generationAlias ? { generationCostUsd: record.providerCostUsd } : {}
|
|
85
115
|
};
|
|
86
|
-
}
|
|
87
|
-
|
|
116
|
+
};
|
|
117
|
+
function sanitizeSessionStatsMeta(meta) {
|
|
118
|
+
const source = metricsRecord(meta);
|
|
119
|
+
if (!source.stats || typeof source.stats !== "object" || Array.isArray(source.stats)) return meta;
|
|
120
|
+
const stats = metricsRecord(source.stats);
|
|
121
|
+
const stripCharge = (value) => {
|
|
122
|
+
const record = metricsRecord(value);
|
|
123
|
+
return Object.hasOwn(record, "chargedCostUsd") && record.chargedCostUsd !== null ? {
|
|
124
|
+
...record,
|
|
125
|
+
chargedCostUsd: null
|
|
126
|
+
} : value;
|
|
127
|
+
};
|
|
128
|
+
const own = stripCharge(stats.own);
|
|
129
|
+
const inherited = stripCharge(stats.inherited);
|
|
130
|
+
if (own === stats.own && inherited === stats.inherited) return meta;
|
|
88
131
|
return {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
132
|
+
...source,
|
|
133
|
+
stats: {
|
|
134
|
+
...stats,
|
|
135
|
+
own,
|
|
136
|
+
inherited
|
|
137
|
+
}
|
|
94
138
|
};
|
|
95
|
-
}
|
|
139
|
+
}
|
|
140
|
+
function readSessionStats(meta) {
|
|
141
|
+
const raw = metricsRecord(sanitizeSessionStatsMeta(meta)).stats;
|
|
142
|
+
const record = metricsRecord(raw);
|
|
143
|
+
const own = normalizeExecutionStatsAliases(record.own);
|
|
144
|
+
const inherited = normalizeExecutionStatsAliases(record.inherited);
|
|
145
|
+
const normalized = own === record.own && inherited === record.inherited ? raw : {
|
|
146
|
+
...record,
|
|
147
|
+
own,
|
|
148
|
+
inherited
|
|
149
|
+
};
|
|
150
|
+
const result = sessionStatsSchema.safeParse(normalized);
|
|
151
|
+
return result.success ? result.data : null;
|
|
152
|
+
}
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region ../protocol/dist/display/index.js
|
|
96
155
|
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
* - `side` — the user pinned a side; `offset` (0..1) positions it along that edge.
|
|
103
|
-
* - `fixed` — the user pinned an exact normalized point on the frame.
|
|
156
|
+
* Displays: a machine's screens, shared with people over WebRTC and with
|
|
157
|
+
* agents as stills and input. Mirrored in Go by `apps/sandbox/display` and
|
|
158
|
+
* `apps/sandbox/rtc`, and in Kotlin by the Android display provider; the
|
|
159
|
+
* vocabulary and limits below are generated into both
|
|
160
|
+
* (`pnpm --filter @cohub/protocol generate:display`).
|
|
104
161
|
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
* first layout it happened to have.
|
|
162
|
+
* Coordinates are normalized to the display (0..1, origin top-left), so
|
|
163
|
+
* viewers and agents never need its pixel size to act on it.
|
|
108
164
|
*/
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
]
|
|
127
|
-
const
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
165
|
+
const unit = z.number().min(0).max(1);
|
|
166
|
+
const displayIdSchema = z.string().regex(/^[A-Za-z0-9._-]{1,64}$/);
|
|
167
|
+
const DISPLAY_DIMENSION_MAX = 16384;
|
|
168
|
+
const DISPLAY_SYSTEM_ACTIONS = [
|
|
169
|
+
"back",
|
|
170
|
+
"home",
|
|
171
|
+
"recents",
|
|
172
|
+
"notifications",
|
|
173
|
+
"quickSettings",
|
|
174
|
+
"lock"
|
|
175
|
+
];
|
|
176
|
+
const DISPLAY_PERMISSIONS = ["screenRecording", "accessibility"];
|
|
177
|
+
const DISPLAY_POINTER_ACTIONS = [
|
|
178
|
+
"down",
|
|
179
|
+
"move",
|
|
180
|
+
"up",
|
|
181
|
+
"cancel"
|
|
182
|
+
];
|
|
183
|
+
const DISPLAY_KEY_ACTIONS = [
|
|
184
|
+
"down",
|
|
185
|
+
"up",
|
|
186
|
+
"press"
|
|
187
|
+
];
|
|
188
|
+
const DISPLAY_BUTTONS = [
|
|
189
|
+
"primary",
|
|
190
|
+
"secondary",
|
|
191
|
+
"middle"
|
|
192
|
+
];
|
|
193
|
+
/** A list of known names; names a newer machine adds are dropped, so older clients keep working. */
|
|
194
|
+
const knownNames = (names, max) => z.array(z.string().max(32)).max(max).transform((values) => values.filter((value) => names.includes(value)));
|
|
195
|
+
const displayViewerSchema = z.object({
|
|
196
|
+
userId: z.string().min(1).max(64),
|
|
197
|
+
control: z.boolean().optional()
|
|
132
198
|
});
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
199
|
+
const displayInfoSchema = z.object({
|
|
200
|
+
id: displayIdSchema,
|
|
201
|
+
name: z.string().max(200),
|
|
202
|
+
width: z.number().int().min(0).max(DISPLAY_DIMENSION_MAX),
|
|
203
|
+
height: z.number().int().min(0).max(DISPLAY_DIMENSION_MAX),
|
|
204
|
+
stream: z.boolean(),
|
|
205
|
+
capture: z.boolean(),
|
|
206
|
+
input: z.boolean(),
|
|
207
|
+
system: knownNames(DISPLAY_SYSTEM_ACTIONS, 16).optional(),
|
|
208
|
+
desktop: z.boolean().optional(),
|
|
209
|
+
tree: z.boolean().optional(),
|
|
210
|
+
needs: knownNames(DISPLAY_PERMISSIONS, 8).optional(),
|
|
211
|
+
viewers: z.array(displayViewerSchema).max(16).optional()
|
|
212
|
+
});
|
|
213
|
+
z.object({ displays: z.array(displayInfoSchema).max(16) });
|
|
214
|
+
z.object({ size: z.string().regex(/^\d{3,4}x\d{3,4}$/).optional() });
|
|
215
|
+
const DISPLAY_ELEMENT_ROLES = [
|
|
216
|
+
"window",
|
|
217
|
+
"dialog",
|
|
218
|
+
"group",
|
|
219
|
+
"list",
|
|
220
|
+
"listItem",
|
|
221
|
+
"text",
|
|
222
|
+
"heading",
|
|
223
|
+
"image",
|
|
224
|
+
"button",
|
|
225
|
+
"link",
|
|
226
|
+
"textField",
|
|
227
|
+
"checkbox",
|
|
228
|
+
"radio",
|
|
229
|
+
"switch",
|
|
230
|
+
"slider",
|
|
231
|
+
"tab",
|
|
232
|
+
"menu",
|
|
233
|
+
"menuItem",
|
|
234
|
+
"web",
|
|
235
|
+
"other"
|
|
146
236
|
];
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
"
|
|
150
|
-
"
|
|
151
|
-
"
|
|
237
|
+
const DISPLAY_ELEMENT_STATES = [
|
|
238
|
+
"focused",
|
|
239
|
+
"selected",
|
|
240
|
+
"checked",
|
|
241
|
+
"disabled",
|
|
242
|
+
"editable",
|
|
243
|
+
"password",
|
|
244
|
+
"scrollable",
|
|
245
|
+
"expanded"
|
|
152
246
|
];
|
|
153
|
-
const
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
247
|
+
const DISPLAY_ELEMENT_ACTIONS = [
|
|
248
|
+
"click",
|
|
249
|
+
"longPress",
|
|
250
|
+
"focus",
|
|
251
|
+
"setText",
|
|
252
|
+
"scrollForward",
|
|
253
|
+
"scrollBackward"
|
|
254
|
+
];
|
|
255
|
+
const DISPLAY_ELEMENT_REF_PATTERN = /^e\d{1,9}\.\d{1,5}$/;
|
|
256
|
+
const DISPLAY_TREE_MAX_ELEMENTS = 1e3;
|
|
257
|
+
const displayElementSchema = z.object({
|
|
258
|
+
ref: z.string().regex(DISPLAY_ELEMENT_REF_PATTERN),
|
|
259
|
+
depth: z.number().int().min(0).max(64),
|
|
260
|
+
role: z.string().max(32).transform((role) => DISPLAY_ELEMENT_ROLES.includes(role) ? role : "other"),
|
|
261
|
+
name: z.string().max(500).optional(),
|
|
262
|
+
/** The current text of a field; never reported for passwords. */
|
|
263
|
+
value: z.string().max(500).optional(),
|
|
264
|
+
bounds: z.tuple([
|
|
265
|
+
unit,
|
|
266
|
+
unit,
|
|
267
|
+
unit,
|
|
268
|
+
unit
|
|
269
|
+
]),
|
|
270
|
+
states: knownNames(DISPLAY_ELEMENT_STATES, 16).optional(),
|
|
271
|
+
actions: knownNames(DISPLAY_ELEMENT_ACTIONS, 16).optional()
|
|
173
272
|
});
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
bend: z.number().finite().min(-.85).max(.85).default(0),
|
|
178
|
-
waypoints: z.array(BoardConnectionWaypointSchema).max(64).default([])
|
|
273
|
+
z.object({
|
|
274
|
+
maxElements: z.number().int().min(1).max(DISPLAY_TREE_MAX_ELEMENTS).optional(),
|
|
275
|
+
actionable: z.boolean().optional()
|
|
179
276
|
});
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
277
|
+
z.object({
|
|
278
|
+
width: z.number().int().min(0).max(DISPLAY_DIMENSION_MAX),
|
|
279
|
+
height: z.number().int().min(0).max(DISPLAY_DIMENSION_MAX),
|
|
280
|
+
elements: z.array(displayElementSchema).max(DISPLAY_TREE_MAX_ELEMENTS),
|
|
281
|
+
truncated: z.boolean().optional()
|
|
185
282
|
});
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
})
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
const jsonObjectSchema$2 = z.record(z.string(), z.unknown());
|
|
219
|
-
const BoardEasingSchema = z.enum([
|
|
220
|
-
"linear",
|
|
221
|
-
"ease-in-quad",
|
|
222
|
-
"ease-out-quad",
|
|
223
|
-
"ease-in-out-quad",
|
|
224
|
-
"ease-in-cubic",
|
|
225
|
-
"ease-out-cubic",
|
|
226
|
-
"ease-in-out-cubic",
|
|
227
|
-
"ease-out-quart",
|
|
228
|
-
"ease-out-expo"
|
|
229
|
-
]);
|
|
230
|
-
const BoardAnimationTargetSchema = z.discriminatedUnion("type", [
|
|
283
|
+
const DISPLAY_INPUT_MAX_TEXT = 4096;
|
|
284
|
+
const DISPLAY_INPUT_MAX_SCHEDULE_MS = 6e4;
|
|
285
|
+
const delta = z.number().min(-10).max(10);
|
|
286
|
+
const at = z.number().int().min(0).max(DISPLAY_INPUT_MAX_SCHEDULE_MS).optional();
|
|
287
|
+
const displayInputEventSchema = z.discriminatedUnion("type", [
|
|
288
|
+
z.object({
|
|
289
|
+
type: z.literal("pointer"),
|
|
290
|
+
action: z.enum(DISPLAY_POINTER_ACTIONS),
|
|
291
|
+
x: unit,
|
|
292
|
+
y: unit,
|
|
293
|
+
button: z.enum(DISPLAY_BUTTONS).optional(),
|
|
294
|
+
t: at
|
|
295
|
+
}),
|
|
296
|
+
z.object({
|
|
297
|
+
type: z.literal("scroll"),
|
|
298
|
+
x: unit,
|
|
299
|
+
y: unit,
|
|
300
|
+
dx: delta.optional(),
|
|
301
|
+
dy: delta.optional(),
|
|
302
|
+
t: at
|
|
303
|
+
}),
|
|
304
|
+
z.object({
|
|
305
|
+
type: z.literal("key"),
|
|
306
|
+
action: z.enum(DISPLAY_KEY_ACTIONS),
|
|
307
|
+
key: z.string().min(1).max(64),
|
|
308
|
+
t: at
|
|
309
|
+
}),
|
|
310
|
+
z.object({
|
|
311
|
+
type: z.literal("text"),
|
|
312
|
+
text: z.string().min(1).max(DISPLAY_INPUT_MAX_TEXT),
|
|
313
|
+
t: at
|
|
314
|
+
}),
|
|
231
315
|
z.object({
|
|
232
|
-
type: z.literal("
|
|
233
|
-
|
|
234
|
-
|
|
316
|
+
type: z.literal("system"),
|
|
317
|
+
action: z.enum(DISPLAY_SYSTEM_ACTIONS),
|
|
318
|
+
t: at
|
|
319
|
+
}),
|
|
235
320
|
z.object({
|
|
236
|
-
type: z.literal("
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
321
|
+
type: z.literal("element"),
|
|
322
|
+
ref: z.string().regex(DISPLAY_ELEMENT_REF_PATTERN),
|
|
323
|
+
action: z.enum(DISPLAY_ELEMENT_ACTIONS),
|
|
324
|
+
text: z.string().max(DISPLAY_INPUT_MAX_TEXT).optional(),
|
|
325
|
+
t: at
|
|
326
|
+
})
|
|
241
327
|
]);
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
const scaleSchema = z.union([finiteSchema$1.nonnegative(), z.object({
|
|
247
|
-
x: finiteSchema$1.nonnegative(),
|
|
248
|
-
y: finiteSchema$1.nonnegative()
|
|
249
|
-
}).strict()]);
|
|
250
|
-
const BOARD_ANIMATION_CHANNELS = {
|
|
251
|
-
"transform.translation": {
|
|
252
|
-
targets: ["item"],
|
|
253
|
-
value: vector2Schema,
|
|
254
|
-
interpolations: ["linear", "step"],
|
|
255
|
-
coordinateSpace: "world-offset",
|
|
256
|
-
unit: "board"
|
|
257
|
-
},
|
|
258
|
-
"transform.rotation": {
|
|
259
|
-
targets: ["item"],
|
|
260
|
-
value: finiteSchema$1,
|
|
261
|
-
interpolations: ["linear", "step"],
|
|
262
|
-
unit: "radian"
|
|
263
|
-
},
|
|
264
|
-
"transform.scale": {
|
|
265
|
-
targets: ["item"],
|
|
266
|
-
value: scaleSchema,
|
|
267
|
-
interpolations: ["linear", "step"],
|
|
268
|
-
unit: "ratio"
|
|
269
|
-
},
|
|
270
|
-
"style.opacity": {
|
|
271
|
-
targets: ["item"],
|
|
272
|
-
value: finiteSchema$1.min(0).max(1),
|
|
273
|
-
interpolations: ["linear", "step"],
|
|
274
|
-
unit: "ratio"
|
|
275
|
-
}
|
|
276
|
-
};
|
|
277
|
-
const BoardTrackKeyframeSchema = z.object({
|
|
278
|
-
time: finiteSchema$1.nonnegative(),
|
|
279
|
-
value: z.unknown(),
|
|
280
|
-
easing: BoardEasingSchema.optional()
|
|
281
|
-
}).strict();
|
|
282
|
-
const BoardTrackSchema = z.object({
|
|
283
|
-
id: idSchema$2,
|
|
284
|
-
target: BoardAnimationTargetSchema,
|
|
285
|
-
channel: z.string().min(1).max(160),
|
|
286
|
-
channelVersion: z.number().int().positive().default(1),
|
|
287
|
-
interpolation: z.enum(["linear", "step"]).default("linear"),
|
|
288
|
-
fill: z.enum([
|
|
289
|
-
"none",
|
|
290
|
-
"backwards",
|
|
291
|
-
"forwards",
|
|
292
|
-
"both"
|
|
293
|
-
]).default("none"),
|
|
294
|
-
keyframes: z.array(BoardTrackKeyframeSchema).min(1).max(1e5),
|
|
295
|
-
metadata: jsonObjectSchema$2.default({})
|
|
296
|
-
}).strict().superRefine((track, context) => {
|
|
297
|
-
let previous = -1;
|
|
298
|
-
for (const [index, keyframe] of track.keyframes.entries()) {
|
|
299
|
-
if (keyframe.time <= previous) context.addIssue({
|
|
300
|
-
code: "custom",
|
|
301
|
-
message: "keyframe times must be strictly increasing",
|
|
302
|
-
path: [
|
|
303
|
-
"keyframes",
|
|
304
|
-
index,
|
|
305
|
-
"time"
|
|
306
|
-
]
|
|
307
|
-
});
|
|
308
|
-
previous = keyframe.time;
|
|
309
|
-
}
|
|
310
|
-
const channel = BOARD_ANIMATION_CHANNELS[track.channel];
|
|
311
|
-
if (!channel) {
|
|
312
|
-
if (!/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+){2,}$/.test(track.channel)) context.addIssue({
|
|
313
|
-
code: "custom",
|
|
314
|
-
message: `unknown channel: ${track.channel}`,
|
|
315
|
-
path: ["channel"]
|
|
316
|
-
});
|
|
317
|
-
return;
|
|
318
|
-
}
|
|
319
|
-
if (!channel.targets.includes(track.target.type)) context.addIssue({
|
|
320
|
-
code: "custom",
|
|
321
|
-
message: `${track.channel} cannot target ${track.target.type}`,
|
|
322
|
-
path: ["target"]
|
|
323
|
-
});
|
|
324
|
-
if (!channel.interpolations.includes(track.interpolation)) context.addIssue({
|
|
325
|
-
code: "custom",
|
|
326
|
-
message: `${track.channel} does not support ${track.interpolation} interpolation`,
|
|
327
|
-
path: ["interpolation"]
|
|
328
|
-
});
|
|
329
|
-
for (const [index, keyframe] of track.keyframes.entries()) {
|
|
330
|
-
const parsed = channel.value.safeParse(keyframe.value);
|
|
331
|
-
if (!parsed.success) context.addIssue({
|
|
332
|
-
code: "custom",
|
|
333
|
-
message: parsed.error.issues[0]?.message ?? `invalid value for ${track.channel}`,
|
|
334
|
-
path: [
|
|
335
|
-
"keyframes",
|
|
336
|
-
index,
|
|
337
|
-
"value"
|
|
338
|
-
]
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
});
|
|
342
|
-
const extensionKindSchema = z.string().regex(/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)+$/).max(160);
|
|
343
|
-
const BoardProceduralClipSchema = z.object({
|
|
344
|
-
id: idSchema$2,
|
|
345
|
-
kind: extensionKindSchema,
|
|
346
|
-
kindVersion: z.number().int().positive(),
|
|
347
|
-
target: BoardAnimationTargetSchema,
|
|
348
|
-
start: finiteSchema$1.nonnegative(),
|
|
349
|
-
duration: finiteSchema$1.positive(),
|
|
350
|
-
layer: z.enum([
|
|
351
|
-
"behind",
|
|
352
|
-
"content",
|
|
353
|
-
"front",
|
|
354
|
-
"screen"
|
|
355
|
-
]).default("content"),
|
|
356
|
-
fill: z.enum([
|
|
357
|
-
"none",
|
|
358
|
-
"backwards",
|
|
359
|
-
"forwards",
|
|
360
|
-
"both"
|
|
361
|
-
]).default("none"),
|
|
362
|
-
easing: BoardEasingSchema.default("linear"),
|
|
363
|
-
params: jsonObjectSchema$2.default({}),
|
|
364
|
-
assetRefs: z.array(z.object({
|
|
365
|
-
type: z.enum(["space-file", "extension"]),
|
|
366
|
-
ref: z.string().min(1).max(4096),
|
|
367
|
-
digest: z.string().min(16).max(160).optional()
|
|
368
|
-
}).strict()).default([]),
|
|
369
|
-
seed: idSchema$2,
|
|
370
|
-
metadata: jsonObjectSchema$2.default({})
|
|
371
|
-
}).strict();
|
|
372
|
-
const BoardTimelineMarkerSchema = z.object({
|
|
373
|
-
id: idSchema$2,
|
|
374
|
-
time: finiteSchema$1.nonnegative(),
|
|
375
|
-
duration: finiteSchema$1.nonnegative().optional(),
|
|
376
|
-
metadata: jsonObjectSchema$2.default({})
|
|
377
|
-
}).strict();
|
|
378
|
-
const BoardTimelineSchema = z.object({
|
|
379
|
-
duration: finiteSchema$1.nonnegative(),
|
|
380
|
-
tracks: z.array(BoardTrackSchema).max(5e4).default([]),
|
|
381
|
-
clips: z.array(BoardProceduralClipSchema).max(5e4).default([]),
|
|
382
|
-
markers: z.array(BoardTimelineMarkerSchema).max(1e4).default([])
|
|
383
|
-
}).strict().superRefine((timeline, context) => {
|
|
384
|
-
const ids = /* @__PURE__ */ new Set();
|
|
385
|
-
const channels = /* @__PURE__ */ new Set();
|
|
386
|
-
for (const [index, track] of timeline.tracks.entries()) {
|
|
387
|
-
if (ids.has(track.id)) context.addIssue({
|
|
388
|
-
code: "custom",
|
|
389
|
-
message: `duplicate timeline id: ${track.id}`,
|
|
390
|
-
path: [
|
|
391
|
-
"tracks",
|
|
392
|
-
index,
|
|
393
|
-
"id"
|
|
394
|
-
]
|
|
395
|
-
});
|
|
396
|
-
ids.add(track.id);
|
|
397
|
-
const targetId = track.target.type === "item" ? track.target.itemId : track.target.type === "effect" ? track.target.effectId : track.target.type;
|
|
398
|
-
const key = `${track.target.type}:${targetId}:${track.channel}`;
|
|
399
|
-
if (channels.has(key)) context.addIssue({
|
|
328
|
+
z.object({ events: z.array(displayInputEventSchema).min(1).max(512) }).superRefine(({ events }, ctx) => {
|
|
329
|
+
let last = 0;
|
|
330
|
+
events.forEach((event, index) => {
|
|
331
|
+
if (event.type === "scroll" && !event.dx && !event.dy) ctx.addIssue({
|
|
400
332
|
code: "custom",
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
"tracks",
|
|
404
|
-
index,
|
|
405
|
-
"channel"
|
|
406
|
-
]
|
|
333
|
+
path: ["events", index],
|
|
334
|
+
message: "scroll needs dx or dy"
|
|
407
335
|
});
|
|
408
|
-
|
|
409
|
-
for (const [keyframeIndex, keyframe] of track.keyframes.entries()) if (keyframe.time > timeline.duration) context.addIssue({
|
|
336
|
+
if (event.type === "element" && event.action === "setText" !== (event.text !== void 0)) ctx.addIssue({
|
|
410
337
|
code: "custom",
|
|
411
|
-
message: "keyframe exceeds timeline duration",
|
|
412
338
|
path: [
|
|
413
|
-
"
|
|
339
|
+
"events",
|
|
414
340
|
index,
|
|
415
|
-
"
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
]
|
|
341
|
+
"text"
|
|
342
|
+
],
|
|
343
|
+
message: "text goes with setText only"
|
|
419
344
|
});
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
if (ids.has(clip.id)) context.addIssue({
|
|
345
|
+
if (event.t === void 0) return;
|
|
346
|
+
if (event.t < last) ctx.addIssue({
|
|
423
347
|
code: "custom",
|
|
424
|
-
message: `duplicate timeline id: ${clip.id}`,
|
|
425
348
|
path: [
|
|
426
|
-
"
|
|
349
|
+
"events",
|
|
427
350
|
index,
|
|
428
|
-
"
|
|
429
|
-
]
|
|
351
|
+
"t"
|
|
352
|
+
],
|
|
353
|
+
message: "t must ascend"
|
|
430
354
|
});
|
|
431
|
-
|
|
432
|
-
if (clip.start + clip.duration > timeline.duration) context.addIssue({
|
|
433
|
-
code: "custom",
|
|
434
|
-
message: "clip exceeds timeline duration",
|
|
435
|
-
path: [
|
|
436
|
-
"clips",
|
|
437
|
-
index,
|
|
438
|
-
"duration"
|
|
439
|
-
]
|
|
440
|
-
});
|
|
441
|
-
}
|
|
442
|
-
for (const [index, marker] of timeline.markers.entries()) {
|
|
443
|
-
if (ids.has(marker.id)) context.addIssue({
|
|
444
|
-
code: "custom",
|
|
445
|
-
message: `duplicate timeline id: ${marker.id}`,
|
|
446
|
-
path: [
|
|
447
|
-
"markers",
|
|
448
|
-
index,
|
|
449
|
-
"id"
|
|
450
|
-
]
|
|
451
|
-
});
|
|
452
|
-
ids.add(marker.id);
|
|
453
|
-
if (marker.time + (marker.duration ?? 0) > timeline.duration) context.addIssue({
|
|
454
|
-
code: "custom",
|
|
455
|
-
message: "marker exceeds timeline duration",
|
|
456
|
-
path: ["markers", index]
|
|
457
|
-
});
|
|
458
|
-
}
|
|
459
|
-
});
|
|
460
|
-
const BoardCompositionPlaybackSchema = z.object({
|
|
461
|
-
loop: z.boolean().default(false),
|
|
462
|
-
endBehavior: z.enum(["hold", "reset"]).default("hold"),
|
|
463
|
-
reducedMotion: z.discriminatedUnion("mode", [
|
|
464
|
-
z.object({ mode: z.literal("base") }).strict(),
|
|
465
|
-
z.object({
|
|
466
|
-
mode: z.literal("time"),
|
|
467
|
-
time: finiteSchema$1.nonnegative()
|
|
468
|
-
}).strict(),
|
|
469
|
-
z.object({
|
|
470
|
-
mode: z.literal("marker"),
|
|
471
|
-
markerId: idSchema$2
|
|
472
|
-
}).strict()
|
|
473
|
-
]).default({ mode: "base" })
|
|
474
|
-
}).strict();
|
|
475
|
-
const compositionFields = {
|
|
476
|
-
id: idSchema$2,
|
|
477
|
-
name: z.string().min(1).max(255),
|
|
478
|
-
timeline: BoardTimelineSchema,
|
|
479
|
-
playback: BoardCompositionPlaybackSchema.default({
|
|
480
|
-
loop: false,
|
|
481
|
-
endBehavior: "hold",
|
|
482
|
-
reducedMotion: { mode: "base" }
|
|
483
|
-
}),
|
|
484
|
-
metadata: jsonObjectSchema$2.default({})
|
|
485
|
-
};
|
|
486
|
-
function validateCompositionFallback(composition, context) {
|
|
487
|
-
const fallback = composition.playback.reducedMotion;
|
|
488
|
-
if (fallback.mode === "time" && fallback.time > composition.timeline.duration) context.addIssue({
|
|
489
|
-
code: "custom",
|
|
490
|
-
message: "reduced-motion time exceeds timeline duration",
|
|
491
|
-
path: [
|
|
492
|
-
"playback",
|
|
493
|
-
"reducedMotion",
|
|
494
|
-
"time"
|
|
495
|
-
]
|
|
496
|
-
});
|
|
497
|
-
if (fallback.mode === "marker" && !composition.timeline.markers.some((marker) => marker.id === fallback.markerId)) context.addIssue({
|
|
498
|
-
code: "custom",
|
|
499
|
-
message: `marker does not exist: ${fallback.markerId}`,
|
|
500
|
-
path: [
|
|
501
|
-
"playback",
|
|
502
|
-
"reducedMotion",
|
|
503
|
-
"markerId"
|
|
504
|
-
]
|
|
355
|
+
last = event.t;
|
|
505
356
|
});
|
|
357
|
+
});
|
|
358
|
+
z.object({
|
|
359
|
+
format: z.enum(["jpeg", "png"]).optional(),
|
|
360
|
+
quality: z.number().int().min(1).max(100).optional(),
|
|
361
|
+
maxSize: z.number().int().min(1).max(DISPLAY_DIMENSION_MAX).optional()
|
|
362
|
+
});
|
|
363
|
+
z.object({
|
|
364
|
+
urls: z.union([z.string().min(1).max(512), z.array(z.string().min(1).max(512)).min(1).max(16)]),
|
|
365
|
+
username: z.string().max(512).optional(),
|
|
366
|
+
credential: z.string().max(512).optional()
|
|
367
|
+
});
|
|
368
|
+
z.object({
|
|
369
|
+
offer: z.string().min(1).max(65536),
|
|
370
|
+
control: z.boolean().optional()
|
|
371
|
+
});
|
|
372
|
+
const DISPLAY_CONTROL_CHANNEL = "control";
|
|
373
|
+
const DISPLAY_INPUT_CHANNEL = "input";
|
|
374
|
+
const DISPLAY_PING_INTERVAL_MS = 5e3;
|
|
375
|
+
function normalizeDisplayPoint(x, y, width, height) {
|
|
376
|
+
const unit = (value, extent) => Math.min(1, Math.max(0, extent > 1 ? value / (extent - 1) : 0));
|
|
377
|
+
return {
|
|
378
|
+
x: unit(x, width),
|
|
379
|
+
y: unit(y, height)
|
|
380
|
+
};
|
|
506
381
|
}
|
|
507
|
-
|
|
508
|
-
const
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
* `revision`. Anything else that looks server-owned (or simply unknown) fails
|
|
515
|
-
* with a pointer at the offending key, so a get → edit → apply round-trip that
|
|
516
|
-
* accidentally carries runtime fields names the culprit instead of a bare
|
|
517
|
-
* "Unrecognized key".
|
|
518
|
-
*/
|
|
519
|
-
function parseBoardCompositionInput(value) {
|
|
520
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
521
|
-
const { revision: _revision, ...input } = value;
|
|
522
|
-
const result = BoardCompositionInputSchema.safeParse(input);
|
|
523
|
-
if (!result.success) {
|
|
524
|
-
const issue = result.error.issues.find((item) => item.code === "unrecognized_keys");
|
|
525
|
-
if (issue) throw new Error(`unknown field in composition input: ${issue.message}. Server-owned fields such as revision are stripped automatically; remove other fields not part of the composition schema`);
|
|
526
|
-
throw result.error;
|
|
382
|
+
function splitDisplayText(text, max = DISPLAY_INPUT_MAX_TEXT) {
|
|
383
|
+
const pieces = [];
|
|
384
|
+
let piece = "";
|
|
385
|
+
for (const char of text) {
|
|
386
|
+
if (piece.length + char.length > max) {
|
|
387
|
+
pieces.push(piece);
|
|
388
|
+
piece = "";
|
|
527
389
|
}
|
|
528
|
-
|
|
390
|
+
piece += char;
|
|
529
391
|
}
|
|
530
|
-
|
|
392
|
+
if (piece) pieces.push(piece);
|
|
393
|
+
return pieces;
|
|
531
394
|
}
|
|
532
|
-
const BOARD_ANIMATION_CHANNEL_CAPABILITIES = Object.entries(BOARD_ANIMATION_CHANNELS).map(([id, definition]) => ({
|
|
533
|
-
id,
|
|
534
|
-
version: 1,
|
|
535
|
-
targets: definition.targets,
|
|
536
|
-
interpolations: definition.interpolations,
|
|
537
|
-
..."coordinateSpace" in definition ? { coordinateSpace: definition.coordinateSpace } : {},
|
|
538
|
-
..."unit" in definition ? { unit: definition.unit } : {},
|
|
539
|
-
valueSchema: z.toJSONSchema(definition.value)
|
|
540
|
-
}));
|
|
541
|
-
//#endregion
|
|
542
|
-
//#region ../protocol/dist/board-effect.js
|
|
543
|
-
/**
|
|
544
|
-
* Board effect schema, isolated so `board-authoring.ts` can reuse the exact
|
|
545
|
-
* definition without a runtime cycle through `board.ts`. One definition, one
|
|
546
|
-
* strictness level — semantic mutations and Board create must not drift apart.
|
|
547
|
-
*/
|
|
548
|
-
const idSchema$1 = z.string().min(1).max(160);
|
|
549
|
-
const extensionIdSchema = z.string().regex(/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)+$/).max(160);
|
|
550
|
-
const jsonObjectSchema$1 = z.record(z.string(), z.unknown());
|
|
551
|
-
const BoardAssetRefSchema = z.object({
|
|
552
|
-
type: z.enum(["space-file", "extension"]),
|
|
553
|
-
ref: z.string().min(1).max(4096),
|
|
554
|
-
digest: z.string().min(16).max(160).optional()
|
|
555
|
-
}).strict();
|
|
556
|
-
const BoardEffectSchema = z.object({
|
|
557
|
-
id: idSchema$1,
|
|
558
|
-
boardId: z.string().uuid(),
|
|
559
|
-
target: z.discriminatedUnion("type", [z.object({
|
|
560
|
-
type: z.literal("item"),
|
|
561
|
-
itemId: idSchema$1
|
|
562
|
-
}).strict(), z.object({ type: z.literal("board") }).strict()]),
|
|
563
|
-
kind: extensionIdSchema,
|
|
564
|
-
kindVersion: z.number().int().positive().default(1),
|
|
565
|
-
enabled: z.boolean().default(true),
|
|
566
|
-
lifecycle: z.enum([
|
|
567
|
-
"persistent",
|
|
568
|
-
"when-visible",
|
|
569
|
-
"manual",
|
|
570
|
-
"on-enter"
|
|
571
|
-
]),
|
|
572
|
-
timeOrigin: z.enum([
|
|
573
|
-
"board",
|
|
574
|
-
"visible",
|
|
575
|
-
"activation"
|
|
576
|
-
]),
|
|
577
|
-
layer: z.enum([
|
|
578
|
-
"behind",
|
|
579
|
-
"front",
|
|
580
|
-
"screen"
|
|
581
|
-
]).default("front"),
|
|
582
|
-
seed: z.string().min(1).max(160),
|
|
583
|
-
params: jsonObjectSchema$1.default({}),
|
|
584
|
-
assetRefs: z.array(BoardAssetRefSchema).default([]),
|
|
585
|
-
metadata: jsonObjectSchema$1.default({}),
|
|
586
|
-
revision: z.number().int().nonnegative()
|
|
587
|
-
}).strict();
|
|
588
|
-
/** The client-authored effect form: no server-owned `boardId`/`revision`. */
|
|
589
|
-
const BoardEffectInputSchema = BoardEffectSchema.omit({
|
|
590
|
-
boardId: true,
|
|
591
|
-
revision: true
|
|
592
|
-
});
|
|
593
|
-
/** Accept a read projection in a write command while stripping server fields. */
|
|
594
|
-
function parseBoardEffectInput(value) {
|
|
595
|
-
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
596
|
-
const { boardId: _boardId, revision: _revision, ...input } = value;
|
|
597
|
-
return BoardEffectInputSchema.parse(input);
|
|
598
|
-
}
|
|
599
|
-
return BoardEffectInputSchema.parse(value);
|
|
600
|
-
}
|
|
601
|
-
//#endregion
|
|
602
|
-
//#region ../protocol/dist/board-authoring.js
|
|
603
|
-
const BOARD_AUTHORING_COLOR_IDS = [
|
|
604
|
-
"brand",
|
|
605
|
-
"neutral",
|
|
606
|
-
"black",
|
|
607
|
-
"white",
|
|
608
|
-
"blue",
|
|
609
|
-
"green",
|
|
610
|
-
"amber",
|
|
611
|
-
"violet",
|
|
612
|
-
"rose"
|
|
613
|
-
];
|
|
614
|
-
const BOARD_AUTHORING_GEO_KINDS = [
|
|
615
|
-
"rectangle",
|
|
616
|
-
"rounded",
|
|
617
|
-
"ellipse",
|
|
618
|
-
"diamond",
|
|
619
|
-
"triangle"
|
|
620
|
-
];
|
|
621
|
-
const idSchema = z.string().min(1).max(160);
|
|
622
|
-
const jsonObjectSchema = z.record(z.string(), z.unknown());
|
|
623
|
-
const finiteSchema = z.number().finite();
|
|
624
|
-
const extensionTypeSchema = z.string().regex(/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)+$/).max(160);
|
|
625
|
-
const BoardAuthoringPositionSchema = z.object({
|
|
626
|
-
x: finiteSchema,
|
|
627
|
-
y: finiteSchema
|
|
628
|
-
}).strict();
|
|
629
|
-
const BoardAuthoringSizeSchema = z.object({
|
|
630
|
-
width: finiteSchema.positive(),
|
|
631
|
-
height: finiteSchema.positive()
|
|
632
|
-
}).strict();
|
|
633
|
-
const BoardAuthoringPositionPatchSchema = z.object({
|
|
634
|
-
x: finiteSchema.optional(),
|
|
635
|
-
y: finiteSchema.optional()
|
|
636
|
-
}).strict();
|
|
637
|
-
const pointSchema = z.object({
|
|
638
|
-
x: finiteSchema,
|
|
639
|
-
y: finiteSchema,
|
|
640
|
-
p: finiteSchema.min(0).max(1).default(.5)
|
|
641
|
-
}).strict();
|
|
642
|
-
const worldPointSchema = z.object({
|
|
643
|
-
x: finiteSchema,
|
|
644
|
-
y: finiteSchema
|
|
645
|
-
}).strict();
|
|
646
|
-
const cropSchema = z.object({
|
|
647
|
-
x: finiteSchema.min(0).max(1),
|
|
648
|
-
y: finiteSchema.min(0).max(1),
|
|
649
|
-
w: finiteSchema.min(0).max(1),
|
|
650
|
-
h: finiteSchema.min(0).max(1)
|
|
651
|
-
}).strict();
|
|
652
|
-
const BoardItemStyleSchema = z.object({
|
|
653
|
-
color: z.enum(BOARD_AUTHORING_COLOR_IDS).optional(),
|
|
654
|
-
strokeWidth: finiteSchema.min(1).max(64).optional(),
|
|
655
|
-
fillOpacity: finiteSchema.min(0).max(1).optional()
|
|
656
|
-
}).strict();
|
|
657
|
-
const sourceSnapshotSchema = z.record(z.string(), z.unknown());
|
|
658
|
-
function isSafeBoardSourcePath(value) {
|
|
659
|
-
return value.length > 0 && value.length <= 4096 && !value.startsWith("/") && !value.startsWith("\\") && !value.includes("\\") && value.split("/").every((part) => part.length > 0 && part !== "." && part !== "..");
|
|
660
|
-
}
|
|
661
|
-
const BoardItemSourceSchema = z.object({
|
|
662
|
-
kind: z.literal("space-file"),
|
|
663
|
-
path: z.string().refine(isSafeBoardSourcePath, "source path must be a safe relative Board file path"),
|
|
664
|
-
snapshot: sourceSnapshotSchema.optional()
|
|
665
|
-
}).strict();
|
|
666
|
-
const sizedBaseFields = {
|
|
667
|
-
id: idSchema,
|
|
668
|
-
position: BoardAuthoringPositionSchema.default({
|
|
669
|
-
x: 0,
|
|
670
|
-
y: 0
|
|
671
|
-
}),
|
|
672
|
-
rotation: finiteSchema.default(0),
|
|
673
|
-
parentId: idSchema.nullable().optional(),
|
|
674
|
-
locked: z.boolean().optional(),
|
|
675
|
-
metadata: jsonObjectSchema.optional(),
|
|
676
|
-
size: BoardAuthoringSizeSchema.optional()
|
|
677
|
-
};
|
|
678
|
-
const styledBaseFields = {
|
|
679
|
-
...sizedBaseFields,
|
|
680
|
-
style: BoardItemStyleSchema.optional()
|
|
681
|
-
};
|
|
682
|
-
const { position: _positionField, size: _sizeField, ...strokeBaseFields } = sizedBaseFields;
|
|
683
|
-
const strokeStyledBaseFields = {
|
|
684
|
-
...strokeBaseFields,
|
|
685
|
-
rotation: z.literal(0).default(0),
|
|
686
|
-
style: BoardItemStyleSchema.optional()
|
|
687
|
-
};
|
|
688
|
-
const BoardTextAuthoringItemSchema = z.object({
|
|
689
|
-
...styledBaseFields,
|
|
690
|
-
type: z.literal("text"),
|
|
691
|
-
props: z.object({
|
|
692
|
-
text: z.string().default(""),
|
|
693
|
-
fontSize: finiteSchema.min(2).max(512).default(24)
|
|
694
|
-
}).strict()
|
|
695
|
-
}).strict();
|
|
696
|
-
const BoardGeoAuthoringItemSchema = z.object({
|
|
697
|
-
...styledBaseFields,
|
|
698
|
-
type: z.literal("geo"),
|
|
699
|
-
props: z.object({
|
|
700
|
-
shape: z.enum(BOARD_AUTHORING_GEO_KINDS).default("rectangle"),
|
|
701
|
-
text: z.string().default("")
|
|
702
|
-
}).strict()
|
|
703
|
-
}).strict();
|
|
704
|
-
const BoardDrawAuthoringItemSchema = z.object({
|
|
705
|
-
...strokeStyledBaseFields,
|
|
706
|
-
type: z.literal("draw"),
|
|
707
|
-
props: z.object({ points: z.array(pointSchema).min(1) }).strict()
|
|
708
|
-
}).strict();
|
|
709
|
-
const BoardArrowAuthoringItemSchema = z.object({
|
|
710
|
-
...strokeStyledBaseFields,
|
|
711
|
-
type: z.literal("arrow"),
|
|
712
|
-
props: z.object({
|
|
713
|
-
start: worldPointSchema,
|
|
714
|
-
end: worldPointSchema,
|
|
715
|
-
bend: finiteSchema.min(-.85).max(.85).default(0),
|
|
716
|
-
arrowStart: z.boolean().default(false),
|
|
717
|
-
arrowEnd: z.boolean().default(true),
|
|
718
|
-
label: z.string().default("")
|
|
719
|
-
}).strict()
|
|
720
|
-
}).strict();
|
|
721
|
-
const BoardFrameAuthoringItemSchema = z.object({
|
|
722
|
-
...styledBaseFields,
|
|
723
|
-
type: z.literal("frame"),
|
|
724
|
-
props: z.object({ label: z.string().default("Frame") }).strict()
|
|
725
|
-
}).strict();
|
|
726
|
-
const fileBackedFields = {
|
|
727
|
-
...sizedBaseFields,
|
|
728
|
-
style: z.object({}).strict().optional(),
|
|
729
|
-
source: BoardItemSourceSchema
|
|
730
|
-
};
|
|
731
|
-
const builtinItemSchemas = [
|
|
732
|
-
BoardTextAuthoringItemSchema,
|
|
733
|
-
BoardGeoAuthoringItemSchema,
|
|
734
|
-
BoardDrawAuthoringItemSchema,
|
|
735
|
-
BoardArrowAuthoringItemSchema,
|
|
736
|
-
BoardFrameAuthoringItemSchema,
|
|
737
|
-
z.object({
|
|
738
|
-
...fileBackedFields,
|
|
739
|
-
type: z.literal("image"),
|
|
740
|
-
props: z.object({ crop: cropSchema.optional() }).strict()
|
|
741
|
-
}).strict(),
|
|
742
|
-
z.object({
|
|
743
|
-
...fileBackedFields,
|
|
744
|
-
type: z.literal("video"),
|
|
745
|
-
props: z.object({}).strict()
|
|
746
|
-
}).strict(),
|
|
747
|
-
z.object({
|
|
748
|
-
...fileBackedFields,
|
|
749
|
-
type: z.literal("audio"),
|
|
750
|
-
props: z.object({}).strict()
|
|
751
|
-
}).strict(),
|
|
752
|
-
z.object({
|
|
753
|
-
...fileBackedFields,
|
|
754
|
-
type: z.literal("file"),
|
|
755
|
-
props: z.object({}).strict()
|
|
756
|
-
}).strict(),
|
|
757
|
-
z.object({
|
|
758
|
-
...sizedBaseFields,
|
|
759
|
-
type: z.literal("task"),
|
|
760
|
-
props: z.object({
|
|
761
|
-
taskRunId: z.string().min(1),
|
|
762
|
-
snapshot: z.record(z.string(), z.unknown())
|
|
763
|
-
}).strict(),
|
|
764
|
-
style: z.object({}).strict().optional()
|
|
765
|
-
}).strict()
|
|
766
|
-
];
|
|
767
|
-
const BoardBuiltinAuthoringItemSchema = z.discriminatedUnion("type", builtinItemSchemas);
|
|
768
|
-
const BoardExtensionAuthoringItemSchema = z.object({
|
|
769
|
-
...sizedBaseFields,
|
|
770
|
-
type: extensionTypeSchema,
|
|
771
|
-
kindVersion: z.number().int().positive(),
|
|
772
|
-
props: jsonObjectSchema,
|
|
773
|
-
style: jsonObjectSchema.optional(),
|
|
774
|
-
source: z.object({
|
|
775
|
-
kind: z.string().min(1).max(80),
|
|
776
|
-
ref: z.string().min(1).max(4096),
|
|
777
|
-
snapshot: jsonObjectSchema.optional()
|
|
778
|
-
}).strict().optional()
|
|
779
|
-
}).strict();
|
|
780
|
-
const BoardAuthoringItemSchema = z.union([BoardBuiltinAuthoringItemSchema, BoardExtensionAuthoringItemSchema]);
|
|
781
|
-
/** JSON Merge Patch semantics, constrained to the stable Item envelope. */
|
|
782
|
-
const BoardItemPatchSchema = z.object({
|
|
783
|
-
position: BoardAuthoringPositionPatchSchema.optional(),
|
|
784
|
-
size: BoardAuthoringSizeSchema.nullable().optional(),
|
|
785
|
-
rotation: finiteSchema.optional(),
|
|
786
|
-
parentId: idSchema.nullable().optional(),
|
|
787
|
-
locked: z.boolean().nullable().optional(),
|
|
788
|
-
props: jsonObjectSchema.optional(),
|
|
789
|
-
style: jsonObjectSchema.nullable().optional(),
|
|
790
|
-
source: jsonObjectSchema.nullable().optional(),
|
|
791
|
-
metadata: jsonObjectSchema.nullable().optional()
|
|
792
|
-
}).strict().refine((patch) => Object.keys(patch).length > 0, "item patch is empty");
|
|
793
|
-
const BoardConnectionInputSchema = BoardConnectionSchema;
|
|
794
|
-
const stripServerFields = (value) => {
|
|
795
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) return value;
|
|
796
|
-
const { boardId: _boardId, revision: _revision, ...input } = value;
|
|
797
|
-
return input;
|
|
798
|
-
};
|
|
799
|
-
const BoardEffectCommandInputSchema = z.preprocess(stripServerFields, BoardEffectInputSchema);
|
|
800
|
-
const BoardCompositionCommandInputSchema = z.preprocess(stripServerFields, BoardCompositionInputSchema);
|
|
801
|
-
const BoardSemanticCommandSchema = z.discriminatedUnion("type", [
|
|
802
|
-
z.object({
|
|
803
|
-
type: z.literal("board.patch"),
|
|
804
|
-
patch: z.object({
|
|
805
|
-
title: z.string().min(1).max(255).optional(),
|
|
806
|
-
metadata: jsonObjectSchema.nullable().optional(),
|
|
807
|
-
metadataPatch: jsonObjectSchema.optional()
|
|
808
|
-
}).strict().refine((patch) => Object.keys(patch).length > 0, "board patch is empty")
|
|
809
|
-
}).strict(),
|
|
810
|
-
z.object({
|
|
811
|
-
type: z.literal("item.create"),
|
|
812
|
-
item: BoardAuthoringItemSchema
|
|
813
|
-
}).strict(),
|
|
814
|
-
z.object({
|
|
815
|
-
type: z.literal("item.patch"),
|
|
816
|
-
itemId: idSchema,
|
|
817
|
-
patch: BoardItemPatchSchema
|
|
818
|
-
}).strict(),
|
|
819
|
-
z.object({
|
|
820
|
-
type: z.literal("item.replace"),
|
|
821
|
-
itemId: idSchema,
|
|
822
|
-
item: BoardAuthoringItemSchema
|
|
823
|
-
}).strict(),
|
|
824
|
-
z.object({
|
|
825
|
-
type: z.literal("item.delete"),
|
|
826
|
-
itemId: idSchema,
|
|
827
|
-
cascade: z.boolean().default(false)
|
|
828
|
-
}).strict(),
|
|
829
|
-
z.object({
|
|
830
|
-
type: z.literal("item.reorder"),
|
|
831
|
-
itemId: idSchema,
|
|
832
|
-
index: z.number().int().nonnegative()
|
|
833
|
-
}).strict(),
|
|
834
|
-
z.object({
|
|
835
|
-
type: z.literal("connection.create"),
|
|
836
|
-
connection: BoardConnectionInputSchema
|
|
837
|
-
}).strict(),
|
|
838
|
-
z.object({
|
|
839
|
-
type: z.literal("connection.patch"),
|
|
840
|
-
connectionId: idSchema,
|
|
841
|
-
patch: BoardConnectionPatchSchema
|
|
842
|
-
}).strict(),
|
|
843
|
-
z.object({
|
|
844
|
-
type: z.literal("connection.delete"),
|
|
845
|
-
connectionId: idSchema
|
|
846
|
-
}).strict(),
|
|
847
|
-
z.object({
|
|
848
|
-
type: z.literal("effect.apply"),
|
|
849
|
-
effect: BoardEffectCommandInputSchema
|
|
850
|
-
}).strict(),
|
|
851
|
-
z.object({
|
|
852
|
-
type: z.literal("effect.delete"),
|
|
853
|
-
effectId: idSchema
|
|
854
|
-
}).strict(),
|
|
855
|
-
z.object({
|
|
856
|
-
type: z.literal("composition.apply"),
|
|
857
|
-
composition: BoardCompositionCommandInputSchema
|
|
858
|
-
}).strict(),
|
|
859
|
-
z.object({
|
|
860
|
-
type: z.literal("composition.delete"),
|
|
861
|
-
compositionId: idSchema
|
|
862
|
-
}).strict()
|
|
863
|
-
]);
|
|
864
|
-
const BoardSemanticMutationSchema = z.object({
|
|
865
|
-
mutationId: idSchema,
|
|
866
|
-
baseVersion: z.number().int().nonnegative(),
|
|
867
|
-
clientId: idSchema.optional(),
|
|
868
|
-
undoGroupId: idSchema.optional(),
|
|
869
|
-
/** Validate (including server-side reference checks) without writing. */
|
|
870
|
-
dryRun: z.boolean().default(false),
|
|
871
|
-
commands: z.array(BoardSemanticCommandSchema).min(1).max(5e4)
|
|
872
|
-
}).strict();
|
|
873
|
-
z.object({
|
|
874
|
-
include: z.array(z.enum([
|
|
875
|
-
"items",
|
|
876
|
-
"connections",
|
|
877
|
-
"effects",
|
|
878
|
-
"compositions",
|
|
879
|
-
"playback"
|
|
880
|
-
])).optional(),
|
|
881
|
-
itemIds: z.array(idSchema).max(5e4).optional(),
|
|
882
|
-
connectionIds: z.array(idSchema).max(5e4).optional(),
|
|
883
|
-
effectIds: z.array(idSchema).max(5e4).optional(),
|
|
884
|
-
compositionIds: z.array(idSchema).max(5e4).optional(),
|
|
885
|
-
viewport: z.object({
|
|
886
|
-
x: finiteSchema,
|
|
887
|
-
y: finiteSchema,
|
|
888
|
-
width: finiteSchema.positive(),
|
|
889
|
-
height: finiteSchema.positive()
|
|
890
|
-
}).optional()
|
|
891
|
-
}).strict();
|
|
892
395
|
//#endregion
|
|
893
396
|
//#region ../protocol/dist/space-hooks.js
|
|
894
397
|
const SPACE_HOOK_WEBHOOK_SECRET_HEADER = "x-cohub-webhook-secret";
|
|
@@ -1472,14 +975,16 @@ var SearchApi = class {
|
|
|
1472
975
|
for (const type of input.types ?? []) params.append("type", type);
|
|
1473
976
|
if (input.spaceId) params.set("spaceId", input.spaceId);
|
|
1474
977
|
if (input.labelRef) params.set("labelRef", input.labelRef);
|
|
1475
|
-
if (input.groupTurns === false) params.set("groupTurns", "false");
|
|
1476
978
|
return this.transport.request(`/api/search?${params.toString()}`, { fetch: customFetch });
|
|
1477
979
|
}
|
|
1478
980
|
/** Default (empty-query) command palette data: viewer-relative space signals. */
|
|
1479
981
|
overview(input, customFetch) {
|
|
1480
982
|
const params = new URLSearchParams();
|
|
1481
983
|
if (input?.spaceLimit !== void 0) params.set("spaceLimit", String(input.spaceLimit));
|
|
1482
|
-
|
|
984
|
+
for (const recent of input?.recentSpaces ?? []) {
|
|
985
|
+
params.append("recentSpaceId", recent.id);
|
|
986
|
+
params.append("recentSpaceAt", new Date(recent.timestamp).toISOString());
|
|
987
|
+
}
|
|
1483
988
|
for (const spaceId of input?.recentSpaceIds ?? []) params.append("recentSpaceId", spaceId);
|
|
1484
989
|
const query = params.toString();
|
|
1485
990
|
return this.transport.request(`/api/palette/overview${query ? `?${query}` : ""}`, { fetch: customFetch });
|
|
@@ -1546,6 +1051,354 @@ function ensureRealtimeConnected(websocketClient) {
|
|
|
1546
1051
|
});
|
|
1547
1052
|
}
|
|
1548
1053
|
//#endregion
|
|
1054
|
+
//#region src/display-stats.ts
|
|
1055
|
+
const num = (value) => typeof value === "number" && Number.isFinite(value) ? value : null;
|
|
1056
|
+
function perFrame(seconds, previousSeconds, frames, previousFrames) {
|
|
1057
|
+
const count = frames - previousFrames;
|
|
1058
|
+
return count > 0 ? Math.round((seconds - previousSeconds) * 1e3 / count) : null;
|
|
1059
|
+
}
|
|
1060
|
+
function readDisplayStats(report, previous, fallbackRttMs = null) {
|
|
1061
|
+
const stats = {
|
|
1062
|
+
path: null,
|
|
1063
|
+
relayProtocol: null,
|
|
1064
|
+
rttMs: fallbackRttMs,
|
|
1065
|
+
fps: null,
|
|
1066
|
+
bitrate: null,
|
|
1067
|
+
width: null,
|
|
1068
|
+
height: null,
|
|
1069
|
+
jitterBufferMs: null,
|
|
1070
|
+
decodeMs: null,
|
|
1071
|
+
packetsLost: null,
|
|
1072
|
+
freezes: null
|
|
1073
|
+
};
|
|
1074
|
+
let counters = null;
|
|
1075
|
+
report.forEach((entry) => {
|
|
1076
|
+
if (entry.type === "candidate-pair" && entry.nominated && entry.state === "succeeded") {
|
|
1077
|
+
const local = report.get(String(entry.localCandidateId));
|
|
1078
|
+
const remote = report.get(String(entry.remoteCandidateId));
|
|
1079
|
+
stats.path = local?.candidateType === "relay" || remote?.candidateType === "relay" ? "relay" : "direct";
|
|
1080
|
+
if (local?.candidateType === "relay" && typeof local.relayProtocol === "string") stats.relayProtocol = local.relayProtocol;
|
|
1081
|
+
const rtt = num(entry.currentRoundTripTime);
|
|
1082
|
+
if (rtt !== null) stats.rttMs = Math.round(rtt * 1e3);
|
|
1083
|
+
}
|
|
1084
|
+
if (entry.type === "inbound-rtp" && entry.kind === "video") {
|
|
1085
|
+
stats.fps = num(entry.framesPerSecond);
|
|
1086
|
+
stats.width = num(entry.frameWidth);
|
|
1087
|
+
stats.height = num(entry.frameHeight);
|
|
1088
|
+
stats.packetsLost = num(entry.packetsLost);
|
|
1089
|
+
stats.freezes = num(entry.freezeCount);
|
|
1090
|
+
counters = {
|
|
1091
|
+
at: num(entry.timestamp) ?? 0,
|
|
1092
|
+
bytes: num(entry.bytesReceived) ?? 0,
|
|
1093
|
+
jitterBufferDelay: num(entry.jitterBufferDelay) ?? 0,
|
|
1094
|
+
jitterBufferEmitted: num(entry.jitterBufferEmittedCount) ?? 0,
|
|
1095
|
+
decodeTime: num(entry.totalDecodeTime) ?? 0,
|
|
1096
|
+
decoded: num(entry.framesDecoded) ?? 0
|
|
1097
|
+
};
|
|
1098
|
+
if (previous && counters.at > previous.at) {
|
|
1099
|
+
stats.bitrate = Math.round((counters.bytes - previous.bytes) * 8 * 1e3 / (counters.at - previous.at));
|
|
1100
|
+
stats.jitterBufferMs = perFrame(counters.jitterBufferDelay, previous.jitterBufferDelay, counters.jitterBufferEmitted, previous.jitterBufferEmitted);
|
|
1101
|
+
stats.decodeMs = perFrame(counters.decodeTime, previous.decodeTime, counters.decoded, previous.decoded);
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
});
|
|
1105
|
+
return {
|
|
1106
|
+
stats,
|
|
1107
|
+
counters
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
1110
|
+
//#endregion
|
|
1111
|
+
//#region src/display-session.ts
|
|
1112
|
+
const DEFAULT_GATHER_TIMEOUT_MS = 2e3;
|
|
1113
|
+
const DEFAULT_CONNECT_TIMEOUT_MS = 2e4;
|
|
1114
|
+
const INPUT_BACKLOG_BYTES = 65536;
|
|
1115
|
+
var DisplayConnection = class DisplayConnection {
|
|
1116
|
+
api;
|
|
1117
|
+
displayId;
|
|
1118
|
+
control;
|
|
1119
|
+
pc;
|
|
1120
|
+
stream = new MediaStream();
|
|
1121
|
+
currentState = "connecting";
|
|
1122
|
+
currentDisplay = null;
|
|
1123
|
+
reason = null;
|
|
1124
|
+
sessionId = null;
|
|
1125
|
+
listeners = /* @__PURE__ */ new Map();
|
|
1126
|
+
controlChannel;
|
|
1127
|
+
inputChannel;
|
|
1128
|
+
pingTimer = null;
|
|
1129
|
+
connectTimer = null;
|
|
1130
|
+
pingId = 0;
|
|
1131
|
+
lastPingAt = 0;
|
|
1132
|
+
rtt = null;
|
|
1133
|
+
counters = null;
|
|
1134
|
+
isPaused = false;
|
|
1135
|
+
constructor(api, displayId, control, pc) {
|
|
1136
|
+
this.api = api;
|
|
1137
|
+
this.displayId = displayId;
|
|
1138
|
+
this.control = control;
|
|
1139
|
+
this.pc = pc;
|
|
1140
|
+
pc.addTransceiver("video", { direction: "recvonly" });
|
|
1141
|
+
pc.ontrack = (event) => {
|
|
1142
|
+
const receiver = event.receiver;
|
|
1143
|
+
if ("jitterBufferTarget" in receiver) receiver.jitterBufferTarget = 0;
|
|
1144
|
+
this.stream.addTrack(event.track);
|
|
1145
|
+
};
|
|
1146
|
+
pc.onconnectionstatechange = () => {
|
|
1147
|
+
if (pc.connectionState === "connected") this.setState("connected");
|
|
1148
|
+
else if (pc.connectionState === "failed") this.close("failed");
|
|
1149
|
+
};
|
|
1150
|
+
this.controlChannel = pc.createDataChannel(DISPLAY_CONTROL_CHANNEL);
|
|
1151
|
+
this.controlChannel.onmessage = (event) => this.onControl(event.data);
|
|
1152
|
+
this.controlChannel.onopen = () => {
|
|
1153
|
+
if (this.isPaused) this.sendControl({ type: "pause" });
|
|
1154
|
+
this.startPings();
|
|
1155
|
+
};
|
|
1156
|
+
this.controlChannel.onclose = () => this.close(this.reason ?? "closed");
|
|
1157
|
+
this.inputChannel = control ? pc.createDataChannel(DISPLAY_INPUT_CHANNEL) : null;
|
|
1158
|
+
}
|
|
1159
|
+
static async open(api, displayId, options = {}) {
|
|
1160
|
+
const { iceServers } = await api.iceServers({ signal: options.signal });
|
|
1161
|
+
const pc = new RTCPeerConnection({
|
|
1162
|
+
iceServers,
|
|
1163
|
+
bundlePolicy: "max-bundle"
|
|
1164
|
+
});
|
|
1165
|
+
const connection = new DisplayConnection(api, displayId, options.control ?? true, pc);
|
|
1166
|
+
try {
|
|
1167
|
+
await pc.setLocalDescription(await pc.createOffer());
|
|
1168
|
+
await gathered(pc, options.gatherTimeoutMs ?? DEFAULT_GATHER_TIMEOUT_MS, options.signal);
|
|
1169
|
+
const offer = pc.localDescription?.sdp;
|
|
1170
|
+
if (!offer) throw new Error("The browser produced no offer");
|
|
1171
|
+
const session = await api.openSession(displayId, {
|
|
1172
|
+
offer,
|
|
1173
|
+
control: connection.control
|
|
1174
|
+
}, { signal: options.signal });
|
|
1175
|
+
connection.sessionId = session.sessionId;
|
|
1176
|
+
await pc.setRemoteDescription({
|
|
1177
|
+
type: "answer",
|
|
1178
|
+
sdp: session.answer
|
|
1179
|
+
});
|
|
1180
|
+
connection.connectTimer = setTimeout(() => connection.close("connect_timeout"), options.connectTimeoutMs ?? DEFAULT_CONNECT_TIMEOUT_MS);
|
|
1181
|
+
return connection;
|
|
1182
|
+
} catch (error) {
|
|
1183
|
+
connection.close("failed");
|
|
1184
|
+
throw error;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
get state() {
|
|
1188
|
+
return this.currentState;
|
|
1189
|
+
}
|
|
1190
|
+
get display() {
|
|
1191
|
+
return this.currentDisplay;
|
|
1192
|
+
}
|
|
1193
|
+
get closeReason() {
|
|
1194
|
+
return this.currentState === "closed" ? this.reason : null;
|
|
1195
|
+
}
|
|
1196
|
+
on(event, listener) {
|
|
1197
|
+
let set = this.listeners.get(event);
|
|
1198
|
+
if (!set) {
|
|
1199
|
+
set = /* @__PURE__ */ new Set();
|
|
1200
|
+
this.listeners.set(event, set);
|
|
1201
|
+
}
|
|
1202
|
+
set.add(listener);
|
|
1203
|
+
return () => set.delete(listener);
|
|
1204
|
+
}
|
|
1205
|
+
emit(event, value) {
|
|
1206
|
+
for (const listener of this.listeners.get(event) ?? []) listener(value);
|
|
1207
|
+
}
|
|
1208
|
+
setState(state) {
|
|
1209
|
+
if (this.currentState === state || this.currentState === "closed") return;
|
|
1210
|
+
this.currentState = state;
|
|
1211
|
+
if (state === "connected" && this.connectTimer) {
|
|
1212
|
+
clearTimeout(this.connectTimer);
|
|
1213
|
+
this.connectTimer = null;
|
|
1214
|
+
}
|
|
1215
|
+
this.emit("state", state);
|
|
1216
|
+
}
|
|
1217
|
+
sendInput(events) {
|
|
1218
|
+
const channel = this.inputChannel;
|
|
1219
|
+
if (channel?.readyState !== "open" || events.length === 0) return false;
|
|
1220
|
+
if (channel.bufferedAmount > INPUT_BACKLOG_BYTES && events.every((event) => event.type === "pointer" && event.action === "move")) return false;
|
|
1221
|
+
channel.send(JSON.stringify({ events }));
|
|
1222
|
+
return true;
|
|
1223
|
+
}
|
|
1224
|
+
requestKeyframe() {
|
|
1225
|
+
this.sendControl({ type: "keyframe" });
|
|
1226
|
+
}
|
|
1227
|
+
get paused() {
|
|
1228
|
+
return this.isPaused;
|
|
1229
|
+
}
|
|
1230
|
+
pause() {
|
|
1231
|
+
if (this.isPaused) return;
|
|
1232
|
+
this.isPaused = true;
|
|
1233
|
+
this.sendControl({ type: "pause" });
|
|
1234
|
+
}
|
|
1235
|
+
resume() {
|
|
1236
|
+
if (!this.isPaused) return;
|
|
1237
|
+
this.isPaused = false;
|
|
1238
|
+
this.counters = null;
|
|
1239
|
+
this.sendControl({ type: "resume" });
|
|
1240
|
+
}
|
|
1241
|
+
async stats() {
|
|
1242
|
+
const { stats, counters } = readDisplayStats(await this.pc.getStats(), this.counters, this.rtt);
|
|
1243
|
+
this.counters = counters;
|
|
1244
|
+
return stats;
|
|
1245
|
+
}
|
|
1246
|
+
close(reason = "closed") {
|
|
1247
|
+
if (this.currentState === "closed") return;
|
|
1248
|
+
this.reason = reason;
|
|
1249
|
+
if (this.pingTimer) clearInterval(this.pingTimer);
|
|
1250
|
+
if (this.connectTimer) clearTimeout(this.connectTimer);
|
|
1251
|
+
if (this.sessionId) this.api.closeSession(this.displayId, this.sessionId).catch(() => void 0);
|
|
1252
|
+
this.sessionId = null;
|
|
1253
|
+
for (const track of this.stream.getTracks()) track.stop();
|
|
1254
|
+
this.pc.close();
|
|
1255
|
+
this.currentState = "closed";
|
|
1256
|
+
this.emit("state", "closed");
|
|
1257
|
+
}
|
|
1258
|
+
startPings() {
|
|
1259
|
+
const ping = () => {
|
|
1260
|
+
this.pingId += 1;
|
|
1261
|
+
this.lastPingAt = performance.now();
|
|
1262
|
+
this.sendControl({
|
|
1263
|
+
type: "ping",
|
|
1264
|
+
id: this.pingId
|
|
1265
|
+
});
|
|
1266
|
+
};
|
|
1267
|
+
ping();
|
|
1268
|
+
this.pingTimer = setInterval(ping, DISPLAY_PING_INTERVAL_MS);
|
|
1269
|
+
}
|
|
1270
|
+
sendControl(message) {
|
|
1271
|
+
if (this.controlChannel.readyState === "open") this.controlChannel.send(JSON.stringify(message));
|
|
1272
|
+
}
|
|
1273
|
+
onControl(data) {
|
|
1274
|
+
if (typeof data !== "string") return;
|
|
1275
|
+
let message;
|
|
1276
|
+
try {
|
|
1277
|
+
message = JSON.parse(data);
|
|
1278
|
+
} catch {
|
|
1279
|
+
return;
|
|
1280
|
+
}
|
|
1281
|
+
switch (message.type) {
|
|
1282
|
+
case "ready":
|
|
1283
|
+
case "display":
|
|
1284
|
+
this.currentDisplay = message.display;
|
|
1285
|
+
this.emit("display", message.display);
|
|
1286
|
+
break;
|
|
1287
|
+
case "pong":
|
|
1288
|
+
if (message.id === this.pingId) this.rtt = Math.round(performance.now() - this.lastPingAt);
|
|
1289
|
+
break;
|
|
1290
|
+
case "error":
|
|
1291
|
+
this.emit("error", {
|
|
1292
|
+
code: message.code,
|
|
1293
|
+
message: message.message
|
|
1294
|
+
});
|
|
1295
|
+
break;
|
|
1296
|
+
case "closed":
|
|
1297
|
+
this.sessionId = null;
|
|
1298
|
+
this.close(message.reason);
|
|
1299
|
+
}
|
|
1300
|
+
}
|
|
1301
|
+
};
|
|
1302
|
+
function gathered(pc, timeoutMs, signal) {
|
|
1303
|
+
if (pc.iceGatheringState === "complete") return Promise.resolve();
|
|
1304
|
+
return new Promise((resolve, reject) => {
|
|
1305
|
+
const done = () => {
|
|
1306
|
+
clearTimeout(timer);
|
|
1307
|
+
pc.removeEventListener("icegatheringstatechange", check);
|
|
1308
|
+
signal?.removeEventListener("abort", abort);
|
|
1309
|
+
resolve();
|
|
1310
|
+
};
|
|
1311
|
+
const check = () => {
|
|
1312
|
+
if (pc.iceGatheringState === "complete") done();
|
|
1313
|
+
};
|
|
1314
|
+
const abort = () => {
|
|
1315
|
+
clearTimeout(timer);
|
|
1316
|
+
pc.removeEventListener("icegatheringstatechange", check);
|
|
1317
|
+
reject(signal?.reason ?? new DOMException("Aborted", "AbortError"));
|
|
1318
|
+
};
|
|
1319
|
+
const timer = setTimeout(done, timeoutMs);
|
|
1320
|
+
pc.addEventListener("icegatheringstatechange", check);
|
|
1321
|
+
signal?.addEventListener("abort", abort, { once: true });
|
|
1322
|
+
});
|
|
1323
|
+
}
|
|
1324
|
+
function connectDisplay(api, displayId, options) {
|
|
1325
|
+
return DisplayConnection.open(api, displayId, options);
|
|
1326
|
+
}
|
|
1327
|
+
//#endregion
|
|
1328
|
+
//#region src/apis/displays.ts
|
|
1329
|
+
const ICE_REFRESH_MARGIN_MS = 3e5;
|
|
1330
|
+
var SpaceDisplaysApi = class {
|
|
1331
|
+
transport;
|
|
1332
|
+
spaceId;
|
|
1333
|
+
ice = null;
|
|
1334
|
+
constructor(transport, spaceId) {
|
|
1335
|
+
this.transport = transport;
|
|
1336
|
+
this.spaceId = spaceId;
|
|
1337
|
+
}
|
|
1338
|
+
path(displayId, suffix = "") {
|
|
1339
|
+
return `/api/spaces/${this.spaceId}/displays/${encodeURIComponent(displayId)}${suffix}`;
|
|
1340
|
+
}
|
|
1341
|
+
list(options = {}) {
|
|
1342
|
+
return this.transport.request(`/api/spaces/${this.spaceId}/displays`, { signal: options.signal });
|
|
1343
|
+
}
|
|
1344
|
+
startVirtual(params = {}) {
|
|
1345
|
+
return this.transport.request(`/api/spaces/${this.spaceId}/displays/virtual`, {
|
|
1346
|
+
method: "POST",
|
|
1347
|
+
headers: { "Content-Type": "application/json" },
|
|
1348
|
+
body: JSON.stringify(params)
|
|
1349
|
+
});
|
|
1350
|
+
}
|
|
1351
|
+
stopVirtual() {
|
|
1352
|
+
return this.transport.request(`/api/spaces/${this.spaceId}/displays/virtual`, { method: "DELETE" });
|
|
1353
|
+
}
|
|
1354
|
+
tree(displayId, params = {}, options = {}) {
|
|
1355
|
+
const query = new URLSearchParams();
|
|
1356
|
+
if (params.maxElements) query.set("maxElements", String(params.maxElements));
|
|
1357
|
+
if (params.actionable) query.set("actionable", "true");
|
|
1358
|
+
const search = query.size ? `?${query}` : "";
|
|
1359
|
+
return this.transport.request(this.path(displayId, `/tree${search}`), { signal: options.signal });
|
|
1360
|
+
}
|
|
1361
|
+
capture(displayId, params = {}, options = {}) {
|
|
1362
|
+
const query = new URLSearchParams();
|
|
1363
|
+
if (params.format) query.set("format", params.format);
|
|
1364
|
+
if (params.quality) query.set("quality", String(params.quality));
|
|
1365
|
+
if (params.maxSize) query.set("maxSize", String(params.maxSize));
|
|
1366
|
+
const search = query.size ? `?${query}` : "";
|
|
1367
|
+
return this.transport.request(this.path(displayId, `/capture${search}`), { signal: options.signal });
|
|
1368
|
+
}
|
|
1369
|
+
input(displayId, events, options = {}) {
|
|
1370
|
+
return this.transport.request(this.path(displayId, "/input"), {
|
|
1371
|
+
method: "POST",
|
|
1372
|
+
headers: { "Content-Type": "application/json" },
|
|
1373
|
+
body: JSON.stringify({ events }),
|
|
1374
|
+
signal: options.signal
|
|
1375
|
+
});
|
|
1376
|
+
}
|
|
1377
|
+
async iceServers(options = {}) {
|
|
1378
|
+
if (this.ice && this.ice.refreshAt > Date.now()) return this.ice.value;
|
|
1379
|
+
const value = await this.transport.request(`/api/spaces/${this.spaceId}/rtc/ice-servers`, { signal: options.signal });
|
|
1380
|
+
this.ice = {
|
|
1381
|
+
value,
|
|
1382
|
+
refreshAt: Date.parse(value.expiresAt) - ICE_REFRESH_MARGIN_MS
|
|
1383
|
+
};
|
|
1384
|
+
return value;
|
|
1385
|
+
}
|
|
1386
|
+
connect(displayId, options) {
|
|
1387
|
+
return connectDisplay(this, displayId, options);
|
|
1388
|
+
}
|
|
1389
|
+
openSession(displayId, body, options = {}) {
|
|
1390
|
+
return this.transport.request(this.path(displayId, "/sessions"), {
|
|
1391
|
+
method: "POST",
|
|
1392
|
+
headers: { "Content-Type": "application/json" },
|
|
1393
|
+
body: JSON.stringify(body),
|
|
1394
|
+
signal: options.signal
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
closeSession(displayId, sessionId) {
|
|
1398
|
+
return this.transport.request(this.path(displayId, `/sessions/${sessionId}`), { method: "DELETE" });
|
|
1399
|
+
}
|
|
1400
|
+
};
|
|
1401
|
+
//#endregion
|
|
1549
1402
|
//#region src/session-patch-reducer.ts
|
|
1550
1403
|
const blockSubPathPattern = /^\/message\/content\/blocks\/(\d+)\/(.+)$/;
|
|
1551
1404
|
const blockPathPattern = /^\/message\/content\/blocks\/(\d+)$/;
|
|
@@ -2165,6 +2018,9 @@ var SessionGenerationStreamClient = class SessionGenerationStreamClient {
|
|
|
2165
2018
|
messageOrdinal = null;
|
|
2166
2019
|
intermediateMessages = [];
|
|
2167
2020
|
patchState = null;
|
|
2021
|
+
recovery = null;
|
|
2022
|
+
resyncedMessageKey = null;
|
|
2023
|
+
disposed = false;
|
|
2168
2024
|
constructor(websocketClient, spaceId, sessionId, fetchStreamSnapshot) {
|
|
2169
2025
|
this.websocketClient = websocketClient;
|
|
2170
2026
|
this.spaceId = spaceId;
|
|
@@ -2175,48 +2031,80 @@ var SessionGenerationStreamClient = class SessionGenerationStreamClient {
|
|
|
2175
2031
|
if (!this.websocketClient) throw new Error("realtime transport is not configured for this client");
|
|
2176
2032
|
ensureRealtimeConnected(this.websocketClient);
|
|
2177
2033
|
const stream = new SessionGenerationStreamClient(this.websocketClient, this.spaceId, this.sessionId, this.fetchStreamSnapshot);
|
|
2178
|
-
const shouldRecover = options.recover === true || options.initialSnapshot !== void 0;
|
|
2179
|
-
let recovering = shouldRecover;
|
|
2180
|
-
let disposed = false;
|
|
2181
|
-
let recoveryAborted = false;
|
|
2182
|
-
let bufferedEvents = [];
|
|
2183
|
-
let bufferedEventsReplayed = false;
|
|
2184
2034
|
const releaseRoom = this.websocketClient.retainRooms([getRealtimeSpaceRoom(this.spaceId)]);
|
|
2185
2035
|
const unsubscribe = this.websocketClient.on("event", (event) => {
|
|
2186
2036
|
if (event.spaceId !== this.spaceId || event.sessionId !== this.sessionId) return;
|
|
2187
|
-
|
|
2188
|
-
bufferedEvents.push(event);
|
|
2189
|
-
if (bufferedEvents.length > SNAPSHOT_RECOVERY_MAX_BUFFERED_EVENTS) {
|
|
2190
|
-
recoveryAborted = true;
|
|
2191
|
-
recovering = false;
|
|
2192
|
-
stream.replayBufferedEvents(bufferedEvents, handlers);
|
|
2193
|
-
bufferedEvents = [];
|
|
2194
|
-
bufferedEventsReplayed = true;
|
|
2195
|
-
}
|
|
2196
|
-
return;
|
|
2197
|
-
}
|
|
2198
|
-
stream.handleEvent(event, handlers);
|
|
2199
|
-
});
|
|
2200
|
-
if (shouldRecover) stream.recoverFromSnapshot(handlers, options, () => !recoveryAborted && !disposed).finally(() => {
|
|
2201
|
-
if (disposed || bufferedEventsReplayed) return;
|
|
2202
|
-
recovering = false;
|
|
2203
|
-
stream.replayBufferedEvents(bufferedEvents, handlers);
|
|
2204
|
-
bufferedEventsReplayed = true;
|
|
2037
|
+
stream.receive(event, handlers);
|
|
2205
2038
|
});
|
|
2039
|
+
const { initialSnapshot } = options;
|
|
2040
|
+
if (initialSnapshot !== void 0) stream.recover(handlers, async () => initialSnapshot, options);
|
|
2041
|
+
else if (options.recover === true) stream.recover(handlers, () => stream.loadSnapshot(), options);
|
|
2206
2042
|
return () => {
|
|
2207
|
-
|
|
2043
|
+
stream.dispose();
|
|
2208
2044
|
unsubscribe();
|
|
2209
2045
|
releaseRoom();
|
|
2210
2046
|
};
|
|
2211
2047
|
}
|
|
2212
|
-
|
|
2213
|
-
|
|
2048
|
+
receive(event, handlers) {
|
|
2049
|
+
if (this.disposed) return;
|
|
2050
|
+
const recovery = this.recovery;
|
|
2051
|
+
if (recovery && isGenerationRealtimeEvent(event)) {
|
|
2052
|
+
recovery.events.push(event);
|
|
2053
|
+
if (recovery.events.length > SNAPSHOT_RECOVERY_MAX_BUFFERED_EVENTS) this.finishRecovery(recovery, handlers);
|
|
2054
|
+
return;
|
|
2055
|
+
}
|
|
2056
|
+
this.handleEvent(event, handlers);
|
|
2057
|
+
}
|
|
2058
|
+
dispose() {
|
|
2059
|
+
this.disposed = true;
|
|
2060
|
+
this.recovery = null;
|
|
2061
|
+
}
|
|
2062
|
+
async loadSnapshot() {
|
|
2063
|
+
if (!this.fetchStreamSnapshot) return null;
|
|
2064
|
+
try {
|
|
2065
|
+
return (await withTimeout(this.fetchStreamSnapshot(), SNAPSHOT_RECOVERY_TIMEOUT_MS)).snapshot;
|
|
2066
|
+
} catch (error) {
|
|
2214
2067
|
console.warn("[SessionGenerationStreamClient] Failed to recover stream snapshot:", error);
|
|
2215
|
-
return
|
|
2216
|
-
}
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2068
|
+
return null;
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
async recover(handlers, load, options, resync) {
|
|
2072
|
+
const recovery = { events: resync ? [resync.event] : [] };
|
|
2073
|
+
this.recovery = recovery;
|
|
2074
|
+
const snapshot = await load().catch(() => null);
|
|
2075
|
+
if (this.recovery !== recovery) return;
|
|
2076
|
+
const expectedTurnId = resync?.turnId;
|
|
2077
|
+
if (snapshot && (!expectedTurnId || !snapshot.turnId || snapshot.turnId === expectedTurnId)) this.seedFromSnapshot(snapshot, handlers, options);
|
|
2078
|
+
this.finishRecovery(recovery, handlers);
|
|
2079
|
+
}
|
|
2080
|
+
finishRecovery(recovery, handlers) {
|
|
2081
|
+
if (this.recovery !== recovery) return;
|
|
2082
|
+
this.recovery = null;
|
|
2083
|
+
this.replayBufferedEvents(recovery.events, handlers);
|
|
2084
|
+
}
|
|
2085
|
+
shouldResync(turnId, messageId) {
|
|
2086
|
+
if (!this.fetchStreamSnapshot || this.recovery) return false;
|
|
2087
|
+
const key = `${turnId ?? ""}:${messageId ?? ""}`;
|
|
2088
|
+
if (key === this.resyncedMessageKey) return false;
|
|
2089
|
+
this.resyncedMessageKey = key;
|
|
2090
|
+
return true;
|
|
2091
|
+
}
|
|
2092
|
+
liveState() {
|
|
2093
|
+
return this.reducer.get({
|
|
2094
|
+
spaceId: this.spaceId,
|
|
2095
|
+
sessionId: this.sessionId
|
|
2096
|
+
});
|
|
2097
|
+
}
|
|
2098
|
+
isForeignTurn(turnId) {
|
|
2099
|
+
if (!turnId) return false;
|
|
2100
|
+
const live = this.liveState();
|
|
2101
|
+
return (live.status === "streaming" || live.status === "pending") && live.turnId != null && live.turnId !== turnId;
|
|
2102
|
+
}
|
|
2103
|
+
isLiveMessage(message) {
|
|
2104
|
+
if (this.messageId == null && this.messageOrdinal == null) return true;
|
|
2105
|
+
const meta = message.meta ?? {};
|
|
2106
|
+
if (typeof meta.messageOrdinal === "number" && this.messageOrdinal != null) return meta.messageOrdinal === this.messageOrdinal;
|
|
2107
|
+
return typeof meta.streamMessageId === "string" && meta.streamMessageId === this.messageId;
|
|
2220
2108
|
}
|
|
2221
2109
|
seedFromSnapshot(snapshot, handlers, options) {
|
|
2222
2110
|
if (snapshot.spaceId !== this.spaceId || snapshot.sessionId !== this.sessionId) return false;
|
|
@@ -2255,7 +2143,7 @@ var SessionGenerationStreamClient = class SessionGenerationStreamClient {
|
|
|
2255
2143
|
const sameMessage = sameTurn && Boolean(this.messageId && (identity.messageId && identity.messageId === this.messageId || identity.messageId == null && identity.messageOrdinal != null && identity.messageOrdinal === this.messageOrdinal));
|
|
2256
2144
|
const olderMessageInSameTurn = sameTurn && identity.messageOrdinal != null && this.messageOrdinal != null && identity.messageOrdinal < this.messageOrdinal;
|
|
2257
2145
|
if ((sameMessage || olderMessageInSameTurn) && this.patchState && seq != null && seq <= this.patchState.patchSeq) continue;
|
|
2258
|
-
this.
|
|
2146
|
+
this.receive(event, handlers);
|
|
2259
2147
|
}
|
|
2260
2148
|
}
|
|
2261
2149
|
emit(handlers, event) {
|
|
@@ -2377,36 +2265,41 @@ var SessionGenerationStreamClient = class SessionGenerationStreamClient {
|
|
|
2377
2265
|
anchorUserMessageId
|
|
2378
2266
|
};
|
|
2379
2267
|
const result = this.reducer.applyPatch(input);
|
|
2268
|
+
if (!result.applied && result.reason === "version_mismatch" && this.shouldResync(turnId, messageId)) {
|
|
2269
|
+
this.recover(handlers, () => this.loadSnapshot(), void 0, {
|
|
2270
|
+
turnId,
|
|
2271
|
+
event
|
|
2272
|
+
});
|
|
2273
|
+
return;
|
|
2274
|
+
}
|
|
2380
2275
|
this.handleAppliedState(handlers, "patch", result, event, messageId, messageOrdinal);
|
|
2381
2276
|
}
|
|
2382
2277
|
handlePersisted(event, handlers) {
|
|
2383
2278
|
const message = event.payload.message;
|
|
2384
2279
|
if (!isRecord(message)) return;
|
|
2385
2280
|
const commit = parseAssistantMessageCommit(message);
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
this.
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2398
|
-
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
});
|
|
2409
|
-
this.resetCurrentMessage();
|
|
2281
|
+
const turnId = getTurnIdFromMessage(commit.message);
|
|
2282
|
+
const key = {
|
|
2283
|
+
spaceId: this.spaceId,
|
|
2284
|
+
sessionId: this.sessionId,
|
|
2285
|
+
turnId
|
|
2286
|
+
};
|
|
2287
|
+
if (commit.kind !== "ignored" && !this.isForeignTurn(turnId)) {
|
|
2288
|
+
if (commit.kind === "intermediate") {
|
|
2289
|
+
const intermediate = messageRecordToIntermediate(commit.message);
|
|
2290
|
+
if (intermediate) this.addIntermediateMessage(intermediate);
|
|
2291
|
+
if (this.isLiveMessage(commit.message)) {
|
|
2292
|
+
this.reducer.start({
|
|
2293
|
+
...key,
|
|
2294
|
+
turnId: turnId ?? this.liveState().turnId
|
|
2295
|
+
});
|
|
2296
|
+
this.resetCurrentMessage();
|
|
2297
|
+
}
|
|
2298
|
+
} else {
|
|
2299
|
+
if (commit.kind === "final") this.reducer.complete(key);
|
|
2300
|
+
else this.reducer.fail(key);
|
|
2301
|
+
this.resetCurrentMessage();
|
|
2302
|
+
}
|
|
2410
2303
|
}
|
|
2411
2304
|
this.emit(handlers, {
|
|
2412
2305
|
type: "commit",
|
|
@@ -2418,17 +2311,16 @@ var SessionGenerationStreamClient = class SessionGenerationStreamClient {
|
|
|
2418
2311
|
const turn = event.payload.turn;
|
|
2419
2312
|
if (!isRecord(turn)) return;
|
|
2420
2313
|
const typedTurn = turn;
|
|
2421
|
-
if (typedTurn.
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
|
|
2427
|
-
|
|
2428
|
-
|
|
2429
|
-
|
|
2430
|
-
}
|
|
2431
|
-
this.resetCurrentMessage();
|
|
2314
|
+
if (!this.isForeignTurn(typedTurn.id)) {
|
|
2315
|
+
const key = {
|
|
2316
|
+
spaceId: this.spaceId,
|
|
2317
|
+
sessionId: this.sessionId,
|
|
2318
|
+
turnId: typedTurn.id
|
|
2319
|
+
};
|
|
2320
|
+
if (typedTurn.status === "interrupted") this.reducer.interrupt(key);
|
|
2321
|
+
else this.reducer.complete(key);
|
|
2322
|
+
this.resetCurrentMessage();
|
|
2323
|
+
}
|
|
2432
2324
|
this.emit(handlers, {
|
|
2433
2325
|
type: "finalized",
|
|
2434
2326
|
turn: typedTurn,
|
|
@@ -2635,17 +2527,22 @@ var SpacesApi = class {
|
|
|
2635
2527
|
constructor(transport) {
|
|
2636
2528
|
this.transport = transport;
|
|
2637
2529
|
}
|
|
2638
|
-
list(customFetch) {
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
|
|
2642
|
-
|
|
2530
|
+
list(optionsOrFetch = {}, customFetch) {
|
|
2531
|
+
const options = typeof optionsOrFetch === "function" ? {} : optionsOrFetch;
|
|
2532
|
+
const fetch = typeof optionsOrFetch === "function" ? optionsOrFetch : customFetch;
|
|
2533
|
+
const params = new URLSearchParams();
|
|
2534
|
+
params.set("filter", options.filter ?? "recent");
|
|
2535
|
+
if (options.limit) params.set("limit", String(options.limit));
|
|
2536
|
+
if (options.cursor) params.set("cursor", options.cursor);
|
|
2537
|
+
if (options.query) params.set("q", options.query);
|
|
2538
|
+
if (options.name) params.set("name", options.name);
|
|
2539
|
+
for (const recent of options.recentSpaces ?? []) {
|
|
2540
|
+
params.append("recentSpaceId", recent.id);
|
|
2541
|
+
params.append("recentSpaceAt", new Date(recent.timestamp).toISOString());
|
|
2542
|
+
}
|
|
2543
|
+
return this.transport.request(`/api/spaces?${params}`, { fetch });
|
|
2643
2544
|
}
|
|
2644
|
-
/**
|
|
2645
|
-
* Resolve the user's default space (owned/member home, else most recent).
|
|
2646
|
-
* When the account has no accessible space, the API creates a blank Home
|
|
2647
|
-
* space (`slug=home`) and returns it.
|
|
2648
|
-
*/
|
|
2545
|
+
/** Landing Space within the user's own Spaces; use `ensureHome()` for a write target. */
|
|
2649
2546
|
getDefault(customFetch) {
|
|
2650
2547
|
return this.transport.request("/api/spaces/default", {
|
|
2651
2548
|
method: "GET",
|
|
@@ -2658,6 +2555,16 @@ var SpacesApi = class {
|
|
|
2658
2555
|
getBySlug(username, slug, customFetch) {
|
|
2659
2556
|
return this.transport.request(`/api/spaces/by-slug/${encodeURIComponent(username)}/${encodeURIComponent(slug)}`, { fetch: customFetch });
|
|
2660
2557
|
}
|
|
2558
|
+
getOwnedBySlug(slug, customFetch) {
|
|
2559
|
+
return this.transport.request(`/api/me/spaces/by-slug/${encodeURIComponent(slug)}`, { fetch: customFetch });
|
|
2560
|
+
}
|
|
2561
|
+
/** The account's own Home Space, created on first use. */
|
|
2562
|
+
ensureHome(customFetch) {
|
|
2563
|
+
return this.transport.request("/api/me/spaces/home", {
|
|
2564
|
+
method: "POST",
|
|
2565
|
+
fetch: customFetch
|
|
2566
|
+
});
|
|
2567
|
+
}
|
|
2661
2568
|
create(input, headers) {
|
|
2662
2569
|
return this.transport.request("/api/spaces", {
|
|
2663
2570
|
method: "POST",
|
|
@@ -2758,7 +2665,7 @@ var SpaceFilesApi = class {
|
|
|
2758
2665
|
}
|
|
2759
2666
|
read(path, customFetch, signal) {
|
|
2760
2667
|
const params = new URLSearchParams({ path });
|
|
2761
|
-
return this.transport.
|
|
2668
|
+
return this.transport.requestRevalidated(`/api/spaces/${this.spaceId}/fs/file?${params.toString()}`, {
|
|
2762
2669
|
fetch: customFetch,
|
|
2763
2670
|
signal
|
|
2764
2671
|
});
|
|
@@ -2821,15 +2728,33 @@ var SpaceFilesApi = class {
|
|
|
2821
2728
|
fetch: customFetch
|
|
2822
2729
|
});
|
|
2823
2730
|
}
|
|
2824
|
-
async
|
|
2731
|
+
async open(path, options = {}) {
|
|
2825
2732
|
const params = new URLSearchParams({ path });
|
|
2826
|
-
const
|
|
2827
|
-
|
|
2828
|
-
|
|
2733
|
+
const deadlineAt = Date.now() + Math.max(0, options.timeoutMs ?? 6e4);
|
|
2734
|
+
while (true) {
|
|
2735
|
+
const raw = await this.transport.raw(`/api/spaces/${this.spaceId}/fs/download?${params.toString()}`, {
|
|
2736
|
+
fetch: options.fetch,
|
|
2737
|
+
signal: options.signal
|
|
2738
|
+
});
|
|
2739
|
+
const { response } = raw;
|
|
2740
|
+
if (response.status !== 202) return {
|
|
2741
|
+
response,
|
|
2742
|
+
filename: getFilenameFromContentDisposition(response.headers.get("content-disposition")) ?? path.split("/").pop() ?? "download",
|
|
2743
|
+
mimeType: response.headers.get("content-type") ?? "application/octet-stream"
|
|
2744
|
+
};
|
|
2745
|
+
const body = await raw.json().catch(() => null);
|
|
2746
|
+
const remainingMs = deadlineAt - Date.now();
|
|
2747
|
+
if (remainingMs <= 0) throw new HttpError("File is being prepared. Please retry shortly.", 202, body);
|
|
2748
|
+
const retryAfterMs = typeof body?.retryAfterMs === "number" ? body.retryAfterMs : 2e3;
|
|
2749
|
+
await waitForSpaceFileRetry(Math.min(Math.max(250, Math.min(retryAfterMs, 2e3)), remainingMs), options.signal);
|
|
2750
|
+
}
|
|
2751
|
+
}
|
|
2752
|
+
async download(path, customFetch) {
|
|
2753
|
+
const { response, filename, mimeType } = await this.open(path, { fetch: customFetch });
|
|
2829
2754
|
return {
|
|
2830
|
-
blob,
|
|
2831
|
-
filename
|
|
2832
|
-
mimeType
|
|
2755
|
+
blob: await response.blob(),
|
|
2756
|
+
filename,
|
|
2757
|
+
mimeType
|
|
2833
2758
|
};
|
|
2834
2759
|
}
|
|
2835
2760
|
write(input) {
|
|
@@ -2839,6 +2764,31 @@ var SpaceFilesApi = class {
|
|
|
2839
2764
|
body: JSON.stringify(input)
|
|
2840
2765
|
});
|
|
2841
2766
|
}
|
|
2767
|
+
copy(input) {
|
|
2768
|
+
return this.transport.request(`/api/spaces/${this.spaceId}/fs/copy`, {
|
|
2769
|
+
method: "POST",
|
|
2770
|
+
headers: { "Content-Type": "application/json" },
|
|
2771
|
+
body: JSON.stringify(input)
|
|
2772
|
+
});
|
|
2773
|
+
}
|
|
2774
|
+
getCopy(copyId, options = {}) {
|
|
2775
|
+
const params = new URLSearchParams();
|
|
2776
|
+
if (options.waitMs) params.set("waitMs", String(options.waitMs));
|
|
2777
|
+
const query = params.toString();
|
|
2778
|
+
return this.transport.request(`/api/spaces/${this.spaceId}/fs/copies/${encodeURIComponent(copyId)}${query ? `?${query}` : ""}`, { signal: options.signal });
|
|
2779
|
+
}
|
|
2780
|
+
async waitForCopy(copy, options = {}) {
|
|
2781
|
+
let current = copy;
|
|
2782
|
+
while (current.status !== "completed" || !current.result) {
|
|
2783
|
+
if (current.progress) options.onProgress?.(current.progress);
|
|
2784
|
+
options.signal?.throwIfAborted();
|
|
2785
|
+
current = await this.getCopy(current.copyId, {
|
|
2786
|
+
waitMs: options.pollMs ?? 5e3,
|
|
2787
|
+
signal: options.signal
|
|
2788
|
+
});
|
|
2789
|
+
}
|
|
2790
|
+
return current.result;
|
|
2791
|
+
}
|
|
2842
2792
|
createDir(path, mutationId) {
|
|
2843
2793
|
return this.transport.request(`/api/spaces/${this.spaceId}/fs/dir`, {
|
|
2844
2794
|
method: "POST",
|
|
@@ -3115,6 +3065,11 @@ var SessionClient = class {
|
|
|
3115
3065
|
get(customFetch) {
|
|
3116
3066
|
return this.transport.request(`/api/sessions/${this.id}`, { fetch: customFetch });
|
|
3117
3067
|
}
|
|
3068
|
+
async stats(options = {}) {
|
|
3069
|
+
const stats = readSessionStats({ stats: (await this.transport.request(`/api/sessions/${this.id}/stats`, options)).stats });
|
|
3070
|
+
if (!stats) throw new Error("Invalid session statistics");
|
|
3071
|
+
return { stats };
|
|
3072
|
+
}
|
|
3118
3073
|
/** Space files changed by this session's agent write/edit tool calls. */
|
|
3119
3074
|
files(options = {}) {
|
|
3120
3075
|
const query = options.limit ? `?limit=${options.limit}` : "";
|
|
@@ -3699,34 +3654,111 @@ var BoardRealtimeClient = class {
|
|
|
3699
3654
|
var BoardClient = class {
|
|
3700
3655
|
spaceId;
|
|
3701
3656
|
id;
|
|
3657
|
+
transport;
|
|
3702
3658
|
websocketClient;
|
|
3703
3659
|
realtime;
|
|
3704
|
-
boards;
|
|
3705
3660
|
constructor(spaceId, id, transport, websocketClient) {
|
|
3706
3661
|
this.spaceId = spaceId;
|
|
3707
3662
|
this.id = id;
|
|
3663
|
+
this.transport = transport;
|
|
3708
3664
|
this.websocketClient = websocketClient;
|
|
3709
|
-
this.boards = new SpaceBoardsApi(transport, spaceId, websocketClient);
|
|
3710
3665
|
this.realtime = new BoardRealtimeClient(websocketClient, spaceId, id);
|
|
3711
3666
|
}
|
|
3712
|
-
|
|
3713
|
-
return this.boards
|
|
3667
|
+
get path() {
|
|
3668
|
+
return `/api/spaces/${this.spaceId}/boards/${this.id}`;
|
|
3714
3669
|
}
|
|
3715
|
-
|
|
3716
|
-
|
|
3670
|
+
/** Read the Board, or part of it. Every value is complete, defaults included. */
|
|
3671
|
+
get(input = {}, customFetch) {
|
|
3672
|
+
const params = new URLSearchParams();
|
|
3673
|
+
if (input.only?.length) params.set("only", input.only.join(","));
|
|
3674
|
+
if (input.rect) params.set("rect", [
|
|
3675
|
+
input.rect.x,
|
|
3676
|
+
input.rect.y,
|
|
3677
|
+
input.rect.width,
|
|
3678
|
+
input.rect.height
|
|
3679
|
+
].join(","));
|
|
3680
|
+
if (input.within) params.set("within", input.within);
|
|
3681
|
+
if (input.items?.length) params.set("items", input.items.join(","));
|
|
3682
|
+
if (input.animations?.length) params.set("animations", input.animations.join(","));
|
|
3683
|
+
if (input.limit !== void 0) params.set("limit", String(input.limit));
|
|
3684
|
+
if (input.cursor) params.set("cursor", input.cursor);
|
|
3685
|
+
const query = params.toString();
|
|
3686
|
+
return this.transport.request(`${this.path}${query ? `?${query}` : ""}`, { fetch: customFetch });
|
|
3717
3687
|
}
|
|
3718
|
-
|
|
3719
|
-
|
|
3688
|
+
/** Merge a patch into the Board: fields merge, `null` deletes. */
|
|
3689
|
+
apply(patch, options = {}) {
|
|
3690
|
+
return this.transport.request(`${this.path}/apply`, {
|
|
3691
|
+
method: "POST",
|
|
3692
|
+
headers: { "Content-Type": "application/json" },
|
|
3693
|
+
body: JSON.stringify({
|
|
3694
|
+
...options,
|
|
3695
|
+
patch,
|
|
3696
|
+
mutationId: options.mutationId ?? randomBoardId()
|
|
3697
|
+
})
|
|
3698
|
+
});
|
|
3720
3699
|
}
|
|
3721
|
-
/**
|
|
3722
|
-
|
|
3723
|
-
|
|
3700
|
+
/** Transactions newest first, each with the before/after state of what it changed. */
|
|
3701
|
+
history(input = {}, customFetch) {
|
|
3702
|
+
const params = new URLSearchParams();
|
|
3703
|
+
if (input.before !== void 0) params.set("before", String(input.before));
|
|
3704
|
+
if (input.limit !== void 0) params.set("limit", String(input.limit));
|
|
3705
|
+
const query = params.toString();
|
|
3706
|
+
return this.transport.request(`${this.path}/history${query ? `?${query}` : ""}`, { fetch: customFetch });
|
|
3724
3707
|
}
|
|
3725
|
-
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3708
|
+
/** Return the Board to an earlier version as a new write. */
|
|
3709
|
+
restore(version) {
|
|
3710
|
+
return this.transport.request(`${this.path}/restore`, {
|
|
3711
|
+
method: "POST",
|
|
3712
|
+
headers: { "Content-Type": "application/json" },
|
|
3713
|
+
body: JSON.stringify({ version })
|
|
3714
|
+
});
|
|
3715
|
+
}
|
|
3716
|
+
playback(command) {
|
|
3717
|
+
return this.transport.request(`${this.path}/playback`, {
|
|
3718
|
+
method: "POST",
|
|
3719
|
+
headers: { "Content-Type": "application/json" },
|
|
3720
|
+
body: JSON.stringify(command)
|
|
3721
|
+
}).then((response) => response.playback);
|
|
3722
|
+
}
|
|
3723
|
+
/** Shared playback: every viewer follows the same clock. */
|
|
3724
|
+
play(animationId, options = {}) {
|
|
3725
|
+
return this.playback({
|
|
3726
|
+
commandId: randomBoardId(),
|
|
3727
|
+
type: "play",
|
|
3728
|
+
animationId,
|
|
3729
|
+
...options
|
|
3730
|
+
});
|
|
3731
|
+
}
|
|
3732
|
+
pause() {
|
|
3733
|
+
return this.playback({
|
|
3734
|
+
commandId: randomBoardId(),
|
|
3735
|
+
type: "pause"
|
|
3736
|
+
});
|
|
3737
|
+
}
|
|
3738
|
+
resume() {
|
|
3739
|
+
return this.playback({
|
|
3740
|
+
commandId: randomBoardId(),
|
|
3741
|
+
type: "resume"
|
|
3742
|
+
});
|
|
3743
|
+
}
|
|
3744
|
+
seek(position) {
|
|
3745
|
+
return this.playback({
|
|
3746
|
+
commandId: randomBoardId(),
|
|
3747
|
+
type: "seek",
|
|
3748
|
+
position
|
|
3749
|
+
});
|
|
3750
|
+
}
|
|
3751
|
+
/** Continue past the marker a presentation is holding at. */
|
|
3752
|
+
next() {
|
|
3753
|
+
return this.playback({
|
|
3754
|
+
commandId: randomBoardId(),
|
|
3755
|
+
type: "next"
|
|
3756
|
+
});
|
|
3757
|
+
}
|
|
3758
|
+
stop() {
|
|
3759
|
+
return this.playback({
|
|
3760
|
+
commandId: randomBoardId(),
|
|
3761
|
+
type: "stop"
|
|
3730
3762
|
});
|
|
3731
3763
|
}
|
|
3732
3764
|
updateAwareness(seq, update) {
|
|
@@ -3738,18 +3770,6 @@ var BoardClient = class {
|
|
|
3738
3770
|
update
|
|
3739
3771
|
});
|
|
3740
3772
|
}
|
|
3741
|
-
play(command) {
|
|
3742
|
-
return this.boards.play(this.id, command);
|
|
3743
|
-
}
|
|
3744
|
-
pause(command) {
|
|
3745
|
-
return this.boards.pause(this.id, command);
|
|
3746
|
-
}
|
|
3747
|
-
seek(command) {
|
|
3748
|
-
return this.boards.seek(this.id, command);
|
|
3749
|
-
}
|
|
3750
|
-
stop(command) {
|
|
3751
|
-
return this.boards.stop(this.id, command);
|
|
3752
|
-
}
|
|
3753
3773
|
subscribe(handlers) {
|
|
3754
3774
|
return this.realtime.subscribe(handlers);
|
|
3755
3775
|
}
|
|
@@ -3771,66 +3791,14 @@ var SpaceBoardsApi = class {
|
|
|
3771
3791
|
byId(boardId) {
|
|
3772
3792
|
return new BoardClient(this.spaceId, boardId, this.transport, this.websocketClient);
|
|
3773
3793
|
}
|
|
3774
|
-
|
|
3775
|
-
|
|
3794
|
+
/** Create a `.board` file and its Board, optionally with an initial document. */
|
|
3795
|
+
create(input) {
|
|
3776
3796
|
return this.transport.request(`/api/spaces/${this.spaceId}/boards`, {
|
|
3777
3797
|
method: "POST",
|
|
3778
3798
|
headers: { "Content-Type": "application/json" },
|
|
3779
3799
|
body: JSON.stringify(input)
|
|
3780
3800
|
});
|
|
3781
3801
|
}
|
|
3782
|
-
authoring(boardId, input = {}, customFetch) {
|
|
3783
|
-
if (input.include?.length === 0) throw new Error("Board authoring include must not be empty.");
|
|
3784
|
-
const params = new URLSearchParams();
|
|
3785
|
-
for (const section of input.include ?? []) params.append("include", section);
|
|
3786
|
-
if (input.itemIds?.length) params.set("itemIds", input.itemIds.join(","));
|
|
3787
|
-
if (input.connectionIds?.length) params.set("connectionIds", input.connectionIds.join(","));
|
|
3788
|
-
if (input.effectIds?.length) params.set("effectIds", input.effectIds.join(","));
|
|
3789
|
-
if (input.compositionIds?.length) params.set("compositionIds", input.compositionIds.join(","));
|
|
3790
|
-
if (input.viewport) params.set("viewport", JSON.stringify(input.viewport));
|
|
3791
|
-
const query = params.toString();
|
|
3792
|
-
return this.transport.request(`/api/spaces/${this.spaceId}/boards/${boardId}/authoring${query ? `?${query}` : ""}`, { fetch: customFetch });
|
|
3793
|
-
}
|
|
3794
|
-
mutateSemantic(boardId, mutation) {
|
|
3795
|
-
return this.transport.request(`/api/spaces/${this.spaceId}/boards/${boardId}/mutations`, {
|
|
3796
|
-
method: "POST",
|
|
3797
|
-
headers: { "Content-Type": "application/json" },
|
|
3798
|
-
body: JSON.stringify(mutation)
|
|
3799
|
-
});
|
|
3800
|
-
}
|
|
3801
|
-
summary(boardId, customFetch) {
|
|
3802
|
-
return this.transport.request(`/api/spaces/${this.spaceId}/boards/${boardId}/summary`, { fetch: customFetch });
|
|
3803
|
-
}
|
|
3804
|
-
capabilities(boardId, customFetch) {
|
|
3805
|
-
return this.transport.request(`/api/spaces/${this.spaceId}/boards/${boardId}/capabilities`, { fetch: customFetch });
|
|
3806
|
-
}
|
|
3807
|
-
transactions(boardId, input = {}, customFetch) {
|
|
3808
|
-
const params = new URLSearchParams();
|
|
3809
|
-
if (input.before !== void 0) params.set("before", String(input.before));
|
|
3810
|
-
if (input.limit !== void 0) params.set("limit", String(input.limit));
|
|
3811
|
-
if (input.snapshot !== void 0) params.set("snapshot", String(input.snapshot));
|
|
3812
|
-
const query = params.toString();
|
|
3813
|
-
return this.transport.request(`/api/spaces/${this.spaceId}/boards/${boardId}/transactions${query ? `?${query}` : ""}`, { fetch: customFetch });
|
|
3814
|
-
}
|
|
3815
|
-
playback(boardId, command) {
|
|
3816
|
-
return this.transport.request(`/api/spaces/${this.spaceId}/boards/${boardId}/playback`, {
|
|
3817
|
-
method: "POST",
|
|
3818
|
-
headers: { "Content-Type": "application/json" },
|
|
3819
|
-
body: JSON.stringify(command)
|
|
3820
|
-
});
|
|
3821
|
-
}
|
|
3822
|
-
play(boardId, command) {
|
|
3823
|
-
return this.playback(boardId, command);
|
|
3824
|
-
}
|
|
3825
|
-
pause(boardId, command) {
|
|
3826
|
-
return this.playback(boardId, command);
|
|
3827
|
-
}
|
|
3828
|
-
seek(boardId, command) {
|
|
3829
|
-
return this.playback(boardId, command);
|
|
3830
|
-
}
|
|
3831
|
-
stop(boardId, command) {
|
|
3832
|
-
return this.playback(boardId, command);
|
|
3833
|
-
}
|
|
3834
3802
|
};
|
|
3835
3803
|
var SpaceCheckpointFilesApi = class {
|
|
3836
3804
|
transport;
|
|
@@ -3970,6 +3938,7 @@ var SpaceClient = class {
|
|
|
3970
3938
|
webhooks;
|
|
3971
3939
|
env;
|
|
3972
3940
|
sandbox;
|
|
3941
|
+
displays;
|
|
3973
3942
|
invitations;
|
|
3974
3943
|
labels;
|
|
3975
3944
|
boards;
|
|
@@ -3993,6 +3962,7 @@ var SpaceClient = class {
|
|
|
3993
3962
|
this.webhooks = new SpaceWebhooksApi(transport, id);
|
|
3994
3963
|
this.env = new SpaceEnvApi(transport, id);
|
|
3995
3964
|
this.sandbox = new SpaceSandboxApi(transport, id);
|
|
3965
|
+
this.displays = new SpaceDisplaysApi(transport, id);
|
|
3996
3966
|
this.invitations = new SpaceInvitationsApi(transport, id);
|
|
3997
3967
|
this.labels = new SpaceLabelsApi(transport, id);
|
|
3998
3968
|
this.boards = new SpaceBoardsApi(transport, id, websocketClient);
|
|
@@ -4398,6 +4368,7 @@ var UserApi = class {
|
|
|
4398
4368
|
if (options?.limit !== void 0) params.set("limit", String(options.limit));
|
|
4399
4369
|
if (options?.cursor) params.set("cursor", options.cursor);
|
|
4400
4370
|
if (options?.source?.length) params.set("source", options.source.join(","));
|
|
4371
|
+
if (options?.includeForks) params.set("includeForks", "1");
|
|
4401
4372
|
const query = params.toString();
|
|
4402
4373
|
return this.transport.request(`/api/me/sessions${query ? `?${query}` : ""}`, { fetch });
|
|
4403
4374
|
}
|
|
@@ -4434,6 +4405,53 @@ var UserLabelsApi = class {
|
|
|
4434
4405
|
constructor(transport) {
|
|
4435
4406
|
this.transport = transport;
|
|
4436
4407
|
}
|
|
4408
|
+
list() {
|
|
4409
|
+
return this.transport.request("/api/me/labels");
|
|
4410
|
+
}
|
|
4411
|
+
create(labelRef) {
|
|
4412
|
+
return this.transport.request("/api/me/labels", {
|
|
4413
|
+
method: "POST",
|
|
4414
|
+
headers: { "Content-Type": "application/json" },
|
|
4415
|
+
body: JSON.stringify({ labelRef })
|
|
4416
|
+
});
|
|
4417
|
+
}
|
|
4418
|
+
resolve(labelRefs) {
|
|
4419
|
+
return this.transport.request("/api/me/labels/resolve", {
|
|
4420
|
+
method: "POST",
|
|
4421
|
+
headers: { "Content-Type": "application/json" },
|
|
4422
|
+
body: JSON.stringify({ labelRefs })
|
|
4423
|
+
});
|
|
4424
|
+
}
|
|
4425
|
+
update(labelRef, input) {
|
|
4426
|
+
return this.transport.request("/api/me/labels/by-ref", {
|
|
4427
|
+
method: "PATCH",
|
|
4428
|
+
headers: { "Content-Type": "application/json" },
|
|
4429
|
+
body: JSON.stringify({
|
|
4430
|
+
labelRef,
|
|
4431
|
+
...input
|
|
4432
|
+
})
|
|
4433
|
+
});
|
|
4434
|
+
}
|
|
4435
|
+
delete(labelRef) {
|
|
4436
|
+
return this.transport.request(`/api/me/labels/by-ref?${new URLSearchParams({ ref: labelRef })}`, { method: "DELETE" });
|
|
4437
|
+
}
|
|
4438
|
+
reorder(labelRefs) {
|
|
4439
|
+
return this.transport.request("/api/me/labels/reorder", {
|
|
4440
|
+
method: "POST",
|
|
4441
|
+
headers: { "Content-Type": "application/json" },
|
|
4442
|
+
body: JSON.stringify({ labelRefs })
|
|
4443
|
+
});
|
|
4444
|
+
}
|
|
4445
|
+
patchResources(resourceRefs, input) {
|
|
4446
|
+
return this.transport.request("/api/me/resources/space/labels/batch", {
|
|
4447
|
+
method: "PATCH",
|
|
4448
|
+
headers: { "Content-Type": "application/json" },
|
|
4449
|
+
body: JSON.stringify({
|
|
4450
|
+
resourceRefs,
|
|
4451
|
+
...input
|
|
4452
|
+
})
|
|
4453
|
+
});
|
|
4454
|
+
}
|
|
4437
4455
|
getResourceLabels(resourceType, resourceRef) {
|
|
4438
4456
|
const params = new URLSearchParams({ resourceRef });
|
|
4439
4457
|
return this.transport.request(`/api/me/resources/${resourceType}/labels?${params.toString()}`);
|
|
@@ -4789,4 +4807,4 @@ var CohubHttpClient = class {
|
|
|
4789
4807
|
};
|
|
4790
4808
|
const createHttpClient = (options) => new CohubHttpClient(options);
|
|
4791
4809
|
//#endregion
|
|
4792
|
-
export {
|
|
4810
|
+
export { resolveOpenSurface as $, ReferencesApi as A, DESKTOP_COMMAND_DEFAULT_TIMEOUT_MS as B, SessionPatchReducer as C, connectDisplay as D, DisplayConnection as E, ModelsApi as F, DESKTOP_COMMAND_SETTLEMENT_GRACE_SECONDS as G, DESKTOP_COMMAND_OPEN_TIMEOUT_MS as H, GenerationsApi as I, defaultDesktopCommandTimeoutMs as J, DESKTOP_COMMAND_TERMINAL_TTL_SECONDS as K, CronJobsApi as L, PublicAssetsApi as M, SkillsApi as N, ensureRealtimeConnected as O, PromptsApi as P, parseDesktopCommandId as Q, ChannelsApi as R, parseAssistantMessageCommit as S, SpaceDisplaysApi as T, DESKTOP_COMMAND_PAYLOAD_MAX_BYTES as U, DESKTOP_COMMAND_MAX_TIMEOUT_MS as V, DESKTOP_COMMAND_PENDING_TTL_SECONDS as W, isTerminalDesktopCommandStatus as X, isDesktopCallMethod as Y, parseDesktopCommand as Z, PublicInviteApi as _, DesktopCommandsApi as a, normalizeDisplayPoint as at, SessionGenerationStreamClient as b, ReferralsApi as c, TasksApi as d, NAVIGATION_ERROR_MESSAGE_MAX_LENGTH as et, BoardClient as f, SpacesApi as g, SpaceWebhooksApi as h, WorkCommerceApi as i, DISPLAY_SYSTEM_ACTIONS as it, SearchApi as j, SessionAccessApi as k, UsersApi as l, SpacePublicFilesApi as m, createHttpClient as n, DISPLAY_INPUT_MAX_SCHEDULE_MS as nt, UiCommandsApi as o, splitDisplayText as ot, SpaceClient as p, DESKTOP_COMMAND_VERSION as q, AppCommerceApi as r, DISPLAY_INPUT_MAX_TEXT as rt, AppsApi as s, CohubHttpClient as t, DISPLAY_ELEMENT_REF_PATTERN as tt, UserApi as u, buildSpaceInvitePath as v, createSessionPatchReducer as w, createSessionGenerationStreamClient as x, buildSpacePath as y, DESKTOP_COMMAND_ACCEPT_TIMEOUT_MS as z };
|