@urk/adapters 0.1.0 → 0.1.2

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 CHANGED
@@ -4,6 +4,7 @@ Contract-first capability adapters that prove URK's v0 kernel surface.
4
4
 
5
5
  ## Reference adapters
6
6
 
7
+ - `createAudioAdapter()` - manage browser-native audio transport across a small set of registered tracks
7
8
  - `createPointerAdapter()` - bind DOM targets or pointer surfaces and emit normalized pointer events
8
9
  - `createInputAdapter()` - normalize keyboard input, track pressed keys, and bind simple key handlers
9
10
  - `createStorageAdapter()` - provide namespaced local/session key-value persistence with swappable backends
@@ -16,6 +17,7 @@ Contract-first capability adapters that prove URK's v0 kernel surface.
16
17
  ```ts
17
18
  import { createKernel } from '@urk/core';
18
19
  import {
20
+ createAudioAdapter,
19
21
  createInputAdapter,
20
22
  createPointerAdapter,
21
23
  createStorageAdapter,
@@ -29,6 +31,7 @@ const kernel = createKernel({
29
31
  'ui:host': document.querySelector('#overlay-host'),
30
32
  },
31
33
  adapters: [
34
+ createAudioAdapter(),
32
35
  createPointerAdapter(),
33
36
  createInputAdapter(),
34
37
  createStorageAdapter({ namespace: 'demo' }),
@@ -42,6 +45,14 @@ await kernel.boot();
42
45
 
43
46
  These adapters use the canonical `AdapterRegistration<TApi>` contract from `@urk/core`. They are intentionally small and exist to prove the kernel surface, not to introduce a broad adapter matrix early.
44
47
 
48
+ `createAudioAdapter()` is deliberately transport-first in this milestone:
49
+
50
+ - register a small set of browser-native media tracks
51
+ - preload them into `HTMLAudioElement` instances
52
+ - control playback through `play`, `pause`, `stop`, `setMuted`, and `setVolume`
53
+ - expose one observable transport snapshot plus `audio:changed`, `audio:ended`, and `audio:error`
54
+ - no Web Audio graph, spatial audio, playlist system, or mixer layer yet
55
+
45
56
  `createInputAdapter()` is deliberately keyboard-first in this milestone:
46
57
 
47
58
  - pressed-key tracking through `isPressed(code)`
@@ -73,7 +84,7 @@ These adapters use the canonical `AdapterRegistration<TApi>` contract from `@urk
73
84
 
74
85
  ## Validation
75
86
 
76
- This package is validated for this milestone through type-check/build plus the standalone DOM + Three picking proof in `@urk/examples`.
87
+ This package is validated for this milestone through type-check/build plus the standalone browser proofs in `@urk/examples`, including the DOM-first audio transport proof.
77
88
 
78
89
  ## Architecture
79
90
 
@@ -1,11 +1,12 @@
1
1
  /**
2
2
  * URK adapter exports.
3
3
  */
4
- export * from './loading';
5
- export * from './input';
6
- export * from './pointer';
7
- export * from './storage';
8
- export * from './three';
9
- export * from './ui-widgets';
10
- export * from './contracts';
4
+ export * from './loading.js';
5
+ export * from './audio.js';
6
+ export * from './input.js';
7
+ export * from './pointer.js';
8
+ export * from './storage.js';
9
+ export * from './three.js';
10
+ export * from './ui-widgets.js';
11
+ export * from './contracts.js';
11
12
  //# sourceMappingURL=adapters.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC"}
1
+ {"version":3,"file":"adapters.d.ts","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC"}
package/dist/adapters.js CHANGED
@@ -1,11 +1,12 @@
1
1
  /**
2
2
  * URK adapter exports.
3
3
  */
4
- export * from './loading';
5
- export * from './input';
6
- export * from './pointer';
7
- export * from './storage';
8
- export * from './three';
9
- export * from './ui-widgets';
10
- export * from './contracts';
4
+ export * from './loading.js';
5
+ export * from './audio.js';
6
+ export * from './input.js';
7
+ export * from './pointer.js';
8
+ export * from './storage.js';
9
+ export * from './three.js';
10
+ export * from './ui-widgets.js';
11
+ export * from './contracts.js';
11
12
  //# sourceMappingURL=adapters.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"adapters.js","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,SAAS,CAAC;AACxB,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC"}
1
+ {"version":3,"file":"adapters.js","sourceRoot":"","sources":["../src/adapters.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Company: EonHive Inc.
3
+ * Title: Audio Adapter
4
+ * Purpose: Provide a small browser-native audio transport capability for URK.
5
+ * Author: Stan Nesi
6
+ * Created: 2026-04-23
7
+ * Updated: 2026-04-23
8
+ * Notes: Vibe coded with Codex.
9
+ */
10
+ import type { AdapterRegistration } from '@urk/core';
11
+ export interface AudioTrackDefinition {
12
+ id: string;
13
+ src: string;
14
+ label: string;
15
+ loop?: boolean;
16
+ volume?: number;
17
+ }
18
+ export type AudioPlaybackStatus = 'idle' | 'loading' | 'ready' | 'playing' | 'paused' | 'error';
19
+ export interface AudioTransportSnapshot {
20
+ activeTrackId: string | null;
21
+ status: AudioPlaybackStatus;
22
+ muted: boolean;
23
+ volume: number;
24
+ loadedTrackIds: string[];
25
+ errorMessage?: string;
26
+ }
27
+ export type AudioListener = (snapshot: AudioTransportSnapshot) => void;
28
+ export interface AudioAdapterApi {
29
+ registerTrack(track: AudioTrackDefinition): void;
30
+ preload(trackId: string): Promise<void>;
31
+ play(trackId?: string): Promise<void>;
32
+ pause(): void;
33
+ stop(): void;
34
+ setMuted(next: boolean): void;
35
+ setVolume(next: number): void;
36
+ getSnapshot(): AudioTransportSnapshot;
37
+ subscribe(listener: AudioListener): () => void;
38
+ }
39
+ export declare function createAudioAdapter(id?: string): AdapterRegistration<AudioAdapterApi>;
40
+ //# sourceMappingURL=audio.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio.d.ts","sourceRoot":"","sources":["../src/audio.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAC;AAErD,MAAM,WAAW,oBAAoB;IACnC,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,SAAS,GAAG,OAAO,GAAG,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAC;AAEhG,MAAM,WAAW,sBAAsB;IACrC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,MAAM,EAAE,mBAAmB,CAAC;IAC5B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,MAAM,aAAa,GAAG,CAAC,QAAQ,EAAE,sBAAsB,KAAK,IAAI,CAAC;AAEvE,MAAM,WAAW,eAAe;IAC9B,aAAa,CAAC,KAAK,EAAE,oBAAoB,GAAG,IAAI,CAAC;IACjD,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,IAAI,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,KAAK,IAAI,IAAI,CAAC;IACd,IAAI,IAAI,IAAI,CAAC;IACb,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,CAAC;IAC9B,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,WAAW,IAAI,sBAAsB,CAAC;IACtC,SAAS,CAAC,QAAQ,EAAE,aAAa,GAAG,MAAM,IAAI,CAAC;CAChD;AAkCD,wBAAgB,kBAAkB,CAChC,EAAE,SAAkB,GACnB,mBAAmB,CAAC,eAAe,CAAC,CA0XtC"}
package/dist/audio.js ADDED
@@ -0,0 +1,333 @@
1
+ /**
2
+ * Company: EonHive Inc.
3
+ * Title: Audio Adapter
4
+ * Purpose: Provide a small browser-native audio transport capability for URK.
5
+ * Author: Stan Nesi
6
+ * Created: 2026-04-23
7
+ * Updated: 2026-04-23
8
+ * Notes: Vibe coded with Codex.
9
+ */
10
+ function clampVolume(value) {
11
+ return Math.max(0, Math.min(value, 1));
12
+ }
13
+ function cloneSnapshot(snapshot) {
14
+ return {
15
+ ...snapshot,
16
+ loadedTrackIds: [...snapshot.loadedTrackIds],
17
+ };
18
+ }
19
+ function createEmptySnapshot() {
20
+ return {
21
+ activeTrackId: null,
22
+ status: 'idle',
23
+ muted: false,
24
+ volume: 1,
25
+ loadedTrackIds: [],
26
+ };
27
+ }
28
+ function formatTrackError(track, action) {
29
+ return `Failed ${action} for audio track: ${track.label}`;
30
+ }
31
+ export function createAudioAdapter(id = 'audio-adapter') {
32
+ let teardown = null;
33
+ return {
34
+ id,
35
+ capability: 'audio',
36
+ isSupported() {
37
+ return typeof window !== 'undefined' && typeof Audio !== 'undefined';
38
+ },
39
+ setup(ctx) {
40
+ const tracks = new Map();
41
+ const loadedTrackIds = new Set();
42
+ const listeners = new Set();
43
+ let snapshot = createEmptySnapshot();
44
+ const publish = (partial = {}, extraEvent) => {
45
+ snapshot = {
46
+ ...snapshot,
47
+ ...partial,
48
+ loadedTrackIds: [...loadedTrackIds],
49
+ };
50
+ const next = cloneSnapshot(snapshot);
51
+ ctx.events.emit({
52
+ type: 'audio:changed',
53
+ source: id,
54
+ payload: next,
55
+ timestamp: Date.now(),
56
+ });
57
+ if (extraEvent) {
58
+ ctx.events.emit({
59
+ type: extraEvent.type,
60
+ source: id,
61
+ payload: extraEvent.payload,
62
+ timestamp: Date.now(),
63
+ });
64
+ }
65
+ for (const listener of [...listeners]) {
66
+ listener(next);
67
+ }
68
+ return next;
69
+ };
70
+ const requireTrack = (trackId) => {
71
+ const record = tracks.get(trackId);
72
+ if (!record) {
73
+ throw new Error(`Unknown audio track: ${trackId}`);
74
+ }
75
+ return record;
76
+ };
77
+ const applyElementSettings = (record) => {
78
+ record.element.loop = record.definition.loop ?? false;
79
+ record.element.muted = snapshot.muted;
80
+ record.element.volume = clampVolume(snapshot.volume * (record.definition.volume ?? 1));
81
+ };
82
+ const stopElement = (record) => {
83
+ record.element.pause();
84
+ try {
85
+ record.element.currentTime = 0;
86
+ }
87
+ catch {
88
+ // Some browsers can reject currentTime writes if metadata is unavailable.
89
+ }
90
+ };
91
+ const preloadElement = async (record) => {
92
+ if (loadedTrackIds.has(record.definition.id) || record.element.readyState >= 3) {
93
+ loadedTrackIds.add(record.definition.id);
94
+ return;
95
+ }
96
+ await new Promise((resolve, reject) => {
97
+ const onReady = () => {
98
+ cleanup();
99
+ loadedTrackIds.add(record.definition.id);
100
+ resolve();
101
+ };
102
+ const onError = () => {
103
+ cleanup();
104
+ reject(new Error(formatTrackError(record.definition, 'preload')));
105
+ };
106
+ const cleanup = () => {
107
+ record.element.removeEventListener('canplaythrough', onReady);
108
+ record.element.removeEventListener('loadeddata', onReady);
109
+ record.element.removeEventListener('error', onError);
110
+ };
111
+ record.element.addEventListener('canplaythrough', onReady, { once: true });
112
+ record.element.addEventListener('loadeddata', onReady, { once: true });
113
+ record.element.addEventListener('error', onError, { once: true });
114
+ record.element.load();
115
+ });
116
+ };
117
+ const preloadTrack = async (record, shouldSetLoading) => {
118
+ if (shouldSetLoading) {
119
+ publish({
120
+ status: 'loading',
121
+ errorMessage: undefined,
122
+ });
123
+ }
124
+ try {
125
+ await preloadElement(record);
126
+ publish({
127
+ status: shouldSetLoading ? 'ready' : snapshot.status,
128
+ errorMessage: undefined,
129
+ });
130
+ }
131
+ catch (error) {
132
+ const message = error instanceof Error ? error.message : formatTrackError(record.definition, 'preload');
133
+ publish({
134
+ status: 'error',
135
+ errorMessage: message,
136
+ }, {
137
+ type: 'audio:error',
138
+ payload: {
139
+ trackId: record.definition.id,
140
+ label: record.definition.label,
141
+ message,
142
+ },
143
+ });
144
+ throw new Error(message);
145
+ }
146
+ };
147
+ teardown = () => {
148
+ for (const record of tracks.values()) {
149
+ record.element.pause();
150
+ record.element.removeEventListener('ended', record.onEnded);
151
+ record.element.removeEventListener('error', record.onError);
152
+ record.element.src = '';
153
+ record.element.load();
154
+ }
155
+ tracks.clear();
156
+ loadedTrackIds.clear();
157
+ listeners.clear();
158
+ };
159
+ return {
160
+ registerTrack(track) {
161
+ if (tracks.has(track.id)) {
162
+ throw new Error(`Audio track already registered: ${track.id}`);
163
+ }
164
+ const element = new Audio(track.src);
165
+ element.preload = 'auto';
166
+ const onEnded = () => {
167
+ if (snapshot.activeTrackId !== track.id) {
168
+ return;
169
+ }
170
+ publish({
171
+ status: 'ready',
172
+ errorMessage: undefined,
173
+ }, {
174
+ type: 'audio:ended',
175
+ payload: {
176
+ trackId: track.id,
177
+ label: track.label,
178
+ },
179
+ });
180
+ };
181
+ const onError = () => {
182
+ const message = formatTrackError(track, 'playback');
183
+ if (snapshot.status === 'error' && snapshot.errorMessage === message) {
184
+ return;
185
+ }
186
+ publish({
187
+ activeTrackId: snapshot.activeTrackId ?? track.id,
188
+ status: 'error',
189
+ errorMessage: message,
190
+ }, {
191
+ type: 'audio:error',
192
+ payload: {
193
+ trackId: track.id,
194
+ label: track.label,
195
+ message,
196
+ },
197
+ });
198
+ };
199
+ const record = {
200
+ definition: {
201
+ ...track,
202
+ volume: clampVolume(track.volume ?? 1),
203
+ },
204
+ element,
205
+ onEnded,
206
+ onError,
207
+ };
208
+ element.addEventListener('ended', onEnded);
209
+ element.addEventListener('error', onError);
210
+ applyElementSettings(record);
211
+ tracks.set(track.id, record);
212
+ },
213
+ async preload(trackId) {
214
+ const record = requireTrack(trackId);
215
+ const shouldSetLoading = snapshot.status === 'idle' ||
216
+ snapshot.status === 'loading' ||
217
+ snapshot.status === 'ready';
218
+ await preloadTrack(record, shouldSetLoading);
219
+ },
220
+ async play(trackId) {
221
+ const nextTrackId = trackId ?? snapshot.activeTrackId;
222
+ if (!nextTrackId) {
223
+ return;
224
+ }
225
+ const nextRecord = requireTrack(nextTrackId);
226
+ const previousTrackId = snapshot.activeTrackId;
227
+ if (previousTrackId && previousTrackId !== nextTrackId) {
228
+ stopElement(requireTrack(previousTrackId));
229
+ }
230
+ if (!loadedTrackIds.has(nextTrackId) && nextRecord.element.readyState < 3) {
231
+ publish({
232
+ activeTrackId: nextTrackId,
233
+ status: 'loading',
234
+ errorMessage: undefined,
235
+ });
236
+ await preloadTrack(nextRecord, false);
237
+ }
238
+ if (previousTrackId !== nextTrackId ||
239
+ nextRecord.element.ended ||
240
+ (nextRecord.element.duration > 0 &&
241
+ nextRecord.element.currentTime >= nextRecord.element.duration)) {
242
+ try {
243
+ nextRecord.element.currentTime = 0;
244
+ }
245
+ catch {
246
+ // Ignore currentTime reset failures for browsers without loaded metadata yet.
247
+ }
248
+ }
249
+ applyElementSettings(nextRecord);
250
+ try {
251
+ await nextRecord.element.play();
252
+ publish({
253
+ activeTrackId: nextTrackId,
254
+ status: 'playing',
255
+ errorMessage: undefined,
256
+ });
257
+ }
258
+ catch (error) {
259
+ const message = error instanceof Error ? error.message : formatTrackError(nextRecord.definition, 'playback');
260
+ publish({
261
+ activeTrackId: nextTrackId,
262
+ status: 'error',
263
+ errorMessage: message,
264
+ }, {
265
+ type: 'audio:error',
266
+ payload: {
267
+ trackId: nextTrackId,
268
+ label: nextRecord.definition.label,
269
+ message,
270
+ },
271
+ });
272
+ throw new Error(message);
273
+ }
274
+ },
275
+ pause() {
276
+ if (!snapshot.activeTrackId) {
277
+ return;
278
+ }
279
+ const record = requireTrack(snapshot.activeTrackId);
280
+ record.element.pause();
281
+ publish({
282
+ status: 'paused',
283
+ errorMessage: undefined,
284
+ });
285
+ },
286
+ stop() {
287
+ if (!snapshot.activeTrackId) {
288
+ return;
289
+ }
290
+ stopElement(requireTrack(snapshot.activeTrackId));
291
+ publish({
292
+ status: 'ready',
293
+ errorMessage: undefined,
294
+ });
295
+ },
296
+ setMuted(next) {
297
+ const muted = Boolean(next);
298
+ for (const record of tracks.values()) {
299
+ record.element.muted = muted;
300
+ }
301
+ publish({
302
+ muted,
303
+ errorMessage: undefined,
304
+ });
305
+ },
306
+ setVolume(next) {
307
+ const volume = clampVolume(next);
308
+ for (const record of tracks.values()) {
309
+ record.element.volume = clampVolume(volume * (record.definition.volume ?? 1));
310
+ }
311
+ publish({
312
+ volume,
313
+ errorMessage: undefined,
314
+ });
315
+ },
316
+ getSnapshot() {
317
+ return cloneSnapshot(snapshot);
318
+ },
319
+ subscribe(listener) {
320
+ listeners.add(listener);
321
+ return () => {
322
+ listeners.delete(listener);
323
+ };
324
+ },
325
+ };
326
+ },
327
+ dispose() {
328
+ teardown?.();
329
+ teardown = null;
330
+ },
331
+ };
332
+ }
333
+ //# sourceMappingURL=audio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audio.js","sourceRoot":"","sources":["../src/audio.ts"],"names":[],"mappings":"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"}
package/dist/dom.d.ts ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Company: EonHive Inc.
3
+ * Title: DOM Adapter Exports
4
+ * Purpose: Provide a dependency-light adapter surface for DOM-only URK consumers.
5
+ * Author: Stan Nesi
6
+ * Created: 2026-04-23
7
+ * Updated: 2026-04-23
8
+ * Notes: Vibe coded with Codex.
9
+ */
10
+ export * from './loading.js';
11
+ export * from './input.js';
12
+ export * from './pointer.js';
13
+ export * from './storage.js';
14
+ export * from './ui-widgets.js';
15
+ export * from './contracts.js';
16
+ //# sourceMappingURL=dom.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dom.d.ts","sourceRoot":"","sources":["../src/dom.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC"}
package/dist/dom.js ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Company: EonHive Inc.
3
+ * Title: DOM Adapter Exports
4
+ * Purpose: Provide a dependency-light adapter surface for DOM-only URK consumers.
5
+ * Author: Stan Nesi
6
+ * Created: 2026-04-23
7
+ * Updated: 2026-04-23
8
+ * Notes: Vibe coded with Codex.
9
+ */
10
+ export * from './loading.js';
11
+ export * from './input.js';
12
+ export * from './pointer.js';
13
+ export * from './storage.js';
14
+ export * from './ui-widgets.js';
15
+ export * from './contracts.js';
16
+ //# sourceMappingURL=dom.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dom.js","sourceRoot":"","sources":["../src/dom.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,cAAc,cAAc,CAAC;AAC7B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,gBAAgB,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * URK Adapters - Contract-first capability adapters.
3
3
  */
4
- export * from './adapters';
4
+ export * from './adapters.js';
5
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,eAAe,CAAC"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
2
  * URK Adapters - Contract-first capability adapters.
3
3
  */
4
- export * from './adapters';
4
+ export * from './adapters.js';
5
5
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,YAAY,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc,eAAe,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@urk/adapters",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "URK adapters - reference three, pointer, input, loading, storage, and overlay capability adapters",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -10,6 +10,10 @@
10
10
  ".": {
11
11
  "import": "./dist/index.js",
12
12
  "types": "./dist/index.d.ts"
13
+ },
14
+ "./dom": {
15
+ "import": "./dist/dom.js",
16
+ "types": "./dist/dom.d.ts"
13
17
  }
14
18
  },
15
19
  "files": [
@@ -23,12 +27,17 @@
23
27
  "lint": "echo 'no lint yet'"
24
28
  },
25
29
  "peerDependencies": {
26
- "@urk/core": "workspace:*",
30
+ "@urk/core": "^0.1.1",
27
31
  "three": "^0.184.0"
28
32
  },
33
+ "peerDependenciesMeta": {
34
+ "three": {
35
+ "optional": true
36
+ }
37
+ },
29
38
  "devDependencies": {
30
39
  "@types/three": "^0.184.0",
31
- "@urk/core": "workspace:*",
40
+ "@urk/core": "^0.1.1",
32
41
  "three": "^0.184.0",
33
42
  "typescript": "^5.3.0"
34
43
  }
package/src/adapters.ts CHANGED
@@ -2,10 +2,11 @@
2
2
  * URK adapter exports.
3
3
  */
4
4
 
5
- export * from './loading';
6
- export * from './input';
7
- export * from './pointer';
8
- export * from './storage';
9
- export * from './three';
10
- export * from './ui-widgets';
11
- export * from './contracts';
5
+ export * from './loading.js';
6
+ export * from './audio.js';
7
+ export * from './input.js';
8
+ export * from './pointer.js';
9
+ export * from './storage.js';
10
+ export * from './three.js';
11
+ export * from './ui-widgets.js';
12
+ export * from './contracts.js';
package/src/audio.ts ADDED
@@ -0,0 +1,458 @@
1
+ /**
2
+ * Company: EonHive Inc.
3
+ * Title: Audio Adapter
4
+ * Purpose: Provide a small browser-native audio transport capability for URK.
5
+ * Author: Stan Nesi
6
+ * Created: 2026-04-23
7
+ * Updated: 2026-04-23
8
+ * Notes: Vibe coded with Codex.
9
+ */
10
+
11
+ import type { AdapterRegistration } from '@urk/core';
12
+
13
+ export interface AudioTrackDefinition {
14
+ id: string;
15
+ src: string;
16
+ label: string;
17
+ loop?: boolean;
18
+ volume?: number;
19
+ }
20
+
21
+ export type AudioPlaybackStatus = 'idle' | 'loading' | 'ready' | 'playing' | 'paused' | 'error';
22
+
23
+ export interface AudioTransportSnapshot {
24
+ activeTrackId: string | null;
25
+ status: AudioPlaybackStatus;
26
+ muted: boolean;
27
+ volume: number;
28
+ loadedTrackIds: string[];
29
+ errorMessage?: string;
30
+ }
31
+
32
+ export type AudioListener = (snapshot: AudioTransportSnapshot) => void;
33
+
34
+ export interface AudioAdapterApi {
35
+ registerTrack(track: AudioTrackDefinition): void;
36
+ preload(trackId: string): Promise<void>;
37
+ play(trackId?: string): Promise<void>;
38
+ pause(): void;
39
+ stop(): void;
40
+ setMuted(next: boolean): void;
41
+ setVolume(next: number): void;
42
+ getSnapshot(): AudioTransportSnapshot;
43
+ subscribe(listener: AudioListener): () => void;
44
+ }
45
+
46
+ type RegisteredTrack = {
47
+ definition: AudioTrackDefinition;
48
+ element: HTMLAudioElement;
49
+ onEnded: () => void;
50
+ onError: () => void;
51
+ };
52
+
53
+ function clampVolume(value: number): number {
54
+ return Math.max(0, Math.min(value, 1));
55
+ }
56
+
57
+ function cloneSnapshot(snapshot: AudioTransportSnapshot): AudioTransportSnapshot {
58
+ return {
59
+ ...snapshot,
60
+ loadedTrackIds: [...snapshot.loadedTrackIds],
61
+ };
62
+ }
63
+
64
+ function createEmptySnapshot(): AudioTransportSnapshot {
65
+ return {
66
+ activeTrackId: null,
67
+ status: 'idle',
68
+ muted: false,
69
+ volume: 1,
70
+ loadedTrackIds: [],
71
+ };
72
+ }
73
+
74
+ function formatTrackError(track: AudioTrackDefinition, action: 'preload' | 'playback'): string {
75
+ return `Failed ${action} for audio track: ${track.label}`;
76
+ }
77
+
78
+ export function createAudioAdapter(
79
+ id = 'audio-adapter',
80
+ ): AdapterRegistration<AudioAdapterApi> {
81
+ let teardown: (() => void) | null = null;
82
+
83
+ return {
84
+ id,
85
+ capability: 'audio',
86
+ isSupported() {
87
+ return typeof window !== 'undefined' && typeof Audio !== 'undefined';
88
+ },
89
+ setup(ctx) {
90
+ const tracks = new Map<string, RegisteredTrack>();
91
+ const loadedTrackIds = new Set<string>();
92
+ const listeners = new Set<AudioListener>();
93
+ let snapshot = createEmptySnapshot();
94
+
95
+ const publish = (
96
+ partial: Partial<AudioTransportSnapshot> = {},
97
+ extraEvent?:
98
+ | {
99
+ type: 'audio:ended' | 'audio:error';
100
+ payload: unknown;
101
+ }
102
+ | undefined,
103
+ ): AudioTransportSnapshot => {
104
+ snapshot = {
105
+ ...snapshot,
106
+ ...partial,
107
+ loadedTrackIds: [...loadedTrackIds],
108
+ };
109
+
110
+ const next = cloneSnapshot(snapshot);
111
+
112
+ ctx.events.emit({
113
+ type: 'audio:changed',
114
+ source: id,
115
+ payload: next,
116
+ timestamp: Date.now(),
117
+ });
118
+
119
+ if (extraEvent) {
120
+ ctx.events.emit({
121
+ type: extraEvent.type,
122
+ source: id,
123
+ payload: extraEvent.payload,
124
+ timestamp: Date.now(),
125
+ });
126
+ }
127
+
128
+ for (const listener of [...listeners]) {
129
+ listener(next);
130
+ }
131
+
132
+ return next;
133
+ };
134
+
135
+ const requireTrack = (trackId: string): RegisteredTrack => {
136
+ const record = tracks.get(trackId);
137
+
138
+ if (!record) {
139
+ throw new Error(`Unknown audio track: ${trackId}`);
140
+ }
141
+
142
+ return record;
143
+ };
144
+
145
+ const applyElementSettings = (record: RegisteredTrack): void => {
146
+ record.element.loop = record.definition.loop ?? false;
147
+ record.element.muted = snapshot.muted;
148
+ record.element.volume = clampVolume(snapshot.volume * (record.definition.volume ?? 1));
149
+ };
150
+
151
+ const stopElement = (record: RegisteredTrack): void => {
152
+ record.element.pause();
153
+
154
+ try {
155
+ record.element.currentTime = 0;
156
+ } catch {
157
+ // Some browsers can reject currentTime writes if metadata is unavailable.
158
+ }
159
+ };
160
+
161
+ const preloadElement = async (record: RegisteredTrack): Promise<void> => {
162
+ if (loadedTrackIds.has(record.definition.id) || record.element.readyState >= 3) {
163
+ loadedTrackIds.add(record.definition.id);
164
+ return;
165
+ }
166
+
167
+ await new Promise<void>((resolve, reject) => {
168
+ const onReady = (): void => {
169
+ cleanup();
170
+ loadedTrackIds.add(record.definition.id);
171
+ resolve();
172
+ };
173
+
174
+ const onError = (): void => {
175
+ cleanup();
176
+ reject(new Error(formatTrackError(record.definition, 'preload')));
177
+ };
178
+
179
+ const cleanup = (): void => {
180
+ record.element.removeEventListener('canplaythrough', onReady);
181
+ record.element.removeEventListener('loadeddata', onReady);
182
+ record.element.removeEventListener('error', onError);
183
+ };
184
+
185
+ record.element.addEventListener('canplaythrough', onReady, { once: true });
186
+ record.element.addEventListener('loadeddata', onReady, { once: true });
187
+ record.element.addEventListener('error', onError, { once: true });
188
+ record.element.load();
189
+ });
190
+ };
191
+
192
+ const preloadTrack = async (
193
+ record: RegisteredTrack,
194
+ shouldSetLoading: boolean,
195
+ ): Promise<void> => {
196
+ if (shouldSetLoading) {
197
+ publish({
198
+ status: 'loading',
199
+ errorMessage: undefined,
200
+ });
201
+ }
202
+
203
+ try {
204
+ await preloadElement(record);
205
+ publish({
206
+ status: shouldSetLoading ? 'ready' : snapshot.status,
207
+ errorMessage: undefined,
208
+ });
209
+ } catch (error) {
210
+ const message =
211
+ error instanceof Error ? error.message : formatTrackError(record.definition, 'preload');
212
+
213
+ publish(
214
+ {
215
+ status: 'error',
216
+ errorMessage: message,
217
+ },
218
+ {
219
+ type: 'audio:error',
220
+ payload: {
221
+ trackId: record.definition.id,
222
+ label: record.definition.label,
223
+ message,
224
+ },
225
+ },
226
+ );
227
+
228
+ throw new Error(message);
229
+ }
230
+ };
231
+
232
+ teardown = (): void => {
233
+ for (const record of tracks.values()) {
234
+ record.element.pause();
235
+ record.element.removeEventListener('ended', record.onEnded);
236
+ record.element.removeEventListener('error', record.onError);
237
+ record.element.src = '';
238
+ record.element.load();
239
+ }
240
+
241
+ tracks.clear();
242
+ loadedTrackIds.clear();
243
+ listeners.clear();
244
+ };
245
+
246
+ return {
247
+ registerTrack(track) {
248
+ if (tracks.has(track.id)) {
249
+ throw new Error(`Audio track already registered: ${track.id}`);
250
+ }
251
+
252
+ const element = new Audio(track.src);
253
+ element.preload = 'auto';
254
+
255
+ const onEnded = (): void => {
256
+ if (snapshot.activeTrackId !== track.id) {
257
+ return;
258
+ }
259
+
260
+ publish(
261
+ {
262
+ status: 'ready',
263
+ errorMessage: undefined,
264
+ },
265
+ {
266
+ type: 'audio:ended',
267
+ payload: {
268
+ trackId: track.id,
269
+ label: track.label,
270
+ },
271
+ },
272
+ );
273
+ };
274
+
275
+ const onError = (): void => {
276
+ const message = formatTrackError(track, 'playback');
277
+
278
+ if (snapshot.status === 'error' && snapshot.errorMessage === message) {
279
+ return;
280
+ }
281
+
282
+ publish(
283
+ {
284
+ activeTrackId: snapshot.activeTrackId ?? track.id,
285
+ status: 'error',
286
+ errorMessage: message,
287
+ },
288
+ {
289
+ type: 'audio:error',
290
+ payload: {
291
+ trackId: track.id,
292
+ label: track.label,
293
+ message,
294
+ },
295
+ },
296
+ );
297
+ };
298
+
299
+ const record: RegisteredTrack = {
300
+ definition: {
301
+ ...track,
302
+ volume: clampVolume(track.volume ?? 1),
303
+ },
304
+ element,
305
+ onEnded,
306
+ onError,
307
+ };
308
+
309
+ element.addEventListener('ended', onEnded);
310
+ element.addEventListener('error', onError);
311
+ applyElementSettings(record);
312
+ tracks.set(track.id, record);
313
+ },
314
+ async preload(trackId) {
315
+ const record = requireTrack(trackId);
316
+ const shouldSetLoading =
317
+ snapshot.status === 'idle' ||
318
+ snapshot.status === 'loading' ||
319
+ snapshot.status === 'ready';
320
+
321
+ await preloadTrack(record, shouldSetLoading);
322
+ },
323
+ async play(trackId) {
324
+ const nextTrackId = trackId ?? snapshot.activeTrackId;
325
+
326
+ if (!nextTrackId) {
327
+ return;
328
+ }
329
+
330
+ const nextRecord = requireTrack(nextTrackId);
331
+ const previousTrackId = snapshot.activeTrackId;
332
+
333
+ if (previousTrackId && previousTrackId !== nextTrackId) {
334
+ stopElement(requireTrack(previousTrackId));
335
+ }
336
+
337
+ if (!loadedTrackIds.has(nextTrackId) && nextRecord.element.readyState < 3) {
338
+ publish({
339
+ activeTrackId: nextTrackId,
340
+ status: 'loading',
341
+ errorMessage: undefined,
342
+ });
343
+
344
+ await preloadTrack(nextRecord, false);
345
+ }
346
+
347
+ if (
348
+ previousTrackId !== nextTrackId ||
349
+ nextRecord.element.ended ||
350
+ (nextRecord.element.duration > 0 &&
351
+ nextRecord.element.currentTime >= nextRecord.element.duration)
352
+ ) {
353
+ try {
354
+ nextRecord.element.currentTime = 0;
355
+ } catch {
356
+ // Ignore currentTime reset failures for browsers without loaded metadata yet.
357
+ }
358
+ }
359
+
360
+ applyElementSettings(nextRecord);
361
+
362
+ try {
363
+ await nextRecord.element.play();
364
+ publish({
365
+ activeTrackId: nextTrackId,
366
+ status: 'playing',
367
+ errorMessage: undefined,
368
+ });
369
+ } catch (error) {
370
+ const message =
371
+ error instanceof Error ? error.message : formatTrackError(nextRecord.definition, 'playback');
372
+
373
+ publish(
374
+ {
375
+ activeTrackId: nextTrackId,
376
+ status: 'error',
377
+ errorMessage: message,
378
+ },
379
+ {
380
+ type: 'audio:error',
381
+ payload: {
382
+ trackId: nextTrackId,
383
+ label: nextRecord.definition.label,
384
+ message,
385
+ },
386
+ },
387
+ );
388
+
389
+ throw new Error(message);
390
+ }
391
+ },
392
+ pause() {
393
+ if (!snapshot.activeTrackId) {
394
+ return;
395
+ }
396
+
397
+ const record = requireTrack(snapshot.activeTrackId);
398
+ record.element.pause();
399
+
400
+ publish({
401
+ status: 'paused',
402
+ errorMessage: undefined,
403
+ });
404
+ },
405
+ stop() {
406
+ if (!snapshot.activeTrackId) {
407
+ return;
408
+ }
409
+
410
+ stopElement(requireTrack(snapshot.activeTrackId));
411
+
412
+ publish({
413
+ status: 'ready',
414
+ errorMessage: undefined,
415
+ });
416
+ },
417
+ setMuted(next) {
418
+ const muted = Boolean(next);
419
+
420
+ for (const record of tracks.values()) {
421
+ record.element.muted = muted;
422
+ }
423
+
424
+ publish({
425
+ muted,
426
+ errorMessage: undefined,
427
+ });
428
+ },
429
+ setVolume(next) {
430
+ const volume = clampVolume(next);
431
+
432
+ for (const record of tracks.values()) {
433
+ record.element.volume = clampVolume(volume * (record.definition.volume ?? 1));
434
+ }
435
+
436
+ publish({
437
+ volume,
438
+ errorMessage: undefined,
439
+ });
440
+ },
441
+ getSnapshot() {
442
+ return cloneSnapshot(snapshot);
443
+ },
444
+ subscribe(listener) {
445
+ listeners.add(listener);
446
+
447
+ return () => {
448
+ listeners.delete(listener);
449
+ };
450
+ },
451
+ };
452
+ },
453
+ dispose() {
454
+ teardown?.();
455
+ teardown = null;
456
+ },
457
+ };
458
+ }
package/src/dom.ts ADDED
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Company: EonHive Inc.
3
+ * Title: DOM Adapter Exports
4
+ * Purpose: Provide a dependency-light adapter surface for DOM-only URK consumers.
5
+ * Author: Stan Nesi
6
+ * Created: 2026-04-23
7
+ * Updated: 2026-04-23
8
+ * Notes: Vibe coded with Codex.
9
+ */
10
+
11
+ export * from './loading.js';
12
+ export * from './input.js';
13
+ export * from './pointer.js';
14
+ export * from './storage.js';
15
+ export * from './ui-widgets.js';
16
+ export * from './contracts.js';
package/src/index.ts CHANGED
@@ -2,4 +2,4 @@
2
2
  * URK Adapters - Contract-first capability adapters.
3
3
  */
4
4
 
5
- export * from './adapters';
5
+ export * from './adapters.js';