@zaflun/lumio-protocol 0.2.4 → 0.6.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/CHANGELOG.md +54 -1
- package/README.md +1 -1
- package/dist/chunk-746VSR4E.js +51 -0
- package/dist/index.d.ts +9 -4
- package/dist/index.js +24 -5
- package/dist/origin.d.ts +24 -0
- package/dist/origin.js +14 -0
- package/package.json +6 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,59 @@ All notable changes to `@zaflun/lumio-protocol` are documented here.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## 0.6.0
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Per-extension runtime origins** — new entry point
|
|
13
|
+
`@zaflun/lumio-protocol/origin` (also re-exported from the package root) with
|
|
14
|
+
`extensionRuntimeLabel(extensionId)` (canonical lowercase UUID → 32 lowercase
|
|
15
|
+
hex, otherwise `null`), `extensionRuntimeOrigin(extensionId, template)`
|
|
16
|
+
(`https://{label}.runtime.ext.lumio.vision` → the extension's origin, `null` for an
|
|
17
|
+
invalid id or template), `extensionRuntimeOriginLabel(origin, template)` and
|
|
18
|
+
`isValidExtensionRuntimeOriginTemplate(template)`. The `/origin` entry has no
|
|
19
|
+
runtime dependencies. Additive.
|
|
20
|
+
|
|
21
|
+
## 0.5.0
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- **Kick event types** — `kick:resub`, `kick:kicks`, `kick:reward`,
|
|
26
|
+
`kick:reward_status` and `kick:stream_update` are in `LUMIO_EVENT_TYPES`,
|
|
27
|
+
`LumioEventType` and the `lumioEventTypeSchema` Zod enum. The Lumio webhook
|
|
28
|
+
receiver emits them for Kick subscription renewals, KICKs gifts, channel-point
|
|
29
|
+
redemptions (first delivery of a redemption `id` → `kick:reward`, every later
|
|
30
|
+
status change → `kick:reward_status`) and livestream metadata changes. Every
|
|
31
|
+
Kick event carries canonical top-level fields (`user_id`, `user_login`,
|
|
32
|
+
`user_name`, `avatarUrl`, `is_anonymous`) next to the original Kick payload.
|
|
33
|
+
`LUMIO_EVENT_TYPES` now holds 58 event types.
|
|
34
|
+
|
|
35
|
+
Additive: existing event strings are unchanged. Consumers that exhaustively
|
|
36
|
+
`switch` over `LumioEventType` need arms for the five new Kick types.
|
|
37
|
+
|
|
38
|
+
## 0.4.0
|
|
39
|
+
|
|
40
|
+
### Added
|
|
41
|
+
|
|
42
|
+
- **YouTube stream lifecycle** — `youtube:stream_online` and
|
|
43
|
+
`youtube:stream_offline`, emitted by the poll-driven YouTube worker on the
|
|
44
|
+
channel-status online/offline edge (once per real transition).
|
|
45
|
+
|
|
46
|
+
## 0.3.0
|
|
47
|
+
|
|
48
|
+
### Removed
|
|
49
|
+
|
|
50
|
+
- **Trovo event types** — `trovo:subscribe`, `trovo:spell` and `trovo:chat` are
|
|
51
|
+
gone from `LUMIO_EVENT_TYPES`, `LumioEventType` and the `lumioEventTypeSchema`
|
|
52
|
+
Zod enum. The Trovo platform has been shut down by its operator, so these
|
|
53
|
+
events can no longer be emitted. `LUMIO_EVENT_TYPES` now holds 72 event types
|
|
54
|
+
across Twitch, YouTube, Kick, Spotify, StreamElements, and per-platform +
|
|
55
|
+
cross-platform chat.
|
|
56
|
+
|
|
57
|
+
**Breaking for consumers** that reference a Trovo event type by name or
|
|
58
|
+
exhaustively `switch` over `LumioEventType`. Drop the Trovo arms; validation of
|
|
59
|
+
a Trovo event string now fails closed via `lumioEventTypeSchema`.
|
|
60
|
+
|
|
8
61
|
## 0.2.4
|
|
9
62
|
|
|
10
63
|
Wire-format types for the Lumio Extension Platform — the contract for
|
|
@@ -15,7 +68,7 @@ the validated schemas.
|
|
|
15
68
|
### Exports
|
|
16
69
|
|
|
17
70
|
- **Events** (`src/events.ts`)
|
|
18
|
-
- `LUMIO_EVENT_TYPES` —
|
|
71
|
+
- `LUMIO_EVENT_TYPES` — 75 platform event types across Twitch, YouTube, Kick,
|
|
19
72
|
Trovo, Spotify, StreamElements, and per-platform + cross-platform chat, with
|
|
20
73
|
`LumioEventType` and the `lumioEventTypeSchema` Zod enum.
|
|
21
74
|
- `ACTION_TYPES` — 21 action types (Chat, OBS, Spotify, Events, Overlay), with
|
package/README.md
CHANGED
|
@@ -10,7 +10,7 @@ npm install @zaflun/lumio-protocol
|
|
|
10
10
|
|
|
11
11
|
## What's included
|
|
12
12
|
|
|
13
|
-
- **54 Event Types** — Twitch, YouTube, Kick,
|
|
13
|
+
- **54 Event Types** — Twitch, YouTube, Kick, Spotify, StreamElements, Chat
|
|
14
14
|
- **21 Action Types** — Chat, OBS, Spotify, Events, Overlay
|
|
15
15
|
- **Message Schemas** — JSON-RPC 2.0 wire format (Zod validated)
|
|
16
16
|
- **30 Component Types** — `LumioComponentNode` for serialized UI trees
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
// src/origin.ts
|
|
2
|
+
var EXTENSION_RUNTIME_LABEL_PLACEHOLDER = "{label}";
|
|
3
|
+
var UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
4
|
+
var LABEL_PATTERN = /^[0-9a-f]{32}$/;
|
|
5
|
+
var DNS_LABEL_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
6
|
+
var TEMPLATE_PATTERN = /^(https:\/\/|http:\/\/)\{label\}(\.[a-z0-9.-]+(?::[0-9]{1,5})?)$/;
|
|
7
|
+
function parseTemplate(template) {
|
|
8
|
+
const match = TEMPLATE_PATTERN.exec(template);
|
|
9
|
+
if (!match) return null;
|
|
10
|
+
const prefix = match[1];
|
|
11
|
+
const suffix = match[2];
|
|
12
|
+
const host = suffix.slice(1).replace(/:[0-9]+$/, "");
|
|
13
|
+
if (prefix === "http://" && host !== "localhost") return null;
|
|
14
|
+
if (!host.split(".").every((label) => DNS_LABEL_PATTERN.test(label))) return null;
|
|
15
|
+
const sample = `${prefix}${"0".repeat(32)}${suffix}`;
|
|
16
|
+
try {
|
|
17
|
+
if (new URL(sample).origin !== sample) return null;
|
|
18
|
+
} catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
return { prefix, suffix };
|
|
22
|
+
}
|
|
23
|
+
function extensionRuntimeLabel(extensionId) {
|
|
24
|
+
if (typeof extensionId !== "string" || !UUID_PATTERN.test(extensionId)) return null;
|
|
25
|
+
return extensionId.replaceAll("-", "");
|
|
26
|
+
}
|
|
27
|
+
function extensionRuntimeOrigin(extensionId, template) {
|
|
28
|
+
const label = extensionRuntimeLabel(extensionId);
|
|
29
|
+
if (!label) return null;
|
|
30
|
+
const parsed = parseTemplate(template);
|
|
31
|
+
if (!parsed) return null;
|
|
32
|
+
return `${parsed.prefix}${label}${parsed.suffix}`;
|
|
33
|
+
}
|
|
34
|
+
function extensionRuntimeOriginLabel(origin, template) {
|
|
35
|
+
const parsed = parseTemplate(template);
|
|
36
|
+
if (!parsed) return null;
|
|
37
|
+
if (!origin.startsWith(parsed.prefix) || !origin.endsWith(parsed.suffix)) return null;
|
|
38
|
+
const label = origin.slice(parsed.prefix.length, origin.length - parsed.suffix.length);
|
|
39
|
+
return LABEL_PATTERN.test(label) ? label : null;
|
|
40
|
+
}
|
|
41
|
+
function isValidExtensionRuntimeOriginTemplate(template) {
|
|
42
|
+
return parseTemplate(template) !== null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export {
|
|
46
|
+
EXTENSION_RUNTIME_LABEL_PLACEHOLDER,
|
|
47
|
+
extensionRuntimeLabel,
|
|
48
|
+
extensionRuntimeOrigin,
|
|
49
|
+
extensionRuntimeOriginLabel,
|
|
50
|
+
isValidExtensionRuntimeOriginTemplate
|
|
51
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { z } from 'zod/v4';
|
|
2
|
+
export { EXTENSION_RUNTIME_LABEL_PLACEHOLDER, extensionRuntimeLabel, extensionRuntimeOrigin, extensionRuntimeOriginLabel, isValidExtensionRuntimeOriginTemplate } from './origin.js';
|
|
2
3
|
|
|
3
4
|
declare const themeColorsSchema: z.ZodObject<{
|
|
4
5
|
primary: z.ZodString;
|
|
@@ -287,7 +288,7 @@ declare const lumioMessageSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
287
288
|
}, z.core.$strip>], "type">;
|
|
288
289
|
type LumioMessage = z.infer<typeof lumioMessageSchema>;
|
|
289
290
|
|
|
290
|
-
declare const LUMIO_EVENT_TYPES: readonly ["twitch:follower", "twitch:subscribe", "twitch:gift_sub", "twitch:resub", "twitch:cheer", "twitch:raid", "twitch:reward", "twitch:hype_train", "twitch:hype_train_end", "twitch:poll", "twitch:poll_end", "twitch:prediction", "twitch:prediction_lock", "twitch:prediction_end", "twitch:goal", "twitch:goal_end", "twitch:ad_break", "twitch:ban", "twitch:unban", "twitch:stream_online", "twitch:stream_offline", "twitch:stream_update", "youtube:subscribe", "youtube:member", "youtube:superchat", "youtube:supersticker", "youtube:gift_membership", "youtube:gift_membership_received", "youtube:poll", "kick:follower", "kick:subscribe", "kick:gift", "kick:
|
|
291
|
+
declare const LUMIO_EVENT_TYPES: readonly ["twitch:follower", "twitch:subscribe", "twitch:gift_sub", "twitch:resub", "twitch:cheer", "twitch:raid", "twitch:reward", "twitch:hype_train", "twitch:hype_train_end", "twitch:poll", "twitch:poll_end", "twitch:prediction", "twitch:prediction_lock", "twitch:prediction_end", "twitch:goal", "twitch:goal_end", "twitch:ad_break", "twitch:ban", "twitch:unban", "twitch:stream_online", "twitch:stream_offline", "twitch:stream_update", "youtube:subscribe", "youtube:member", "youtube:superchat", "youtube:supersticker", "youtube:gift_membership", "youtube:gift_membership_received", "youtube:poll", "youtube:stream_online", "youtube:stream_offline", "kick:follower", "kick:subscribe", "kick:resub", "kick:gift", "kick:kicks", "kick:reward", "kick:reward_status", "kick:stream_online", "kick:stream_offline", "kick:stream_update", "spotify:track", "spotify:play", "spotify:pause", "spotify:skip", "spotify:volume", "spotify:queue_add", "spotify:device", "spotify:playlist_add", "spotify:playlist_remove", "spotify:playlist_create", "spotify:playlist_edit", "spotify:playlist_delete", "streamelements:tip", "twitch:chat", "youtube:chat", "kick:chat", "chat:message"];
|
|
291
292
|
type LumioEventType = (typeof LUMIO_EVENT_TYPES)[number];
|
|
292
293
|
declare const lumioEventTypeSchema: z.ZodEnum<{
|
|
293
294
|
"twitch:follower": "twitch:follower";
|
|
@@ -319,13 +320,18 @@ declare const lumioEventTypeSchema: z.ZodEnum<{
|
|
|
319
320
|
"youtube:gift_membership": "youtube:gift_membership";
|
|
320
321
|
"youtube:gift_membership_received": "youtube:gift_membership_received";
|
|
321
322
|
"youtube:poll": "youtube:poll";
|
|
323
|
+
"youtube:stream_online": "youtube:stream_online";
|
|
324
|
+
"youtube:stream_offline": "youtube:stream_offline";
|
|
322
325
|
"kick:follower": "kick:follower";
|
|
323
326
|
"kick:subscribe": "kick:subscribe";
|
|
327
|
+
"kick:resub": "kick:resub";
|
|
324
328
|
"kick:gift": "kick:gift";
|
|
329
|
+
"kick:kicks": "kick:kicks";
|
|
330
|
+
"kick:reward": "kick:reward";
|
|
331
|
+
"kick:reward_status": "kick:reward_status";
|
|
325
332
|
"kick:stream_online": "kick:stream_online";
|
|
326
333
|
"kick:stream_offline": "kick:stream_offline";
|
|
327
|
-
"
|
|
328
|
-
"trovo:spell": "trovo:spell";
|
|
334
|
+
"kick:stream_update": "kick:stream_update";
|
|
329
335
|
"spotify:track": "spotify:track";
|
|
330
336
|
"spotify:play": "spotify:play";
|
|
331
337
|
"spotify:pause": "spotify:pause";
|
|
@@ -342,7 +348,6 @@ declare const lumioEventTypeSchema: z.ZodEnum<{
|
|
|
342
348
|
"twitch:chat": "twitch:chat";
|
|
343
349
|
"youtube:chat": "youtube:chat";
|
|
344
350
|
"kick:chat": "kick:chat";
|
|
345
|
-
"trovo:chat": "trovo:chat";
|
|
346
351
|
"chat:message": "chat:message";
|
|
347
352
|
}>;
|
|
348
353
|
declare const ACTION_TYPES: readonly ["chat:send", "chat:delete", "chat:ban", "chat:unban", "chat:timeout", "obs:set_scene", "obs:set_source_visible", "obs:start_stream", "obs:stop_stream", "obs:start_recording", "obs:stop_recording", "spotify:play", "spotify:pause", "spotify:skip", "spotify:seek", "spotify:volume", "spotify:shuffle", "spotify:repeat", "spotify:queue_add", "events:emit", "overlay:update_layer"];
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EXTENSION_RUNTIME_LABEL_PLACEHOLDER,
|
|
3
|
+
extensionRuntimeLabel,
|
|
4
|
+
extensionRuntimeOrigin,
|
|
5
|
+
extensionRuntimeOriginLabel,
|
|
6
|
+
isValidExtensionRuntimeOriginTemplate
|
|
7
|
+
} from "./chunk-746VSR4E.js";
|
|
8
|
+
|
|
1
9
|
// src/messages.ts
|
|
2
10
|
import { z } from "zod/v4";
|
|
3
11
|
var themeColorsSchema = z.object({
|
|
@@ -140,15 +148,22 @@ var LUMIO_EVENT_TYPES = [
|
|
|
140
148
|
"youtube:gift_membership",
|
|
141
149
|
"youtube:gift_membership_received",
|
|
142
150
|
"youtube:poll",
|
|
143
|
-
//
|
|
151
|
+
// YouTube stream lifecycle (ZAF-1444 / ZAF-622 §75) — emitted by the poll-driven
|
|
152
|
+
// YouTube worker on the channel-status online/offline edge, mirroring Twitch/Kick.
|
|
153
|
+
"youtube:stream_online",
|
|
154
|
+
"youtube:stream_offline",
|
|
155
|
+
// Kick — the webhook receiver normalizes every Kick payload into these types
|
|
156
|
+
// (ZAF-1624): canonical fields on top, the original payload kept in `raw`.
|
|
144
157
|
"kick:follower",
|
|
145
158
|
"kick:subscribe",
|
|
159
|
+
"kick:resub",
|
|
146
160
|
"kick:gift",
|
|
161
|
+
"kick:kicks",
|
|
162
|
+
"kick:reward",
|
|
163
|
+
"kick:reward_status",
|
|
147
164
|
"kick:stream_online",
|
|
148
165
|
"kick:stream_offline",
|
|
149
|
-
|
|
150
|
-
"trovo:subscribe",
|
|
151
|
-
"trovo:spell",
|
|
166
|
+
"kick:stream_update",
|
|
152
167
|
// Spotify
|
|
153
168
|
"spotify:track",
|
|
154
169
|
"spotify:play",
|
|
@@ -168,7 +183,6 @@ var LUMIO_EVENT_TYPES = [
|
|
|
168
183
|
"twitch:chat",
|
|
169
184
|
"youtube:chat",
|
|
170
185
|
"kick:chat",
|
|
171
|
-
"trovo:chat",
|
|
172
186
|
// Chat (cross-platform catch-all — fires for ALL platforms)
|
|
173
187
|
"chat:message"
|
|
174
188
|
];
|
|
@@ -268,6 +282,7 @@ var HOST_METHODS = {
|
|
|
268
282
|
};
|
|
269
283
|
export {
|
|
270
284
|
ACTION_TYPES,
|
|
285
|
+
EXTENSION_RUNTIME_LABEL_PLACEHOLDER,
|
|
271
286
|
HOST_METHODS,
|
|
272
287
|
LUMIO_COMPONENT_TYPES,
|
|
273
288
|
LUMIO_EVENT_TYPES,
|
|
@@ -279,7 +294,11 @@ export {
|
|
|
279
294
|
errorMessageSchema,
|
|
280
295
|
eventMessageSchema,
|
|
281
296
|
extensionConfigSchema,
|
|
297
|
+
extensionRuntimeLabel,
|
|
298
|
+
extensionRuntimeOrigin,
|
|
299
|
+
extensionRuntimeOriginLabel,
|
|
282
300
|
initMessageSchema,
|
|
301
|
+
isValidExtensionRuntimeOriginTemplate,
|
|
283
302
|
lumioActionTypeSchema,
|
|
284
303
|
lumioEventTypeSchema,
|
|
285
304
|
lumioMessageSchema,
|
package/dist/origin.d.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/** Placeholder that a runtime origin template carries in place of the label. */
|
|
2
|
+
declare const EXTENSION_RUNTIME_LABEL_PLACEHOLDER = "{label}";
|
|
3
|
+
/**
|
|
4
|
+
* The runtime host label for an extension: its UUID as 32 lowercase hex
|
|
5
|
+
* characters. Returns `null` unless `extensionId` is a canonical lowercase,
|
|
6
|
+
* hyphenated UUID.
|
|
7
|
+
*/
|
|
8
|
+
declare function extensionRuntimeLabel(extensionId: string): string | null;
|
|
9
|
+
/**
|
|
10
|
+
* The runtime origin for an extension under a template such as
|
|
11
|
+
* `https://{label}.lumio.ext.staging.zaflun.dev`. Returns `null` for an invalid
|
|
12
|
+
* extension id or an invalid template.
|
|
13
|
+
*/
|
|
14
|
+
declare function extensionRuntimeOrigin(extensionId: string, template: string): string | null;
|
|
15
|
+
/**
|
|
16
|
+
* If `origin` is a runtime origin under `template`, returns its 32-hex label;
|
|
17
|
+
* otherwise `null`. Used where the extension id is not known, e.g. the runtime
|
|
18
|
+
* checking its own `location.origin`.
|
|
19
|
+
*/
|
|
20
|
+
declare function extensionRuntimeOriginLabel(origin: string, template: string): string | null;
|
|
21
|
+
/** Whether `template` is a usable runtime origin template. */
|
|
22
|
+
declare function isValidExtensionRuntimeOriginTemplate(template: string): boolean;
|
|
23
|
+
|
|
24
|
+
export { EXTENSION_RUNTIME_LABEL_PLACEHOLDER, extensionRuntimeLabel, extensionRuntimeOrigin, extensionRuntimeOriginLabel, isValidExtensionRuntimeOriginTemplate };
|
package/dist/origin.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EXTENSION_RUNTIME_LABEL_PLACEHOLDER,
|
|
3
|
+
extensionRuntimeLabel,
|
|
4
|
+
extensionRuntimeOrigin,
|
|
5
|
+
extensionRuntimeOriginLabel,
|
|
6
|
+
isValidExtensionRuntimeOriginTemplate
|
|
7
|
+
} from "./chunk-746VSR4E.js";
|
|
8
|
+
export {
|
|
9
|
+
EXTENSION_RUNTIME_LABEL_PLACEHOLDER,
|
|
10
|
+
extensionRuntimeLabel,
|
|
11
|
+
extensionRuntimeOrigin,
|
|
12
|
+
extensionRuntimeOriginLabel,
|
|
13
|
+
isValidExtensionRuntimeOriginTemplate
|
|
14
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zaflun/lumio-protocol",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Wire-format types for Lumio extension iframe-to-host communication",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
".": {
|
|
10
10
|
"import": "./dist/index.js",
|
|
11
11
|
"types": "./dist/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"./origin": {
|
|
14
|
+
"import": "./dist/origin.js",
|
|
15
|
+
"types": "./dist/origin.d.ts"
|
|
12
16
|
}
|
|
13
17
|
},
|
|
14
18
|
"publishConfig": {
|
|
@@ -42,7 +46,7 @@
|
|
|
42
46
|
"email": "developers@zaflun.com"
|
|
43
47
|
},
|
|
44
48
|
"scripts": {
|
|
45
|
-
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
49
|
+
"build": "tsup src/index.ts src/origin.ts --format esm --dts --clean",
|
|
46
50
|
"typecheck": "tsc --noEmit",
|
|
47
51
|
"lint": "eslint src/",
|
|
48
52
|
"test": "vitest run"
|