@openbrf/display 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/LICENSE-EXCEPTION.md +28 -0
- package/README.md +126 -2
- package/dist/contract.d.ts +244 -0
- package/dist/contract.d.ts.map +1 -0
- package/dist/contract.js +54 -0
- package/dist/contract.js.map +1 -0
- package/dist/fetch-loop.d.ts +87 -0
- package/dist/fetch-loop.d.ts.map +1 -0
- package/dist/fetch-loop.js +351 -0
- package/dist/fetch-loop.js.map +1 -0
- package/dist/fit.d.ts +44 -0
- package/dist/fit.d.ts.map +1 -0
- package/dist/fit.js +257 -0
- package/dist/fit.js.map +1 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/parse.d.ts +39 -0
- package/dist/parse.d.ts.map +1 -0
- package/dist/parse.js +494 -0
- package/dist/parse.js.map +1 -0
- package/dist/renderer.d.ts +85 -0
- package/dist/renderer.d.ts.map +1 -0
- package/dist/renderer.js +476 -0
- package/dist/renderer.js.map +1 -0
- package/dist/rotation.d.ts +129 -0
- package/dist/rotation.d.ts.map +1 -0
- package/dist/rotation.js +335 -0
- package/dist/rotation.js.map +1 -0
- package/dist/slides.d.ts +41 -0
- package/dist/slides.d.ts.map +1 -0
- package/dist/slides.js +341 -0
- package/dist/slides.js.map +1 -0
- package/dist/styles.d.ts +10 -0
- package/dist/styles.d.ts.map +1 -0
- package/dist/styles.js +107 -0
- package/dist/styles.js.map +1 -0
- package/dist/text.d.ts +16 -0
- package/dist/text.d.ts.map +1 -0
- package/dist/text.js +62 -0
- package/dist/text.js.map +1 -0
- package/dist/time.d.ts +39 -0
- package/dist/time.d.ts.map +1 -0
- package/dist/time.js +94 -0
- package/dist/time.js.map +1 -0
- package/dist/translate.d.ts +18 -0
- package/dist/translate.d.ts.map +1 -0
- package/dist/translate.js +35 -0
- package/dist/translate.js.map +1 -0
- package/dist/words.d.ts +41 -0
- package/dist/words.d.ts.map +1 -0
- package/dist/words.js +73 -0
- package/dist/words.js.map +1 -0
- package/package.json +58 -3
- package/src/contract.ts +279 -0
- package/src/fetch-loop.ts +472 -0
- package/src/fit.ts +348 -0
- package/src/index.ts +77 -0
- package/src/parse.ts +605 -0
- package/src/renderer.ts +708 -0
- package/src/rotation.ts +515 -0
- package/src/slides.ts +595 -0
- package/src/styles.ts +113 -0
- package/src/text.ts +65 -0
- package/src/time.ts +147 -0
- package/src/translate.ts +56 -0
- package/src/words.ts +91 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Open BRF Module Exception
|
|
2
|
+
|
|
3
|
+
Version 1.0
|
|
4
|
+
|
|
5
|
+
## The exception
|
|
6
|
+
|
|
7
|
+
As an additional permission under section 7 of the GNU Affero General Public License, version 3 ("AGPL-3.0"), the copyright holders of Open BRF give you permission to combine Open BRF with modules.
|
|
8
|
+
|
|
9
|
+
A "module" is a plugin, theme, or other extension package that interacts with the Open BRF core exclusively through the documented public extension interfaces: the plugin API, the theme/token contract, the public HTTP API, and the extension points declared in the module manifest format (together, the "Module API").
|
|
10
|
+
|
|
11
|
+
If you create and convey a module:
|
|
12
|
+
|
|
13
|
+
1. The module is **not** considered a work based on Open BRF for the purposes of AGPL-3.0, regardless of the linking or loading mechanism.
|
|
14
|
+
2. You may license the module under terms of your own choosing, including proprietary terms; nothing requires you to convey the module's source code.
|
|
15
|
+
3. Running a combined work consisting of an unmodified Open BRF core and one or more modules, including offering it to users over a network, does not extend the obligations of AGPL-3.0 (including section 13) to the modules.
|
|
16
|
+
|
|
17
|
+
This exception does **not** apply to:
|
|
18
|
+
|
|
19
|
+
- Modifications of the Open BRF core itself, which remain governed by AGPL-3.0 in full. A "module" that patches, replaces, or bundles modified core code is a modification of the core, not a module.
|
|
20
|
+
- The obligations of AGPL-3.0 regarding the core: anyone conveying or network-hosting a modified core must still offer its Corresponding Source.
|
|
21
|
+
|
|
22
|
+
As provided by section 7 of AGPL-3.0, you may remove this additional permission from your copy, but you may not add further restrictions.
|
|
23
|
+
|
|
24
|
+
## Why this exists
|
|
25
|
+
|
|
26
|
+
The legal status of in-process plugins to (A)GPL software is a decades-old gray zone. Rather than relying on it, Open BRF states the rule explicitly, in both directions: the core is and remains copyleft, and the module ecosystem - community and commercial alike - is legally safe to build in. Vendure pioneered this model for GPL; Open BRF applies it to AGPL.
|
|
27
|
+
|
|
28
|
+
This exception is granted by Apteo AB as copyright holder of Open BRF. Contributions are accepted under a [CLA](CLA.md) precisely so that this grant remains valid for the whole codebase.
|
package/README.md
CHANGED
|
@@ -1,3 +1,127 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @openbrf/display
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
What every screen showing a housing cooperative's content is built from, in
|
|
4
|
+
[Open BRF](https://github.com/openbrf/openbrf): the screen content contract,
|
|
5
|
+
the rotation builder, the renderer and the fetch loop. The core's display mode
|
|
6
|
+
(a browser opened on a display link) and the digital signage module's player
|
|
7
|
+
both use all four, so a screen shows the association's news, events, the
|
|
8
|
+
notice of the next general meeting and free/busy availability the same way
|
|
9
|
+
whichever of them it runs.
|
|
10
|
+
|
|
11
|
+
It uses no framework and no DOM library, and has one dependency,
|
|
12
|
+
[`@openbrf/tokens`](https://www.npmjs.com/package/@openbrf/tokens), for the
|
|
13
|
+
default theme's colours.
|
|
14
|
+
|
|
15
|
+
## Installing
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
npm install @openbrf/display
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## The four parts
|
|
22
|
+
|
|
23
|
+
1. **The screen content contract.** The `ScreenContent` document, version 1,
|
|
24
|
+
as types (`ScreenContent`, `Slide`, the body kinds), its limits
|
|
25
|
+
(`SCREEN_CONTENT_LIMITS`, `SLIDE_ID_PATTERN`), and `parseScreenContent`,
|
|
26
|
+
which reads a document by the player's rules: unknown fields are ignored,
|
|
27
|
+
a slide of an unknown or malformed kind is skipped, and an unknown
|
|
28
|
+
`version` is refused with a `ScreenContentError`.
|
|
29
|
+
2. **The rotation builder.** `buildRotation` turns the news items,
|
|
30
|
+
occurrences, meeting notices, bookable resources and busy intervals a
|
|
31
|
+
caller has read, and the moment of the fetch, into the `news`, `events`,
|
|
32
|
+
`meeting` and `availability` slides. It reads nothing on its own.
|
|
33
|
+
`availabilityWindow` gives the window an availability slide covers: from
|
|
34
|
+
the minute of the fetch to the end of the next day in the association's
|
|
35
|
+
time zone.
|
|
36
|
+
3. **The renderer.** `mountRenderer` shows a document on an element: the
|
|
37
|
+
rotation with each slide's `seconds` and `until`, the schedule, the clock
|
|
38
|
+
band, the orientation, the theme and every body kind, in Swedish or
|
|
39
|
+
English. Nothing a slide holds is cut off: a slide the screen cannot hold
|
|
40
|
+
whole is shown a page at a time, each page for the slide's `seconds`. A
|
|
41
|
+
list, such as eight events, goes as many items to a page as fit whole; a
|
|
42
|
+
page still too tall is set smaller, and a text too tall even then, such as
|
|
43
|
+
a news item of fifty paragraphs, continues on the next page, broken between
|
|
44
|
+
two words, or a word longer than a page between two of its characters. It
|
|
45
|
+
fetches nothing; a picture comes from a function you hand it. It adds its stylesheet (`DISPLAY_STYLESHEET`, every class prefixed
|
|
46
|
+
`obrf-display`) to the document once, as a `<style>` element. A page whose
|
|
47
|
+
content security policy has no `'unsafe-inline'` in `style-src` serves the
|
|
48
|
+
stylesheet as a file instead: it links the file as
|
|
49
|
+
`<link rel="stylesheet" data-obrf-display href="...">` and passes
|
|
50
|
+
`stylesheet: false` to `mountRenderer`. The renderer then adds no `<style>`
|
|
51
|
+
element and sets no `style` attribute, only properties through the style
|
|
52
|
+
object, which such a policy allows. A page that has a `link` or `style` with
|
|
53
|
+
`data-obrf-display` in its head gets no second copy either way.
|
|
54
|
+
4. **The fetch loop.** `startFetchLoop` asks for screen content with a device
|
|
55
|
+
token and acts on each answer: 200 and 304 show content, a 401 with
|
|
56
|
+
`realm="device"` and `device-token-invalid` forgets it and stops, 404
|
|
57
|
+
pauses, 429 waits, and anything else shows the last content for at most
|
|
58
|
+
24 hours. Where the content is cached is yours to choose.
|
|
59
|
+
|
|
60
|
+
## Usage
|
|
61
|
+
|
|
62
|
+
```ts
|
|
63
|
+
import { mountRenderer, startFetchLoop } from "@openbrf/display";
|
|
64
|
+
|
|
65
|
+
const renderer = mountRenderer(document.getElementById("screen")!, {
|
|
66
|
+
// Fetch the picture with the token in a header, never in its URL.
|
|
67
|
+
picture: async (ref) => {
|
|
68
|
+
const response = await fetch(
|
|
69
|
+
new URL(`pictures/${encodeURIComponent(ref.mediaFileId)}`, contentUrl),
|
|
70
|
+
{ headers: { Authorization: `Bearer ${token}` }, credentials: "omit" },
|
|
71
|
+
);
|
|
72
|
+
return response.ok ? URL.createObjectURL(await response.blob()) : null;
|
|
73
|
+
},
|
|
74
|
+
releasePicture: (url) => URL.revokeObjectURL(url),
|
|
75
|
+
});
|
|
76
|
+
renderer.message("loading", "sv");
|
|
77
|
+
|
|
78
|
+
startFetchLoop({
|
|
79
|
+
url: contentUrl,
|
|
80
|
+
token: () => localStorage.getItem("token"),
|
|
81
|
+
cache: {
|
|
82
|
+
read: () => JSON.parse(localStorage.getItem("content") ?? "null"),
|
|
83
|
+
write: (entry) => localStorage.setItem("content", JSON.stringify(entry)),
|
|
84
|
+
clear: () => localStorage.removeItem("content"),
|
|
85
|
+
},
|
|
86
|
+
onContent: (content, status) => renderer.show(content, status),
|
|
87
|
+
onRevoked: () => {
|
|
88
|
+
localStorage.removeItem("token");
|
|
89
|
+
renderer.message("link-invalid", "sv");
|
|
90
|
+
},
|
|
91
|
+
onPaused: () => renderer.message("paused", "sv"),
|
|
92
|
+
onDisconnected: () => renderer.message("not-connected", "sv"),
|
|
93
|
+
});
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Give the element a size, such as the whole viewport: the canvas fills it and
|
|
97
|
+
is turned by the document's orientation.
|
|
98
|
+
|
|
99
|
+
## Browsers
|
|
100
|
+
|
|
101
|
+
The renderer and its stylesheet run in Chromium 85, the browser of Samsung's
|
|
102
|
+
2021 Tizen displays, without a shim: the package calls nothing newer and writes
|
|
103
|
+
no CSS newer. A test fails when one of the newer calls or CSS features it
|
|
104
|
+
lists, such as `replaceChildren`, `Array.prototype.at` or `inset`, is added;
|
|
105
|
+
anything it does not list is for review to catch. A word longer than a page
|
|
106
|
+
is broken between characters with `Intl.Segmenter` where the browser has it
|
|
107
|
+
(Chromium 87), and between code points where it does not.
|
|
108
|
+
|
|
109
|
+
## Versions
|
|
110
|
+
|
|
111
|
+
The package carries version 1 of the screen content contract. A field or a
|
|
112
|
+
body kind may be added within version 1, and an older renderer ignores what it
|
|
113
|
+
does not know. A change an older renderer would misread raises the contract's
|
|
114
|
+
version, which is served beside version 1, and the package then carries both.
|
|
115
|
+
|
|
116
|
+
## Documentation
|
|
117
|
+
|
|
118
|
+
- [The screen content contract](https://github.com/openbrf/openbrf/blob/main/docs/screen-content-contract.md)
|
|
119
|
+
- [ADR 0050: the display mode, and one renderer for every screen](https://github.com/openbrf/openbrf/blob/main/docs/adr/0050-the-display-mode-and-one-renderer-for-every-screen.md)
|
|
120
|
+
- [ADR 0034: digital signage on the public plugin API](https://github.com/openbrf/openbrf/blob/main/docs/adr/0034-digital-signage-on-the-public-plugin-api.md)
|
|
121
|
+
|
|
122
|
+
## Licence
|
|
123
|
+
|
|
124
|
+
AGPL-3.0-only, with the
|
|
125
|
+
[Open BRF Module Exception](https://github.com/openbrf/openbrf/blob/main/LICENSE-EXCEPTION.md).
|
|
126
|
+
A player or shell built on this package is a module under that exception and
|
|
127
|
+
may be licensed on any terms. Both texts are in the package.
|
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The screen content contract, version 1: the document a screen fetches and
|
|
3
|
+
* shows, as ADR 0034 defined it and ADR 0050 made the core's.
|
|
4
|
+
*
|
|
5
|
+
* The text of record is docs/screen-content-contract.md. These types and
|
|
6
|
+
* limits are that text in code, and a change to either is a change to the
|
|
7
|
+
* contract under its version rules: a field or a body kind may be added within
|
|
8
|
+
* version 1, because an older player ignores what it does not know, and a
|
|
9
|
+
* change an older player would misread raises the version.
|
|
10
|
+
*
|
|
11
|
+
* "Characters" in a limit are Unicode code points. A server that measures in
|
|
12
|
+
* UTF-16 code units, as JavaScript's `length` does, never exceeds a limit
|
|
13
|
+
* measured in code points, so the builder in this package measures in code
|
|
14
|
+
* units and the parser in code points.
|
|
15
|
+
*/
|
|
16
|
+
/** The version of the document this package reads and builds. */
|
|
17
|
+
export declare const SCREEN_CONTENT_VERSION = 1;
|
|
18
|
+
/** An instant: RFC 3339 in UTC, whole seconds, with "Z": "2026-12-01T09:00:00Z". */
|
|
19
|
+
export type Instant = string;
|
|
20
|
+
/** A time of day, "HH:MM" from "00:00" to "24:00", in the document's `timeZone`. */
|
|
21
|
+
export type LocalTime = string;
|
|
22
|
+
/** The languages a screen speaks. */
|
|
23
|
+
export type ScreenLocale = "sv" | "en";
|
|
24
|
+
/** An ISO weekday: 1 is Monday and 7 is Sunday. */
|
|
25
|
+
export type IsoWeekday = 1 | 2 | 3 | 4 | 5 | 6 | 7;
|
|
26
|
+
/** How the screen lays out its canvas. */
|
|
27
|
+
export interface ScreenLayout {
|
|
28
|
+
/** Degrees clockwise the player turns its canvas. */
|
|
29
|
+
orientation: 0 | 90 | 180 | 270;
|
|
30
|
+
/** A band above the slides with the date and the time, or none. */
|
|
31
|
+
header: "none" | "clock";
|
|
32
|
+
theme: "light" | "dark";
|
|
33
|
+
}
|
|
34
|
+
/** One window in which the screen shows the rotation. */
|
|
35
|
+
export interface ScreenScheduleWindow {
|
|
36
|
+
days: IsoWeekday[];
|
|
37
|
+
/** Inclusive. */
|
|
38
|
+
from: LocalTime;
|
|
39
|
+
/** Exclusive, and after `from`. A window over midnight is two windows. */
|
|
40
|
+
to: LocalTime;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* When the screen shows the rotation. Outside every window it shows black;
|
|
44
|
+
* with no window at all it shows the rotation always.
|
|
45
|
+
*/
|
|
46
|
+
export interface ScreenSchedule {
|
|
47
|
+
/** At most 14. */
|
|
48
|
+
on: ScreenScheduleWindow[];
|
|
49
|
+
}
|
|
50
|
+
/** One kind of thing in a screen's rotation (GLOSSARY.md, "source"). */
|
|
51
|
+
export type SlideSource = "news" | "events" | "meeting" | "availability" | "picture" | "addon";
|
|
52
|
+
/** A picture the screen may show, fetched by its `mediaFileId`. */
|
|
53
|
+
export interface PictureRef {
|
|
54
|
+
mediaFileId: string;
|
|
55
|
+
/** The image type the core identified from the bytes. */
|
|
56
|
+
contentType: string;
|
|
57
|
+
byteSize: number;
|
|
58
|
+
/** Opaque, and changes when the file does: the picture's ETag. */
|
|
59
|
+
version: string;
|
|
60
|
+
/** At most 300 characters. */
|
|
61
|
+
alt: string;
|
|
62
|
+
}
|
|
63
|
+
/** A published news item. */
|
|
64
|
+
export interface NewsBody {
|
|
65
|
+
kind: "news";
|
|
66
|
+
/** At most 200 characters. */
|
|
67
|
+
title: string;
|
|
68
|
+
/** At most 2 000 characters together. */
|
|
69
|
+
paragraphs: string[];
|
|
70
|
+
picture: PictureRef | null;
|
|
71
|
+
publishedAt: Instant;
|
|
72
|
+
}
|
|
73
|
+
/** One occurrence of a published event, as an events slide lists it. */
|
|
74
|
+
export interface EventsOccurrence {
|
|
75
|
+
/** At most 200 characters. */
|
|
76
|
+
title: string;
|
|
77
|
+
/** At most 500 characters. */
|
|
78
|
+
description: string | null;
|
|
79
|
+
/** At most 200 characters. */
|
|
80
|
+
location: string | null;
|
|
81
|
+
startsAt: Instant;
|
|
82
|
+
endsAt: Instant;
|
|
83
|
+
calledOff: boolean;
|
|
84
|
+
placesLeft: number | null;
|
|
85
|
+
}
|
|
86
|
+
/** The coming occurrences of published events. */
|
|
87
|
+
export interface EventsBody {
|
|
88
|
+
kind: "events";
|
|
89
|
+
/** 1 to 8, soonest first. */
|
|
90
|
+
occurrences: EventsOccurrence[];
|
|
91
|
+
}
|
|
92
|
+
/** One matter on a general meeting's agenda. */
|
|
93
|
+
export interface AgendaItem {
|
|
94
|
+
position: number;
|
|
95
|
+
/** At most 200 characters. */
|
|
96
|
+
title: string;
|
|
97
|
+
}
|
|
98
|
+
/** The notice of a general meeting the board has called. */
|
|
99
|
+
export interface MeetingBody {
|
|
100
|
+
kind: "meeting";
|
|
101
|
+
meetingKind: "ordinary" | "extraordinary";
|
|
102
|
+
startsAt: Instant;
|
|
103
|
+
/** At most 200 characters. */
|
|
104
|
+
place: string;
|
|
105
|
+
/** At most 500 characters. */
|
|
106
|
+
digitalParticipation: string | null;
|
|
107
|
+
/** At most 40. */
|
|
108
|
+
agenda: AgendaItem[];
|
|
109
|
+
}
|
|
110
|
+
/** A period a bookable resource is taken. Never by whom. */
|
|
111
|
+
export interface BusyInterval {
|
|
112
|
+
start: Instant;
|
|
113
|
+
end: Instant;
|
|
114
|
+
}
|
|
115
|
+
/** One bookable resource on an availability slide. */
|
|
116
|
+
export interface AvailabilityResource {
|
|
117
|
+
/** At most 80 characters. */
|
|
118
|
+
name: string;
|
|
119
|
+
mode: "timeSlots" | "wholeDay" | "dateRange";
|
|
120
|
+
slotMinutes: number | null;
|
|
121
|
+
/** Minutes after midnight in the document's `timeZone`. */
|
|
122
|
+
opensAtMinute: number | null;
|
|
123
|
+
closesAtMinute: number | null;
|
|
124
|
+
/** Merged, sorted, and inside [from, to). */
|
|
125
|
+
busy: BusyInterval[];
|
|
126
|
+
}
|
|
127
|
+
/** When the house's bookable resources are free and busy. */
|
|
128
|
+
export interface AvailabilityBody {
|
|
129
|
+
kind: "availability";
|
|
130
|
+
/** The fetch, to the minute; never earlier. */
|
|
131
|
+
from: Instant;
|
|
132
|
+
/** At most 7 days after `from`. */
|
|
133
|
+
to: Instant;
|
|
134
|
+
/** 1 to 12. */
|
|
135
|
+
resources: AvailabilityResource[];
|
|
136
|
+
}
|
|
137
|
+
/** A picture the board chose for the screen. */
|
|
138
|
+
export interface PictureBody {
|
|
139
|
+
kind: "picture";
|
|
140
|
+
picture: PictureRef;
|
|
141
|
+
/** At most 200 characters. */
|
|
142
|
+
caption: string | null;
|
|
143
|
+
}
|
|
144
|
+
/** An addon's text slide. */
|
|
145
|
+
export interface AddonTextBody {
|
|
146
|
+
kind: "text";
|
|
147
|
+
heading: string;
|
|
148
|
+
/** At most 2 000 characters together. */
|
|
149
|
+
body: string[];
|
|
150
|
+
}
|
|
151
|
+
/** An addon's table slide. */
|
|
152
|
+
export interface AddonTableBody {
|
|
153
|
+
kind: "table";
|
|
154
|
+
heading: string;
|
|
155
|
+
/** At most 4. */
|
|
156
|
+
columns: string[];
|
|
157
|
+
/** At most 60, each as long as `columns`. */
|
|
158
|
+
rows: string[][];
|
|
159
|
+
}
|
|
160
|
+
/** One door on an addon's doors slide. */
|
|
161
|
+
export interface AddonDoor {
|
|
162
|
+
floor: number | null;
|
|
163
|
+
apartment: string;
|
|
164
|
+
names: string[];
|
|
165
|
+
}
|
|
166
|
+
/** An addon's doors slide: the names on the doors of one entrance. */
|
|
167
|
+
export interface AddonDoorsBody {
|
|
168
|
+
kind: "doors";
|
|
169
|
+
heading: string;
|
|
170
|
+
/** At most 120. */
|
|
171
|
+
doors: AddonDoor[];
|
|
172
|
+
}
|
|
173
|
+
/** One slide of an addon's output at the `source` extension point. */
|
|
174
|
+
export type AddonBody = AddonTextBody | AddonTableBody | AddonDoorsBody;
|
|
175
|
+
/** What a slide shows. Its `kind` matches the slide's `source`. */
|
|
176
|
+
export type SlideBody = NewsBody | EventsBody | MeetingBody | AvailabilityBody | PictureBody | AddonBody;
|
|
177
|
+
/** One slide of the rotation. */
|
|
178
|
+
export interface Slide {
|
|
179
|
+
/** 1 to 64 of A-Z a-z 0-9 . _ : -, the same on every fetch while the slide shows the same thing. */
|
|
180
|
+
id: string;
|
|
181
|
+
/** Integer, 5 to 120: how long it stays. */
|
|
182
|
+
seconds: number;
|
|
183
|
+
/** The player skips the slide from this instant, without waiting for a fetch. */
|
|
184
|
+
until: Instant | null;
|
|
185
|
+
source: SlideSource;
|
|
186
|
+
/** The addon's plugin id when `source` is "addon", otherwise null. */
|
|
187
|
+
addon: string | null;
|
|
188
|
+
body: SlideBody;
|
|
189
|
+
}
|
|
190
|
+
/** The screen content document, version 1. */
|
|
191
|
+
export interface ScreenContent {
|
|
192
|
+
version: 1;
|
|
193
|
+
/** The device token's subject: the screen's id. */
|
|
194
|
+
screenId: string;
|
|
195
|
+
generatedAt: Instant;
|
|
196
|
+
/** Integer, 30 to 60. */
|
|
197
|
+
refreshSeconds: number;
|
|
198
|
+
locale: ScreenLocale;
|
|
199
|
+
/** IANA zone of the association's calendar day: "Europe/Stockholm". */
|
|
200
|
+
timeZone: string;
|
|
201
|
+
/** The only channel a paired screen's shell takes player releases from. */
|
|
202
|
+
releaseChannel: "stable" | "beta";
|
|
203
|
+
layout: ScreenLayout;
|
|
204
|
+
schedule: ScreenSchedule;
|
|
205
|
+
/** In order, at most 100; the player loops through them. */
|
|
206
|
+
rotation: Slide[];
|
|
207
|
+
}
|
|
208
|
+
/** The contract's limits, by what they bound. */
|
|
209
|
+
export declare const SCREEN_CONTENT_LIMITS: {
|
|
210
|
+
/** The document's size, in bytes of its UTF-8 JSON. */
|
|
211
|
+
readonly documentBytes: number;
|
|
212
|
+
readonly rotation: 100;
|
|
213
|
+
readonly slideSecondsMin: 5;
|
|
214
|
+
readonly slideSecondsMax: 120;
|
|
215
|
+
readonly refreshSecondsMin: 30;
|
|
216
|
+
readonly refreshSecondsMax: 60;
|
|
217
|
+
readonly scheduleWindows: 14;
|
|
218
|
+
readonly title: 200;
|
|
219
|
+
/** All of a news item's paragraphs together. */
|
|
220
|
+
readonly paragraphs: 2000;
|
|
221
|
+
readonly description: 500;
|
|
222
|
+
readonly location: 200;
|
|
223
|
+
readonly place: 200;
|
|
224
|
+
readonly digitalParticipation: 500;
|
|
225
|
+
readonly agendaItems: 40;
|
|
226
|
+
readonly agendaTitle: 200;
|
|
227
|
+
readonly occurrencesMin: 1;
|
|
228
|
+
readonly occurrencesMax: 8;
|
|
229
|
+
readonly resourcesMin: 1;
|
|
230
|
+
readonly resourcesMax: 12;
|
|
231
|
+
readonly resourceName: 80;
|
|
232
|
+
/** How far an availability slide's `to` may be after its `from`, in days. */
|
|
233
|
+
readonly availabilityDays: 7;
|
|
234
|
+
readonly alt: 300;
|
|
235
|
+
readonly caption: 200;
|
|
236
|
+
/** All of an addon text slide's paragraphs together. */
|
|
237
|
+
readonly addonTextBody: 2000;
|
|
238
|
+
readonly tableColumns: 4;
|
|
239
|
+
readonly tableRows: 60;
|
|
240
|
+
readonly doors: 120;
|
|
241
|
+
};
|
|
242
|
+
/** What a slide's `id` is made of. */
|
|
243
|
+
export declare const SLIDE_ID_PATTERN: RegExp;
|
|
244
|
+
//# sourceMappingURL=contract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"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"}
|
package/dist/contract.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The screen content contract, version 1: the document a screen fetches and
|
|
3
|
+
* shows, as ADR 0034 defined it and ADR 0050 made the core's.
|
|
4
|
+
*
|
|
5
|
+
* The text of record is docs/screen-content-contract.md. These types and
|
|
6
|
+
* limits are that text in code, and a change to either is a change to the
|
|
7
|
+
* contract under its version rules: a field or a body kind may be added within
|
|
8
|
+
* version 1, because an older player ignores what it does not know, and a
|
|
9
|
+
* change an older player would misread raises the version.
|
|
10
|
+
*
|
|
11
|
+
* "Characters" in a limit are Unicode code points. A server that measures in
|
|
12
|
+
* UTF-16 code units, as JavaScript's `length` does, never exceeds a limit
|
|
13
|
+
* measured in code points, so the builder in this package measures in code
|
|
14
|
+
* units and the parser in code points.
|
|
15
|
+
*/
|
|
16
|
+
/** The version of the document this package reads and builds. */
|
|
17
|
+
export const SCREEN_CONTENT_VERSION = 1;
|
|
18
|
+
/** The contract's limits, by what they bound. */
|
|
19
|
+
export const SCREEN_CONTENT_LIMITS = {
|
|
20
|
+
/** The document's size, in bytes of its UTF-8 JSON. */
|
|
21
|
+
documentBytes: 1024 * 1024,
|
|
22
|
+
rotation: 100,
|
|
23
|
+
slideSecondsMin: 5,
|
|
24
|
+
slideSecondsMax: 120,
|
|
25
|
+
refreshSecondsMin: 30,
|
|
26
|
+
refreshSecondsMax: 60,
|
|
27
|
+
scheduleWindows: 14,
|
|
28
|
+
title: 200,
|
|
29
|
+
/** All of a news item's paragraphs together. */
|
|
30
|
+
paragraphs: 2000,
|
|
31
|
+
description: 500,
|
|
32
|
+
location: 200,
|
|
33
|
+
place: 200,
|
|
34
|
+
digitalParticipation: 500,
|
|
35
|
+
agendaItems: 40,
|
|
36
|
+
agendaTitle: 200,
|
|
37
|
+
occurrencesMin: 1,
|
|
38
|
+
occurrencesMax: 8,
|
|
39
|
+
resourcesMin: 1,
|
|
40
|
+
resourcesMax: 12,
|
|
41
|
+
resourceName: 80,
|
|
42
|
+
/** How far an availability slide's `to` may be after its `from`, in days. */
|
|
43
|
+
availabilityDays: 7,
|
|
44
|
+
alt: 300,
|
|
45
|
+
caption: 200,
|
|
46
|
+
/** All of an addon text slide's paragraphs together. */
|
|
47
|
+
addonTextBody: 2000,
|
|
48
|
+
tableColumns: 4,
|
|
49
|
+
tableRows: 60,
|
|
50
|
+
doors: 120,
|
|
51
|
+
};
|
|
52
|
+
/** What a slide's `id` is made of. */
|
|
53
|
+
export const SLIDE_ID_PATTERN = /^[A-Za-z0-9._:-]{1,64}$/;
|
|
54
|
+
//# sourceMappingURL=contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,iEAAiE;AACjE,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC;AAiOxC,iDAAiD;AACjD,MAAM,CAAC,MAAM,qBAAqB,GAAG;IACnC,uDAAuD;IACvD,aAAa,EAAE,IAAI,GAAG,IAAI;IAC1B,QAAQ,EAAE,GAAG;IACb,eAAe,EAAE,CAAC;IAClB,eAAe,EAAE,GAAG;IACpB,iBAAiB,EAAE,EAAE;IACrB,iBAAiB,EAAE,EAAE;IACrB,eAAe,EAAE,EAAE;IACnB,KAAK,EAAE,GAAG;IACV,gDAAgD;IAChD,UAAU,EAAE,IAAI;IAChB,WAAW,EAAE,GAAG;IAChB,QAAQ,EAAE,GAAG;IACb,KAAK,EAAE,GAAG;IACV,oBAAoB,EAAE,GAAG;IACzB,WAAW,EAAE,EAAE;IACf,WAAW,EAAE,GAAG;IAChB,cAAc,EAAE,CAAC;IACjB,cAAc,EAAE,CAAC;IACjB,YAAY,EAAE,CAAC;IACf,YAAY,EAAE,EAAE;IAChB,YAAY,EAAE,EAAE;IAChB,6EAA6E;IAC7E,gBAAgB,EAAE,CAAC;IACnB,GAAG,EAAE,GAAG;IACR,OAAO,EAAE,GAAG;IACZ,wDAAwD;IACxD,aAAa,EAAE,IAAI;IACnB,YAAY,EAAE,CAAC;IACf,SAAS,EAAE,EAAE;IACb,KAAK,EAAE,GAAG;CACF,CAAC;AAEX,sCAAsC;AACtC,MAAM,CAAC,MAAM,gBAAgB,GAAG,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The fetch loop: the request for screen content, and what is done with each
|
|
3
|
+
* answer, as the table under "Pairing" in ADR 0034 says it.
|
|
4
|
+
*
|
|
5
|
+
* | Answer | What the loop does |
|
|
6
|
+
* | ------------------------------------------------------------- | -------------------------------------------------------------------------- |
|
|
7
|
+
* | 200 | Replaces the cache whole and shows the document. |
|
|
8
|
+
* | 304 | Shows the cached document, now confirmed. |
|
|
9
|
+
* | 401, `realm="device"` and `device-token-invalid` | Clears the cache, stops, and says so: the caller forgets the token. |
|
|
10
|
+
* | 404 | Clears the cache and says the screen is paused; asks again every minute. |
|
|
11
|
+
* | 429 | Keeps what is shown and asks again after `Retry-After`, within a minute. |
|
|
12
|
+
* | Anything else, a body that is not a document, a network error | Shows the cache for 24 hours since it was fetched, then says disconnected. |
|
|
13
|
+
*
|
|
14
|
+
* Where the content is cached, and what a revocation wipes besides it, is the
|
|
15
|
+
* caller's, because a shell holds more than the display page does.
|
|
16
|
+
*/
|
|
17
|
+
import type { ScreenContent } from "./contract.ts";
|
|
18
|
+
/** What the loop keeps between fetches. */
|
|
19
|
+
export interface CachedScreenContent {
|
|
20
|
+
content: ScreenContent;
|
|
21
|
+
/** The strong ETag the document came with, or null. */
|
|
22
|
+
etag: string | null;
|
|
23
|
+
/** ISO 8601 instant of the last 200 or 304. */
|
|
24
|
+
fetchedAt: string;
|
|
25
|
+
}
|
|
26
|
+
/** Where the content is kept, such as `localStorage`. The caller's to choose. */
|
|
27
|
+
export interface ScreenContentCache {
|
|
28
|
+
read(): CachedScreenContent | null;
|
|
29
|
+
write(entry: CachedScreenContent): void;
|
|
30
|
+
clear(): void;
|
|
31
|
+
}
|
|
32
|
+
/** How fresh the content handed to `onContent` is. */
|
|
33
|
+
export interface FetchedStatus {
|
|
34
|
+
fetchedAt: Date;
|
|
35
|
+
/** True when the instance could not be reached and this is the last good content. */
|
|
36
|
+
stale: boolean;
|
|
37
|
+
}
|
|
38
|
+
export interface FetchLoopOptions {
|
|
39
|
+
/** The absolute content URL, query included. The token is never put in it. */
|
|
40
|
+
url: string;
|
|
41
|
+
/** The device token, read before each request; null when there is none. */
|
|
42
|
+
token: () => string | null;
|
|
43
|
+
cache: ScreenContentCache;
|
|
44
|
+
/** Content to show: fresh, or the last good content while the instance cannot be reached. */
|
|
45
|
+
onContent(content: ScreenContent, status: FetchedStatus): void;
|
|
46
|
+
/**
|
|
47
|
+
* The instance refused the token (401 with `realm="device"` and
|
|
48
|
+
* `device-token-invalid`), or there is no token. The loop has cleared the
|
|
49
|
+
* cache and stopped; the caller forgets whatever else it holds, the token
|
|
50
|
+
* first.
|
|
51
|
+
*/
|
|
52
|
+
onRevoked(): void;
|
|
53
|
+
/**
|
|
54
|
+
* 404: the route is switched off. The loop has cleared the cache, and asks
|
|
55
|
+
* again every 60 seconds. Without it, a 404 is told as `onDisconnected(null)`.
|
|
56
|
+
*/
|
|
57
|
+
onPaused?(): void;
|
|
58
|
+
/**
|
|
59
|
+
* Nothing to show: no content was ever fetched, or the last was fetched 24
|
|
60
|
+
* hours ago or more. The loop asks again every 60 seconds.
|
|
61
|
+
*/
|
|
62
|
+
onDisconnected(lastFetchedAt: Date | null): void;
|
|
63
|
+
fetch?: typeof fetch;
|
|
64
|
+
now?: () => Date;
|
|
65
|
+
setTimeout?: (callback: () => void, ms: number) => unknown;
|
|
66
|
+
clearTimeout?: (handle: unknown) => void;
|
|
67
|
+
}
|
|
68
|
+
/** A running fetch loop. */
|
|
69
|
+
export interface FetchLoop {
|
|
70
|
+
/** Stops the loop; a request in flight is aborted and its answer ignored. */
|
|
71
|
+
stop(): void;
|
|
72
|
+
/** Fetches now, unless a request is already in flight. */
|
|
73
|
+
refreshNow(): void;
|
|
74
|
+
}
|
|
75
|
+
/** How long the last good content may be shown, from when it was fetched. */
|
|
76
|
+
export declare const STALE_CONTENT_LIMIT_MS: number;
|
|
77
|
+
/**
|
|
78
|
+
* Starts fetching screen content: at once, then `refreshSeconds` after each
|
|
79
|
+
* 200 or 304, held to 30 to 60, and 60 seconds after anything else.
|
|
80
|
+
*
|
|
81
|
+
* Each request is `GET url` with the token in `Authorization: Bearer`, the
|
|
82
|
+
* cached ETag in `If-None-Match`, no cookie (`credentials: "omit"`) and no
|
|
83
|
+
* HTTP cache (`cache: "no-store"`). Only one is ever in flight. A cache entry
|
|
84
|
+
* younger than 24 hours is shown at once, as stale, before the first answer.
|
|
85
|
+
*/
|
|
86
|
+
export declare function startFetchLoop(options: FetchLoopOptions): FetchLoop;
|
|
87
|
+
//# sourceMappingURL=fetch-loop.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch-loop.d.ts","sourceRoot":"","sources":["../src/fetch-loop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAInD,2CAA2C;AAC3C,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,aAAa,CAAC;IACvB,uDAAuD;IACvD,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,+CAA+C;IAC/C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,iFAAiF;AACjF,MAAM,WAAW,kBAAkB;IACjC,IAAI,IAAI,mBAAmB,GAAG,IAAI,CAAC;IACnC,KAAK,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACxC,KAAK,IAAI,IAAI,CAAC;CACf;AAED,sDAAsD;AACtD,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,IAAI,CAAC;IAChB,qFAAqF;IACrF,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,8EAA8E;IAC9E,GAAG,EAAE,MAAM,CAAC;IACZ,2EAA2E;IAC3E,KAAK,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,EAAE,kBAAkB,CAAC;IAC1B,6FAA6F;IAC7F,SAAS,CAAC,OAAO,EAAE,aAAa,EAAE,MAAM,EAAE,aAAa,GAAG,IAAI,CAAC;IAC/D;;;;;OAKG;IACH,SAAS,IAAI,IAAI,CAAC;IAClB;;;OAGG;IACH,QAAQ,CAAC,IAAI,IAAI,CAAC;IAClB;;;OAGG;IACH,cAAc,CAAC,aAAa,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;IACjD,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;IACjB,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;IAC3D,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,OAAO,KAAK,IAAI,CAAC;CAC1C;AAED,4BAA4B;AAC5B,MAAM,WAAW,SAAS;IACxB,6EAA6E;IAC7E,IAAI,IAAI,IAAI,CAAC;IACb,0DAA0D;IAC1D,UAAU,IAAI,IAAI,CAAC;CACpB;AAGD,6EAA6E;AAC7E,eAAO,MAAM,sBAAsB,QAAwB,CAAC;AAO5D;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,gBAAgB,GAAG,SAAS,CAiQnE"}
|