chore: release signage 0.1.2
0.1.2 fixes what the first production Signage deployment found: - #4: the display and server share one message schema; every heartbeat from an image slide used to be refused with a 400; - #5: every display request retries with backoff and never gives up, so a display that failed one request no longer stays offline until reloaded; - #6: one object URL per cached slide, released when done; the leak exhausted the browser within minutes; - CI (#7, #8): every change runs inside a pinned Ekklesia, end to end in a real browser, and cleans up even when killed. compatibility.platform stays ^0.1.0: every fix is module-side, and CI tested it against platform v0.1.1 plus test-only scripts. The README no longer says Signage is unreleased or undeployed. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
e6c6553afc
commit
855b5f9f54
@@ -16,7 +16,8 @@ Ekklesia and vendors none of its SDKs.
|
|||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
Version 0.1.1 is feature-complete for its declared scope and unreleased. What exists today:
|
Version 0.1.2 is feature-complete for its declared scope, and Signage has run in production since
|
||||||
|
0.1.1. What exists today:
|
||||||
|
|
||||||
- The module package, manifest (v7), four capabilities, six permissions, ten operator settings, and
|
- The module package, manifest (v7), four capabilities, six permissions, ten operator settings, and
|
||||||
one logical storage bucket.
|
one logical storage bucket.
|
||||||
@@ -34,10 +35,10 @@ Version 0.1.1 is feature-complete for its declared scope and unreleased. What ex
|
|||||||
renderers mounted as browser contributions, the built-in "Content unavailable" slate, deliberate
|
renderers mounted as browser contributions, the built-in "Content unavailable" slate, deliberate
|
||||||
blackout, and offline continuation from this display's own persisted state.
|
blackout, and offline continuation from this display's own persisted state.
|
||||||
|
|
||||||
Nothing here is deployed, and no cutover from the legacy Announcements container has happened or is
|
No cutover from the legacy Announcements container has happened or is planned by this repository.
|
||||||
planned by this repository. Video, audio, MQTT, Home Assistant, Companion, and a notification outbox
|
Video, audio, MQTT, Home Assistant, Companion, and a notification outbox are out of scope; so is
|
||||||
are out of scope; so is any content module — Signage ships with no announcements, scripture, or
|
any content module — Signage ships with no announcements, scripture, or trivia, and cannot render
|
||||||
trivia, and cannot render any until one is installed.
|
any until one is installed.
|
||||||
|
|
||||||
### Offline cold start is not yet possible
|
### Offline cold start is not yet possible
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"id": "signage",
|
"id": "signage",
|
||||||
"name": "Signage",
|
"name": "Signage",
|
||||||
"description": "Visual programming, timelines, synchronized channels, and display delivery for church screens.",
|
"description": "Visual programming, timelines, synchronized channels, and display delivery for church screens.",
|
||||||
"version": "0.1.1",
|
"version": "0.1.2",
|
||||||
"compatibility": {
|
"compatibility": {
|
||||||
"platform": "^0.1.0",
|
"platform": "^0.1.0",
|
||||||
"moduleSdk": "^0.1.0"
|
"moduleSdk": "^0.1.0"
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@ekklesia/signage",
|
"name": "@ekklesia/signage",
|
||||||
"version": "0.1.1",
|
"version": "0.1.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"description": "Ekklesia's visual programming, timeline, synchronization, and display-delivery module.",
|
"description": "Ekklesia's visual programming, timeline, synchronization, and display-delivery module.",
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ function SignageDisplay({ context }: { readonly context: ModuleUiHostContext }):
|
|||||||
*/
|
*/
|
||||||
export default defineModuleUi({
|
export default defineModuleUi({
|
||||||
id: "signage",
|
id: "signage",
|
||||||
version: "0.1.1",
|
version: "0.1.2",
|
||||||
styles: displayStyles,
|
styles: displayStyles,
|
||||||
mount(container, context) {
|
mount(container, context) {
|
||||||
const root = createRoot(container);
|
const root = createRoot(container);
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ export function createSignageModuleRuntime(
|
|||||||
|
|
||||||
const definition = defineModuleRuntime({
|
const definition = defineModuleRuntime({
|
||||||
id: "signage",
|
id: "signage",
|
||||||
version: "0.1.1",
|
version: "0.1.2",
|
||||||
|
|
||||||
register({ capabilities, config, http, realtime }) {
|
register({ capabilities, config, http, realtime }) {
|
||||||
const settings = readSignageSettings(config);
|
const settings = readSignageSettings(config);
|
||||||
@@ -189,7 +189,7 @@ export function createSignageModuleRuntime(
|
|||||||
// server enforces — a UI believing a stale limit would offer an upload the server refuses.
|
// server enforces — a UI believing a stale limit would offer an upload the server refuses.
|
||||||
return Response.json({
|
return Response.json({
|
||||||
crossfadeMs: current.crossfadeMs,
|
crossfadeMs: current.crossfadeMs,
|
||||||
moduleVersion: "0.1.1",
|
moduleVersion: "0.1.2",
|
||||||
nativeImageDurationMs: current.nativeImageDurationMs,
|
nativeImageDurationMs: current.nativeImageDurationMs,
|
||||||
nativePresentation: current.nativePresentation,
|
nativePresentation: current.nativePresentation,
|
||||||
rendererInputVersion: SIGNAGE_SLIDE_RENDERER_INPUT_VERSION,
|
rendererInputVersion: SIGNAGE_SLIDE_RENDERER_INPUT_VERSION,
|
||||||
|
|||||||
+1
-1
@@ -15,7 +15,7 @@ import ownStyles from "./styles.css";
|
|||||||
*/
|
*/
|
||||||
export default defineModuleUi({
|
export default defineModuleUi({
|
||||||
id: "signage",
|
id: "signage",
|
||||||
version: "0.1.1",
|
version: "0.1.2",
|
||||||
styles: `${EKKLESIA_UI_STYLESHEET}\n${ownStyles}`,
|
styles: `${EKKLESIA_UI_STYLESHEET}\n${ownStyles}`,
|
||||||
mount(container, context) {
|
mount(container, context) {
|
||||||
const root = createRoot(container);
|
const root = createRoot(container);
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ export function createFakeHost(options: FakeHostOptions): FakeHost {
|
|||||||
/** The settings payload every screen loads first. */
|
/** The settings payload every screen loads first. */
|
||||||
export const FAKE_SETTINGS = Object.freeze({
|
export const FAKE_SETTINGS = Object.freeze({
|
||||||
crossfadeMs: 1000,
|
crossfadeMs: 1000,
|
||||||
moduleVersion: "0.1.1",
|
moduleVersion: "0.1.2",
|
||||||
nativeImageDurationMs: 7000,
|
nativeImageDurationMs: 7000,
|
||||||
nativePresentation: {
|
nativePresentation: {
|
||||||
backgroundColor: "#000000",
|
backgroundColor: "#000000",
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ describe("Signage module manifest", () => {
|
|||||||
expect(manifest.manifestVersion).toBe(7);
|
expect(manifest.manifestVersion).toBe(7);
|
||||||
expect(manifest.id).toBe("signage");
|
expect(manifest.id).toBe("signage");
|
||||||
expect(manifest.name).toBe("Signage");
|
expect(manifest.name).toBe("Signage");
|
||||||
expect(manifest.version).toBe("0.1.1");
|
expect(manifest.version).toBe("0.1.2");
|
||||||
expect(manifest.compatibility).toEqual({ moduleSdk: "^0.1.0", platform: "^0.1.0" });
|
expect(manifest.compatibility).toEqual({ moduleSdk: "^0.1.0", platform: "^0.1.0" });
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -142,7 +142,7 @@ describe("Signage module manifest", () => {
|
|||||||
|
|
||||||
describe("Signage server runtime", () => {
|
describe("Signage server runtime", () => {
|
||||||
it("exports the typed runtime definition the compiled server entrypoint publishes", async () => {
|
it("exports the typed runtime definition the compiled server entrypoint publishes", async () => {
|
||||||
expect(moduleRuntime).toMatchObject({ id: "signage", version: "0.1.1" });
|
expect(moduleRuntime).toMatchObject({ id: "signage", version: "0.1.2" });
|
||||||
});
|
});
|
||||||
|
|
||||||
it("registers its routes declaratively, with no database, storage, or realtime work", async () => {
|
it("registers its routes declaratively, with no database, storage, or realtime work", async () => {
|
||||||
@@ -241,7 +241,7 @@ describe("Signage server runtime", () => {
|
|||||||
expect(response.status).toBe(200);
|
expect(response.status).toBe(200);
|
||||||
expect(await response.json()).toEqual({
|
expect(await response.json()).toEqual({
|
||||||
crossfadeMs: 400,
|
crossfadeMs: 400,
|
||||||
moduleVersion: "0.1.1",
|
moduleVersion: "0.1.2",
|
||||||
nativeImageDurationMs: 12_000,
|
nativeImageDurationMs: 12_000,
|
||||||
nativePresentation: SIGNAGE_DEFAULT_SETTINGS.nativePresentation,
|
nativePresentation: SIGNAGE_DEFAULT_SETTINGS.nativePresentation,
|
||||||
rendererInputVersion: "1.0.0",
|
rendererInputVersion: "1.0.0",
|
||||||
|
|||||||
Reference in New Issue
Block a user