@peekling/runtime 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.
Files changed (120) hide show
  1. package/AUTHORS +8 -0
  2. package/LICENSE +202 -0
  3. package/LICENSING.md +20 -0
  4. package/NOTICE +6 -0
  5. package/README.md +294 -0
  6. package/dist/configuration-validation.d.ts +19 -0
  7. package/dist/configuration-validation.d.ts.map +1 -0
  8. package/dist/configuration-validation.js +298 -0
  9. package/dist/content.d.ts +44 -0
  10. package/dist/content.d.ts.map +1 -0
  11. package/dist/content.js +738 -0
  12. package/dist/contracts.d.ts +10 -0
  13. package/dist/contracts.d.ts.map +1 -0
  14. package/dist/contracts.js +106 -0
  15. package/dist/defaults.d.ts +7 -0
  16. package/dist/defaults.d.ts.map +1 -0
  17. package/dist/defaults.js +6 -0
  18. package/dist/diagnostics.d.ts +25 -0
  19. package/dist/diagnostics.d.ts.map +1 -0
  20. package/dist/diagnostics.js +122 -0
  21. package/dist/direction.d.ts +3 -0
  22. package/dist/direction.d.ts.map +1 -0
  23. package/dist/direction.js +6 -0
  24. package/dist/errors.d.ts +15 -0
  25. package/dist/errors.d.ts.map +1 -0
  26. package/dist/errors.js +53 -0
  27. package/dist/events.d.ts +5 -0
  28. package/dist/events.d.ts.map +1 -0
  29. package/dist/events.js +60 -0
  30. package/dist/index.d.ts +6 -0
  31. package/dist/index.d.ts.map +1 -0
  32. package/dist/index.js +3 -0
  33. package/dist/input.d.ts +31 -0
  34. package/dist/input.d.ts.map +1 -0
  35. package/dist/input.js +215 -0
  36. package/dist/json.d.ts +3 -0
  37. package/dist/json.d.ts.map +1 -0
  38. package/dist/json.js +109 -0
  39. package/dist/loader.d.ts +33 -0
  40. package/dist/loader.d.ts.map +1 -0
  41. package/dist/loader.js +477 -0
  42. package/dist/locomotion.d.ts +8 -0
  43. package/dist/locomotion.d.ts.map +1 -0
  44. package/dist/locomotion.js +29 -0
  45. package/dist/normalized.d.ts +5 -0
  46. package/dist/normalized.d.ts.map +1 -0
  47. package/dist/normalized.js +266 -0
  48. package/dist/overrides.d.ts +29 -0
  49. package/dist/overrides.d.ts.map +1 -0
  50. package/dist/overrides.js +296 -0
  51. package/dist/own-data.d.ts +18 -0
  52. package/dist/own-data.d.ts.map +1 -0
  53. package/dist/own-data.js +205 -0
  54. package/dist/pack-api.d.ts +9 -0
  55. package/dist/pack-api.d.ts.map +1 -0
  56. package/dist/pack-api.js +6 -0
  57. package/dist/pack-shared.d.ts +28 -0
  58. package/dist/pack-shared.d.ts.map +1 -0
  59. package/dist/pack-shared.js +408 -0
  60. package/dist/pack.d.ts +6 -0
  61. package/dist/pack.d.ts.map +1 -0
  62. package/dist/pack.js +316 -0
  63. package/dist/peekling.css +1 -0
  64. package/dist/peekling.css.sri +1 -0
  65. package/dist/peekling.js +7469 -0
  66. package/dist/peekling.js.sri +1 -0
  67. package/dist/peekling.min.js +2 -0
  68. package/dist/peekling.min.js.sri +1 -0
  69. package/dist/plan-compiler.d.ts +82 -0
  70. package/dist/plan-compiler.d.ts.map +1 -0
  71. package/dist/plan-compiler.js +724 -0
  72. package/dist/plan.d.ts +21 -0
  73. package/dist/plan.d.ts.map +1 -0
  74. package/dist/plan.js +287 -0
  75. package/dist/preflight-api.d.ts +3 -0
  76. package/dist/preflight-api.d.ts.map +1 -0
  77. package/dist/preflight-api.js +1 -0
  78. package/dist/preflight.d.ts +22 -0
  79. package/dist/preflight.d.ts.map +1 -0
  80. package/dist/preflight.js +158 -0
  81. package/dist/registry.d.ts +9 -0
  82. package/dist/registry.d.ts.map +1 -0
  83. package/dist/registry.js +24 -0
  84. package/dist/renderer.d.ts +16 -0
  85. package/dist/renderer.d.ts.map +1 -0
  86. package/dist/renderer.js +176 -0
  87. package/dist/resolver.d.ts +3 -0
  88. package/dist/resolver.d.ts.map +1 -0
  89. package/dist/resolver.js +30 -0
  90. package/dist/runtime-diagnostics.d.ts +3 -0
  91. package/dist/runtime-diagnostics.d.ts.map +1 -0
  92. package/dist/runtime-diagnostics.js +5 -0
  93. package/dist/runtime-validation.d.ts +6 -0
  94. package/dist/runtime-validation.d.ts.map +1 -0
  95. package/dist/runtime-validation.js +104 -0
  96. package/dist/runtime.d.ts +87 -0
  97. package/dist/runtime.d.ts.map +1 -0
  98. package/dist/runtime.js +1015 -0
  99. package/dist/sections.d.ts +15 -0
  100. package/dist/sections.d.ts.map +1 -0
  101. package/dist/sections.js +221 -0
  102. package/dist/styles.d.ts +18 -0
  103. package/dist/styles.d.ts.map +1 -0
  104. package/dist/styles.js +132 -0
  105. package/dist/tooling-preflight.d.ts +23 -0
  106. package/dist/tooling-preflight.d.ts.map +1 -0
  107. package/dist/tooling-preflight.js +207 -0
  108. package/dist/tooling-validation.d.ts +8 -0
  109. package/dist/tooling-validation.d.ts.map +1 -0
  110. package/dist/tooling-validation.js +94 -0
  111. package/dist/types.d.ts +403 -0
  112. package/dist/types.d.ts.map +1 -0
  113. package/dist/types.js +29 -0
  114. package/dist/visibility.d.ts +12 -0
  115. package/dist/visibility.d.ts.map +1 -0
  116. package/dist/visibility.js +333 -0
  117. package/dist/web-component.d.ts +31 -0
  118. package/dist/web-component.d.ts.map +1 -0
  119. package/dist/web-component.js +236 -0
  120. package/package.json +74 -0
package/dist/input.js ADDED
@@ -0,0 +1,215 @@
1
+ import { runtimeMessage } from "./runtime-diagnostics.js";
2
+ export class InputCollector {
3
+ pointer;
4
+ reactions = [];
5
+ lastActivityAt;
6
+ #nextReactionId = 1;
7
+ #listeners = [];
8
+ #events = new Map();
9
+ #document;
10
+ #window;
11
+ #now;
12
+ #wake;
13
+ #diagnostic;
14
+ #coalesce;
15
+ #suspended = false;
16
+ constructor(document, window, options) {
17
+ this.#document = document;
18
+ this.#window = window;
19
+ this.#now = options.now;
20
+ this.#wake = options.wake;
21
+ this.#diagnostic = options.diagnostic ?? (() => { });
22
+ this.lastActivityAt = options.now();
23
+ this.#listen(window, "resize", () => this.#wake());
24
+ for (const event of options.events)
25
+ this.#retainEvent(event);
26
+ this.#coalesce = options.coalesce ?? new Map();
27
+ }
28
+ get reaction() {
29
+ return this.reactions[0];
30
+ }
31
+ captureReaction(name, payload, source = "application") {
32
+ const at = this.#now();
33
+ const last = this.reactions.at(-1);
34
+ const policy = this.#coalesce.get(`${source}:${name}`);
35
+ const coalesced = Boolean(last?.name === name &&
36
+ last.source === source &&
37
+ (policy === true ||
38
+ (policy !== undefined && newerRevision(last.payload, payload, policy))));
39
+ if (!coalesced && this.reactions.length >= 32) {
40
+ this.#diagnostic(runtimeMessage("event.queue-full", "Event queue overflow rejected the newest event"));
41
+ return { accepted: false, reason: "queue-full" };
42
+ }
43
+ // A coalesced queue tail keeps its original ID so acceptance and
44
+ // consumption continue to refer to the same logical event.
45
+ const reaction = {
46
+ id: coalesced ? last.id : this.#nextReactionId++,
47
+ source,
48
+ name,
49
+ at,
50
+ ...(payload === undefined ? {} : { payload }),
51
+ };
52
+ if (coalesced)
53
+ this.reactions[this.reactions.length - 1] = reaction;
54
+ else
55
+ this.reactions.push(reaction);
56
+ this.lastActivityAt = at;
57
+ this.#wake();
58
+ return { accepted: true, id: reaction.id, coalesced };
59
+ }
60
+ consumeReaction(id) {
61
+ if (this.reactions[0]?.id === id)
62
+ this.reactions.shift();
63
+ }
64
+ retainEvent(event) {
65
+ this.#retainEvent(event);
66
+ let retained = true;
67
+ return () => {
68
+ if (!retained)
69
+ return;
70
+ retained = false;
71
+ this.#releaseEvent(event);
72
+ };
73
+ }
74
+ setSuspended(value) {
75
+ if (value === this.#suspended)
76
+ return;
77
+ this.#suspended = value;
78
+ for (const binding of this.#listeners) {
79
+ if (value && binding[3] && !binding[4]) {
80
+ binding[0].removeEventListener(binding[1], binding[2]);
81
+ binding[3] = false;
82
+ }
83
+ else if (!value && !binding[3]) {
84
+ binding[0].addEventListener(binding[1], binding[2], { passive: true });
85
+ binding[3] = true;
86
+ }
87
+ }
88
+ if (value)
89
+ this.pointer = undefined;
90
+ }
91
+ destroy() {
92
+ for (const binding of this.#listeners.splice(0)) {
93
+ if (binding[3])
94
+ binding[0].removeEventListener(binding[1], binding[2]);
95
+ }
96
+ this.pointer = undefined;
97
+ this.reactions.length = 0;
98
+ this.#events.clear();
99
+ }
100
+ #retainEvent(event) {
101
+ const retained = this.#events.get(event);
102
+ if (retained) {
103
+ retained[0] += 1;
104
+ return;
105
+ }
106
+ const bindings = [];
107
+ this.#events.set(event, [1, bindings]);
108
+ const listen = (target, type, listener, persistent = false) => bindings.push(this.#listen(target, type, listener, persistent));
109
+ if (event === "pointer.move") {
110
+ listen(this.#document, "pointerdown", (input) => {
111
+ const pointer = input;
112
+ if (pointer.pointerType === "touch" || pointer.pointerType === "pen") {
113
+ this.#capturePointer(pointer);
114
+ }
115
+ });
116
+ listen(this.#document, "pointermove", (input) => this.#capturePointer(input));
117
+ const clearTouch = (input) => {
118
+ const type = input.pointerType;
119
+ if (type === "touch" || type === "pen")
120
+ this.#clearPointer();
121
+ };
122
+ listen(this.#document, "pointerup", clearTouch);
123
+ listen(this.#document, "pointercancel", clearTouch);
124
+ listen(this.#document, "pointerout", (input) => {
125
+ if (input.relatedTarget === null)
126
+ this.#clearPointer();
127
+ });
128
+ listen(this.#window, "blur", () => this.#clearPointer());
129
+ }
130
+ else if (event === "pointer.click") {
131
+ listen(this.#document, "click", (input) => this.#captureObserved("pointer.click", input));
132
+ }
133
+ else if (event === "document.visibility") {
134
+ listen(this.#document, "visibilitychange", () => this.captureReaction("document.visibility", { visible: !this.#document.hidden }, "browser"), true);
135
+ }
136
+ else if (event === "window.scroll") {
137
+ listen(this.#window, "scroll", (input) => this.#captureObserved("window.scroll", input));
138
+ }
139
+ else if (event === "window.focus") {
140
+ listen(this.#window, "focus", () => this.captureReaction("window.focus", { focused: true }, "browser"));
141
+ listen(this.#window, "blur", () => this.captureReaction("window.focus", { focused: false }, "browser"));
142
+ }
143
+ }
144
+ #releaseEvent(event) {
145
+ const retained = this.#events.get(event);
146
+ if (!retained)
147
+ return;
148
+ if (retained[0] > 1) {
149
+ retained[0] -= 1;
150
+ return;
151
+ }
152
+ this.#events.delete(event);
153
+ for (const binding of retained[1]) {
154
+ if (binding[3])
155
+ binding[0].removeEventListener(binding[1], binding[2]);
156
+ const index = this.#listeners.indexOf(binding);
157
+ if (index >= 0)
158
+ this.#listeners.splice(index, 1);
159
+ }
160
+ }
161
+ #clearPointer() {
162
+ if (this.#suspended)
163
+ return;
164
+ if (!this.pointer)
165
+ return;
166
+ this.pointer = undefined;
167
+ this.lastActivityAt = this.#now();
168
+ this.#wake();
169
+ }
170
+ #capturePointer(pointer) {
171
+ if (this.#suspended)
172
+ return;
173
+ this.pointer = { x: pointer.clientX, y: pointer.clientY };
174
+ this.lastActivityAt = this.#now();
175
+ this.#wake();
176
+ }
177
+ #captureObserved(name, event) {
178
+ if (this.#suspended)
179
+ return;
180
+ if (event
181
+ .composedPath()
182
+ .some((target) => "hasAttribute" in target &&
183
+ typeof target.hasAttribute === "function" &&
184
+ target.hasAttribute("data-peekling-controls")))
185
+ return;
186
+ const pointer = event;
187
+ const payload = Number.isFinite(pointer.clientX) && Number.isFinite(pointer.clientY)
188
+ ? { x: pointer.clientX, y: pointer.clientY }
189
+ : undefined;
190
+ this.captureReaction(name, payload, "browser");
191
+ }
192
+ #listen(target, type, listener, persistent = false) {
193
+ const options = { passive: true };
194
+ target.addEventListener(type, listener, options);
195
+ const binding = [target, type, listener, true, persistent];
196
+ this.#listeners.push(binding);
197
+ return binding;
198
+ }
199
+ }
200
+ function newerRevision(previous, next, policy) {
201
+ if (!previous ||
202
+ !next ||
203
+ typeof previous !== "object" ||
204
+ typeof next !== "object" ||
205
+ Array.isArray(previous) ||
206
+ Array.isArray(next))
207
+ return false;
208
+ const prior = previous;
209
+ const incoming = next;
210
+ return (incoming[policy.sessionField] === prior[policy.sessionField] &&
211
+ Number.isSafeInteger(incoming[policy.revisionField]) &&
212
+ Number.isSafeInteger(prior[policy.revisionField]) &&
213
+ incoming[policy.revisionField] >
214
+ prior[policy.revisionField]);
215
+ }
package/dist/json.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ /** Parse bounded JSON while rejecting ambiguous duplicate object keys. */
2
+ export declare function parseDataText(text: string, label: string, maxBytes?: number): unknown;
3
+ //# sourceMappingURL=json.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"json.d.ts","sourceRoot":"","sources":["../src/json.ts"],"names":[],"mappings":"AAKA,0EAA0E;AAC1E,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,QAAQ,SAAY,GACnB,OAAO,CAmCT"}
package/dist/json.js ADDED
@@ -0,0 +1,109 @@
1
+ import { packIssue, PackValidationError } from "./errors.js";
2
+ const MAX_JSON_NESTING = 64;
3
+ const JSON_NESTING_ERROR = 0;
4
+ /** Parse bounded JSON while rejecting ambiguous duplicate object keys. */
5
+ export function parseDataText(text, label, maxBytes = 64 * 1024) {
6
+ if (new TextEncoder().encode(text).byteLength > maxBytes) {
7
+ throw new PackValidationError([
8
+ packIssue(`${label}.size`, `${label} exceeds ${maxBytes} bytes`),
9
+ ]);
10
+ }
11
+ let parsed;
12
+ try {
13
+ parsed = JSON.parse(text);
14
+ }
15
+ catch {
16
+ throw new PackValidationError([
17
+ packIssue(`${label}.json`, `${label} is not valid JSON`),
18
+ ]);
19
+ }
20
+ let duplicate;
21
+ try {
22
+ duplicate = firstDuplicateKey(text);
23
+ }
24
+ catch (cause) {
25
+ if (cause !== JSON_NESTING_ERROR)
26
+ throw cause;
27
+ throw new PackValidationError([
28
+ packIssue(`${label}.depth`, `${label} nesting exceeds ${MAX_JSON_NESTING} levels`),
29
+ ]);
30
+ }
31
+ if (duplicate !== undefined) {
32
+ throw new PackValidationError([
33
+ packIssue(`${label}.duplicate-key`, `${label} contains duplicate object key ${JSON.stringify(duplicate)}`),
34
+ ]);
35
+ }
36
+ return parsed;
37
+ }
38
+ function firstDuplicateKey(text) {
39
+ let cursor = 0;
40
+ let duplicate;
41
+ const space = () => {
42
+ while (/\s/.test(text[cursor] ?? ""))
43
+ cursor++;
44
+ };
45
+ const string = () => {
46
+ const start = cursor++;
47
+ while (cursor < text.length) {
48
+ if (text[cursor] === "\\")
49
+ cursor += 2;
50
+ else if (text[cursor++] === '"')
51
+ break;
52
+ }
53
+ return JSON.parse(text.slice(start, cursor));
54
+ };
55
+ const value = (depth = 0) => {
56
+ space();
57
+ if (text[cursor] === "{") {
58
+ if (depth >= MAX_JSON_NESTING)
59
+ throw JSON_NESTING_ERROR;
60
+ cursor++;
61
+ const keys = new Set();
62
+ space();
63
+ while (text[cursor] !== "}") {
64
+ const key = string();
65
+ if (keys.has(key) && duplicate === undefined)
66
+ duplicate = key;
67
+ keys.add(key);
68
+ space();
69
+ cursor++;
70
+ value(depth + 1);
71
+ space();
72
+ if (text[cursor] === ",") {
73
+ cursor++;
74
+ space();
75
+ }
76
+ else
77
+ break;
78
+ }
79
+ cursor++;
80
+ return;
81
+ }
82
+ if (text[cursor] === "[") {
83
+ if (depth >= MAX_JSON_NESTING)
84
+ throw JSON_NESTING_ERROR;
85
+ cursor++;
86
+ space();
87
+ while (text[cursor] !== "]") {
88
+ value(depth + 1);
89
+ space();
90
+ if (text[cursor] === ",") {
91
+ cursor++;
92
+ space();
93
+ }
94
+ else
95
+ break;
96
+ }
97
+ cursor++;
98
+ return;
99
+ }
100
+ if (text[cursor] === '"') {
101
+ string();
102
+ return;
103
+ }
104
+ while (cursor < text.length && !/[\s,}\]]/.test(text[cursor]))
105
+ cursor++;
106
+ };
107
+ value();
108
+ return duplicate;
109
+ }
@@ -0,0 +1,33 @@
1
+ import type { PackLoadFailureCategory } from "./errors.js";
2
+ import type { NativeDensity, NormalizedPack } from "./types.js";
3
+ export interface LoadPackOptions {
4
+ character?: string;
5
+ baseUrl: string;
6
+ pack?: unknown;
7
+ packUrl?: string;
8
+ atlasUrl?: string;
9
+ renderScale?: number;
10
+ devicePixelRatio?: number;
11
+ densityOverride?: NativeDensity;
12
+ maxDensity?: NativeDensity;
13
+ fetch: typeof globalThis.fetch;
14
+ image: () => HTMLImageElement;
15
+ signal: AbortSignal;
16
+ /** Package-internal override used by deterministic loader tests. */
17
+ resourceTimeoutMs?: number;
18
+ createObjectURL: (blob: Blob) => string;
19
+ revokeObjectURL: (url: string) => void;
20
+ diagnostic?: (message: string, category?: PackLoadFailureCategory) => void;
21
+ }
22
+ export type { PackLoadFailureCategory } from "./errors.js";
23
+ export interface LoadedPack {
24
+ readonly content: NormalizedPack;
25
+ readonly atlasObjectUrl: string;
26
+ readonly loadedDensity: number;
27
+ upgrade(requiredDensity: number): Promise<boolean>;
28
+ release(): void;
29
+ }
30
+ export declare function resolveManifestUrl(input: string, base: string): string;
31
+ export declare function requiredAtlasDensity(devicePixelRatio: number, renderScale: number, maxDensity?: NativeDensity): number;
32
+ export declare function loadNativePack(options: LoadPackOptions): Promise<LoadedPack>;
33
+ //# sourceMappingURL=loader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../src/loader.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAU3D,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAUhE,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,aAAa,CAAC;IAChC,UAAU,CAAC,EAAE,aAAa,CAAC;IAC3B,KAAK,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAC/B,KAAK,EAAE,MAAM,gBAAgB,CAAC;IAC9B,MAAM,EAAE,WAAW,CAAC;IACpB,oEAAoE;IACpE,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,CAAC;IACxC,eAAe,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IACvC,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,uBAAuB,KAAK,IAAI,CAAC;CAC5E;AAED,YAAY,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAE3D,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,OAAO,EAAE,cAAc,CAAC;IACjC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,OAAO,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnD,OAAO,IAAI,IAAI,CAAC;CACjB;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAQtE;AAgUD,wBAAgB,oBAAoB,CAClC,gBAAgB,EAAE,MAAM,EACxB,WAAW,EAAE,MAAM,EACnB,UAAU,GAAE,aAAiB,GAC5B,MAAM,CAMR;AAED,wBAAsB,cAAc,CAClC,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,UAAU,CAAC,CAuUrB"}