@overlaysymphony/core 0.1.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/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # `@overlaysymphony/core`
2
+
3
+ The is the core module for the [OverlaySymphony interactive streaming framework](https://github.com/OverlaySymphony/overlaysymphony), though at the moment it only includes a few utility helpers.
4
+
5
+ **You should not need to use this package directly.**
6
+
7
+ # OverlaySymphony
8
+
9
+ An interactive streaming framework for orchestrating overlays, bots, and other services.
10
+
11
+ > Note: This project is still very much under development. I use it actively on my own stream, so all releases should be fully functional. I will do my best to follow semver, though I cannot make any guarantees about release timelines or stability at this time.
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@overlaysymphony/core",
3
+ "version": "0.1.0",
4
+ "description": "Core module for the OverlaySymphony interactive streaming framework.",
5
+ "homepage": "https://github.com/OverlaySymphony/overlaysymphony",
6
+ "type": "module",
7
+ "exports": {
8
+ "./libs/*": "./src/libs/*/index.js",
9
+ "./ui/*": "./src/ui/*.js",
10
+ "./package.json": "./package.json"
11
+ },
12
+ "scripts": {
13
+ "lint": "pnpm run \"/^lint-.*/\"",
14
+ "lint-typecheck": "tsc --noEmit",
15
+ "lint-eslint": "eslint --ext .js --ext .jsx --ext .ts --ext .tsx .",
16
+ "lint-prettier": "prettier --check .",
17
+ "lint-depcheck": "depcheck .",
18
+ "test": "vitest",
19
+ "dev": "echo TODO",
20
+ "clean": "rm -rf node_modules/.cache tsconfig.tsbuildinfo dist",
21
+ "build": "echo TODO"
22
+ },
23
+ "devDependencies": {
24
+ "@overlaysymphony/eslint-config": "workspace:*",
25
+ "@overlaysymphony/tooling": "workspace:*",
26
+ "@vitest/coverage-v8": "^2.1.2",
27
+ "depcheck": "catalog:",
28
+ "eslint": "catalog:",
29
+ "prettier": "catalog:",
30
+ "typescript": "catalog:",
31
+ "vitest": "^2.1.2"
32
+ }
33
+ }
@@ -0,0 +1,16 @@
1
+ export default function createBroadcaster<Data>(
2
+ name: string,
3
+ handler?: (data: Data) => void,
4
+ ): (data: Data) => void {
5
+ const channel = new BroadcastChannel(name)
6
+
7
+ if (handler) {
8
+ channel.onmessage = (_event) => {
9
+ handler(_event.data as Data)
10
+ }
11
+ }
12
+
13
+ return (data: Data) => {
14
+ channel.postMessage(data)
15
+ }
16
+ }
@@ -0,0 +1 @@
1
+ export { default } from "./broadcast.js"
@@ -0,0 +1,19 @@
1
+ export default function createDefer<Data = void>(): {
2
+ promise: Promise<Data>
3
+ resolve: (value: Data) => void
4
+ reject: (reason?: string) => void
5
+ } {
6
+ let resolve: ((value: Data) => void) | undefined = undefined
7
+ let reject: ((reason?: string) => void) | undefined = undefined
8
+
9
+ const promise = new Promise<Data>((resolve_, reject_) => {
10
+ resolve = resolve_
11
+ reject = reject_
12
+ })
13
+
14
+ return {
15
+ promise,
16
+ resolve: resolve as Exclude<typeof resolve, undefined>,
17
+ reject: reject as Exclude<typeof reject, undefined>,
18
+ }
19
+ }
@@ -0,0 +1 @@
1
+ export { default } from "./defer.js"
@@ -0,0 +1,2 @@
1
+ export { default } from "./pubsub.js"
2
+ export * from "./pubsub.js"
@@ -0,0 +1,36 @@
1
+ type Handler<Data> = (data: Data) => void
2
+
3
+ type Subscriber<Data> = (handler: (data: Data) => void) => () => void
4
+ type Dispatcher<Data> = (data: Data) => void
5
+
6
+ export interface PubSub<Data> {
7
+ subscribe: Subscriber<Data>
8
+ dispatch: Dispatcher<Data>
9
+ }
10
+
11
+ export default function createPubSub<Data>(): PubSub<Data> {
12
+ const handlers: Array<Handler<Data>> = []
13
+
14
+ function subscribe(handler: Handler<Data>) {
15
+ handlers.push(handler)
16
+
17
+ return () => {
18
+ const index = handlers.indexOf(handler)
19
+ if (index === -1) return false
20
+
21
+ handlers.splice(index, 1)
22
+ return true
23
+ }
24
+ }
25
+
26
+ function dispatch(data: Data) {
27
+ for (const handler of handlers) {
28
+ handler(data)
29
+ }
30
+ }
31
+
32
+ return {
33
+ subscribe,
34
+ dispatch,
35
+ }
36
+ }
@@ -0,0 +1,2 @@
1
+ export { default } from "./querystring.js"
2
+ export * from "./querystring.js"
@@ -0,0 +1,37 @@
1
+ type ParsedQuerystring = Record<
2
+ string,
3
+ string | number | boolean | null | undefined
4
+ >
5
+
6
+ export function stringify(input: ParsedQuerystring): string {
7
+ const output: string[] = []
8
+
9
+ for (const [key, value] of Object.entries(input)) {
10
+ if (typeof value !== "undefined" && value !== null) {
11
+ output.push(`${key}=${value}`)
12
+ }
13
+ }
14
+
15
+ return output.join("&")
16
+ }
17
+
18
+ export function parse(input: string): ParsedQuerystring {
19
+ input = decodeURIComponent(input)
20
+ if (input[0] === "#") input = input.slice(1)
21
+ if (input[0] === "?") input = input.slice(1)
22
+
23
+ if (input.length === 0) {
24
+ return {}
25
+ }
26
+
27
+ const output: ParsedQuerystring = {}
28
+
29
+ for (const pair of input.split("&")) {
30
+ const [key, value] = pair.split("=")
31
+ output[key] = value
32
+ }
33
+
34
+ return output
35
+ }
36
+
37
+ export default { stringify, parse }
@@ -0,0 +1,2 @@
1
+ export { default } from "./queue.js"
2
+ export * from "./queue.js"
@@ -0,0 +1,71 @@
1
+ type Handler<Data> = (data: Data) => void
2
+
3
+ type Listener<Data> = (handler: Handler<Data>) => () => void
4
+ type Enqueuer<Data> = (priority: number, data: Data) => void
5
+ type Dismisser = () => void
6
+
7
+ export interface Queue<Data> {
8
+ listen: Listener<Data>
9
+ enqueue: Enqueuer<Data>
10
+ dismiss: Dismisser
11
+ }
12
+
13
+ interface QueueItem<Data> {
14
+ priority: number
15
+ data: Data
16
+ }
17
+
18
+ export default function createQueue<Data>(): Queue<Data> {
19
+ const handlers: Array<Handler<Data>> = []
20
+ const queue: Array<QueueItem<Data>> = []
21
+ let current: { data: Data } | undefined = undefined
22
+
23
+ function dispatch(data: Data | undefined) {
24
+ if (typeof data === "undefined") {
25
+ current = undefined
26
+ return
27
+ }
28
+
29
+ current = { data }
30
+ for (const handler of handlers) {
31
+ handler(current.data)
32
+ }
33
+ }
34
+
35
+ function listen(handler: Handler<Data>) {
36
+ handlers.push(handler)
37
+
38
+ return () => {
39
+ const index = handlers.indexOf(handler)
40
+ if (index === -1) return false
41
+
42
+ handlers.splice(index, 1)
43
+ return true
44
+ }
45
+ }
46
+
47
+ function enqueue(priority: number, data: Data) {
48
+ if (!current) {
49
+ return dispatch(data)
50
+ }
51
+
52
+ const after = queue.findIndex((item) => item.priority < priority)
53
+
54
+ if (after === -1) {
55
+ queue.push({ priority, data })
56
+ } else {
57
+ queue.splice(after, 0, { priority, data })
58
+ }
59
+ }
60
+
61
+ function dismiss() {
62
+ const { data } = queue.shift() || {}
63
+ return dispatch(data)
64
+ }
65
+
66
+ return {
67
+ listen,
68
+ enqueue,
69
+ dismiss,
70
+ }
71
+ }
File without changes