@urk/adapters 0.1.2 → 0.1.4

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 (63) hide show
  1. package/README.md +44 -2
  2. package/dist/adapters.d.ts +7 -7
  3. package/dist/adapters.d.ts.map +1 -1
  4. package/dist/adapters.js +7 -7
  5. package/dist/adapters.js.map +1 -1
  6. package/dist/{audio.d.ts → audio/index.d.ts} +1 -1
  7. package/dist/audio/index.d.ts.map +1 -0
  8. package/dist/{audio.js → audio/index.js} +1 -1
  9. package/dist/audio/index.js.map +1 -0
  10. package/dist/dom.d.ts +5 -5
  11. package/dist/dom.d.ts.map +1 -1
  12. package/dist/dom.js +5 -5
  13. package/dist/dom.js.map +1 -1
  14. package/dist/{input.d.ts → input/index.d.ts} +1 -1
  15. package/dist/input/index.d.ts.map +1 -0
  16. package/dist/{input.js → input/index.js} +1 -1
  17. package/dist/input/index.js.map +1 -0
  18. package/dist/{loading.d.ts → loading/index.d.ts} +1 -1
  19. package/dist/loading/index.d.ts.map +1 -0
  20. package/dist/{loading.js → loading/index.js} +1 -1
  21. package/dist/loading/index.js.map +1 -0
  22. package/dist/{pointer.d.ts → pointer/index.d.ts} +1 -1
  23. package/dist/pointer/index.d.ts.map +1 -0
  24. package/dist/{pointer.js → pointer/index.js} +1 -1
  25. package/dist/pointer/index.js.map +1 -0
  26. package/dist/{storage.d.ts → storage/index.d.ts} +1 -1
  27. package/dist/storage/index.d.ts.map +1 -0
  28. package/dist/{storage.js → storage/index.js} +1 -1
  29. package/dist/storage/index.js.map +1 -0
  30. package/dist/{three.d.ts → three/index.d.ts} +1 -1
  31. package/dist/three/index.d.ts.map +1 -0
  32. package/dist/{three.js → three/index.js} +1 -1
  33. package/dist/three/index.js.map +1 -0
  34. package/dist/{ui-widgets.d.ts → ui-widgets/index.d.ts} +1 -1
  35. package/dist/ui-widgets/index.d.ts.map +1 -0
  36. package/dist/{ui-widgets.js → ui-widgets/index.js} +1 -1
  37. package/dist/ui-widgets/index.js.map +1 -0
  38. package/package.json +3 -4
  39. package/dist/audio.d.ts.map +0 -1
  40. package/dist/audio.js.map +0 -1
  41. package/dist/input.d.ts.map +0 -1
  42. package/dist/input.js.map +0 -1
  43. package/dist/loading.d.ts.map +0 -1
  44. package/dist/loading.js.map +0 -1
  45. package/dist/pointer.d.ts.map +0 -1
  46. package/dist/pointer.js.map +0 -1
  47. package/dist/storage.d.ts.map +0 -1
  48. package/dist/storage.js.map +0 -1
  49. package/dist/three.d.ts.map +0 -1
  50. package/dist/three.js.map +0 -1
  51. package/dist/ui-widgets.d.ts.map +0 -1
  52. package/dist/ui-widgets.js.map +0 -1
  53. package/src/adapters.ts +0 -12
  54. package/src/audio.ts +0 -458
  55. package/src/contracts.ts +0 -15
  56. package/src/dom.ts +0 -16
  57. package/src/index.ts +0 -5
  58. package/src/input.ts +0 -207
  59. package/src/loading.ts +0 -174
  60. package/src/pointer.ts +0 -203
  61. package/src/storage.ts +0 -194
  62. package/src/three.ts +0 -178
  63. package/src/ui-widgets.ts +0 -120
package/src/loading.ts DELETED
@@ -1,174 +0,0 @@
1
- /**
2
- * Company: EonHive Inc.
3
- * Title: Loading Adapter
4
- * Purpose: Track staged loading progress and expose a small observable API.
5
- * Author: Stan Nesi
6
- * Created: 2026-04-12
7
- * Updated: 2026-04-15
8
- * Notes: Vibe coded with Codex.
9
- */
10
-
11
- import type { AdapterRegistration } from '@urk/core';
12
-
13
- export interface LoadingStage {
14
- id: string;
15
- label: string;
16
- weight?: number;
17
- }
18
-
19
- export interface LoadingSnapshot {
20
- active: boolean;
21
- complete: boolean;
22
- progress: number;
23
- message: string;
24
- stageId: string | null;
25
- stageLabel: string | null;
26
- stages: LoadingStage[];
27
- updatedAt: number;
28
- }
29
-
30
- export type LoadingListener = (snapshot: LoadingSnapshot) => void;
31
-
32
- export interface LoadingAdapterApi {
33
- begin(stages: LoadingStage[], message?: string): LoadingSnapshot;
34
- setStage(stageId: string, progressWithinStage: number, message?: string): LoadingSnapshot;
35
- complete(message?: string): LoadingSnapshot;
36
- getSnapshot(): LoadingSnapshot;
37
- subscribe(listener: LoadingListener): () => void;
38
- }
39
-
40
- function normalizeStages(stages: LoadingStage[]): LoadingStage[] {
41
- if (stages.length === 0) {
42
- throw new Error('Loading adapter requires at least one stage.');
43
- }
44
-
45
- return stages.map((stage) => ({
46
- ...stage,
47
- weight: stage.weight && stage.weight > 0 ? stage.weight : 1,
48
- }));
49
- }
50
-
51
- function clampProgress(progress: number): number {
52
- return Math.max(0, Math.min(progress, 1));
53
- }
54
-
55
- function createEmptySnapshot(): LoadingSnapshot {
56
- return {
57
- active: false,
58
- complete: false,
59
- progress: 0,
60
- message: 'Waiting to start',
61
- stageId: null,
62
- stageLabel: null,
63
- stages: [],
64
- updatedAt: Date.now(),
65
- };
66
- }
67
-
68
- export function createLoadingAdapter(
69
- id = 'loading-adapter',
70
- ): AdapterRegistration<LoadingAdapterApi> {
71
- return {
72
- id,
73
- capability: 'loading',
74
- setup(ctx) {
75
- let snapshot = createEmptySnapshot();
76
- const listeners = new Set<LoadingListener>();
77
-
78
- const publish = (): LoadingSnapshot => {
79
- const next = { ...snapshot, stages: [...snapshot.stages] };
80
-
81
- ctx.events.emit({
82
- type: 'loading:changed',
83
- source: id,
84
- payload: next,
85
- timestamp: Date.now(),
86
- });
87
-
88
- for (const listener of [...listeners]) {
89
- listener(next);
90
- }
91
-
92
- return next;
93
- };
94
-
95
- const getTotalWeight = (): number => {
96
- return snapshot.stages.reduce((total, stage) => total + (stage.weight ?? 1), 0);
97
- };
98
-
99
- return {
100
- begin(stages, message = 'Starting load') {
101
- const nextStages = normalizeStages(stages);
102
- const firstStage = nextStages[0];
103
-
104
- snapshot = {
105
- active: true,
106
- complete: false,
107
- progress: 0,
108
- message,
109
- stageId: firstStage.id,
110
- stageLabel: firstStage.label,
111
- stages: nextStages,
112
- updatedAt: Date.now(),
113
- };
114
-
115
- return publish();
116
- },
117
- setStage(stageId, progressWithinStage, message) {
118
- const stageIndex = snapshot.stages.findIndex((stage) => stage.id === stageId);
119
-
120
- if (stageIndex === -1) {
121
- throw new Error(`Unknown loading stage: ${stageId}`);
122
- }
123
-
124
- const previousWeight = snapshot.stages
125
- .slice(0, stageIndex)
126
- .reduce((total, stage) => total + (stage.weight ?? 1), 0);
127
- const currentStage = snapshot.stages[stageIndex];
128
- const totalWeight = getTotalWeight();
129
- const progress =
130
- (previousWeight + (currentStage.weight ?? 1) * clampProgress(progressWithinStage)) /
131
- totalWeight;
132
-
133
- snapshot = {
134
- ...snapshot,
135
- active: true,
136
- progress,
137
- message: message ?? snapshot.message,
138
- stageId: currentStage.id,
139
- stageLabel: currentStage.label,
140
- updatedAt: Date.now(),
141
- };
142
-
143
- return publish();
144
- },
145
- complete(message = 'Loading complete') {
146
- const lastStage = snapshot.stages[snapshot.stages.length - 1] ?? null;
147
-
148
- snapshot = {
149
- ...snapshot,
150
- active: false,
151
- complete: true,
152
- progress: 1,
153
- message,
154
- stageId: lastStage?.id ?? null,
155
- stageLabel: lastStage?.label ?? null,
156
- updatedAt: Date.now(),
157
- };
158
-
159
- return publish();
160
- },
161
- getSnapshot() {
162
- return { ...snapshot, stages: [...snapshot.stages] };
163
- },
164
- subscribe(listener) {
165
- listeners.add(listener);
166
-
167
- return () => {
168
- listeners.delete(listener);
169
- };
170
- },
171
- };
172
- },
173
- };
174
- }
package/src/pointer.ts DELETED
@@ -1,203 +0,0 @@
1
- /**
2
- * Company: EonHive Inc.
3
- * Title: Pointer Adapter
4
- * Purpose: Normalize pointer target binding and emit interaction events.
5
- * Author: Stan Nesi
6
- * Created: 2026-04-12
7
- * Updated: 2026-04-22
8
- * Notes: Vibe coded with Codex.
9
- */
10
-
11
- import type { AdapterRegistration } from '@urk/core';
12
-
13
- export interface PointerTargetDefinition {
14
- id: string;
15
- element: HTMLElement;
16
- meta?: Record<string, unknown>;
17
- }
18
-
19
- export interface PointerTargetEventPayload {
20
- targetId: string;
21
- element: HTMLElement;
22
- meta?: Record<string, unknown>;
23
- nativeEvent: MouseEvent | PointerEvent;
24
- }
25
-
26
- export interface PointerSurfaceDefinition {
27
- id: string;
28
- element: HTMLElement;
29
- meta?: Record<string, unknown>;
30
- }
31
-
32
- export interface PointerSurfaceEventPayload {
33
- surfaceId: string;
34
- element: HTMLElement;
35
- meta?: Record<string, unknown>;
36
- clientX: number;
37
- clientY: number;
38
- localX: number;
39
- localY: number;
40
- nativeEvent: MouseEvent | PointerEvent;
41
- }
42
-
43
- export interface PointerAdapterApi {
44
- bindTarget(target: PointerTargetDefinition): () => void;
45
- bindSurface(surface: PointerSurfaceDefinition): () => void;
46
- clear(): void;
47
- }
48
-
49
- function getSurfaceCoordinates(
50
- element: HTMLElement,
51
- nativeEvent: MouseEvent | PointerEvent,
52
- ): {
53
- clientX: number;
54
- clientY: number;
55
- localX: number;
56
- localY: number;
57
- } {
58
- const bounds = element.getBoundingClientRect();
59
-
60
- return {
61
- clientX: nativeEvent.clientX,
62
- clientY: nativeEvent.clientY,
63
- localX: nativeEvent.clientX - bounds.left,
64
- localY: nativeEvent.clientY - bounds.top,
65
- };
66
- }
67
-
68
- export function createPointerAdapter(
69
- id = 'pointer-adapter',
70
- ): AdapterRegistration<PointerAdapterApi> {
71
- return {
72
- id,
73
- capability: 'pointer',
74
- setup(ctx) {
75
- const cleanups = new Map<string, () => void>();
76
-
77
- const emit = <TPayload>(type: string, payload: TPayload): void => {
78
- ctx.events.emit({
79
- type,
80
- source: id,
81
- payload,
82
- timestamp: Date.now(),
83
- });
84
- };
85
-
86
- return {
87
- bindTarget(target) {
88
- if (cleanups.has(target.id)) {
89
- throw new Error(`Pointer target already bound: ${target.id}`);
90
- }
91
-
92
- const onEnter = (nativeEvent: PointerEvent): void => {
93
- emit('pointer:hover', {
94
- targetId: target.id,
95
- element: target.element,
96
- meta: target.meta,
97
- nativeEvent,
98
- });
99
- };
100
-
101
- const onLeave = (nativeEvent: PointerEvent): void => {
102
- emit('pointer:leave', {
103
- targetId: target.id,
104
- element: target.element,
105
- meta: target.meta,
106
- nativeEvent,
107
- });
108
- };
109
-
110
- const onSelect = (nativeEvent: MouseEvent): void => {
111
- emit('pointer:select', {
112
- targetId: target.id,
113
- element: target.element,
114
- meta: target.meta,
115
- nativeEvent,
116
- });
117
- };
118
-
119
- target.element.addEventListener('pointerenter', onEnter);
120
- target.element.addEventListener('pointerleave', onLeave);
121
- target.element.addEventListener('click', onSelect);
122
-
123
- const cleanup = (): void => {
124
- target.element.removeEventListener('pointerenter', onEnter);
125
- target.element.removeEventListener('pointerleave', onLeave);
126
- target.element.removeEventListener('click', onSelect);
127
- };
128
-
129
- cleanups.set(target.id, cleanup);
130
-
131
- return () => {
132
- cleanup();
133
- cleanups.delete(target.id);
134
- };
135
- },
136
- bindSurface(surface) {
137
- const cleanupKey = `surface:${surface.id}`;
138
-
139
- if (cleanups.has(cleanupKey)) {
140
- throw new Error(`Pointer surface already bound: ${surface.id}`);
141
- }
142
-
143
- const onMove = (nativeEvent: PointerEvent): void => {
144
- emit('pointer:surface-move', {
145
- surfaceId: surface.id,
146
- element: surface.element,
147
- meta: surface.meta,
148
- ...getSurfaceCoordinates(surface.element, nativeEvent),
149
- nativeEvent,
150
- } satisfies PointerSurfaceEventPayload);
151
- };
152
-
153
- const onLeave = (nativeEvent: PointerEvent): void => {
154
- emit('pointer:surface-leave', {
155
- surfaceId: surface.id,
156
- element: surface.element,
157
- meta: surface.meta,
158
- ...getSurfaceCoordinates(surface.element, nativeEvent),
159
- nativeEvent,
160
- } satisfies PointerSurfaceEventPayload);
161
- };
162
-
163
- const onSelect = (nativeEvent: MouseEvent): void => {
164
- emit('pointer:surface-select', {
165
- surfaceId: surface.id,
166
- element: surface.element,
167
- meta: surface.meta,
168
- ...getSurfaceCoordinates(surface.element, nativeEvent),
169
- nativeEvent,
170
- } satisfies PointerSurfaceEventPayload);
171
- };
172
-
173
- surface.element.addEventListener('pointermove', onMove);
174
- surface.element.addEventListener('pointerleave', onLeave);
175
- surface.element.addEventListener('click', onSelect);
176
-
177
- const cleanup = (): void => {
178
- surface.element.removeEventListener('pointermove', onMove);
179
- surface.element.removeEventListener('pointerleave', onLeave);
180
- surface.element.removeEventListener('click', onSelect);
181
- };
182
-
183
- cleanups.set(cleanupKey, cleanup);
184
-
185
- return () => {
186
- cleanup();
187
- cleanups.delete(cleanupKey);
188
- };
189
- },
190
- clear() {
191
- for (const cleanup of cleanups.values()) {
192
- cleanup();
193
- }
194
-
195
- cleanups.clear();
196
- },
197
- };
198
- },
199
- dispose(_ctx, api) {
200
- api.clear();
201
- },
202
- };
203
- }
package/src/storage.ts DELETED
@@ -1,194 +0,0 @@
1
- /**
2
- * Company: EonHive Inc.
3
- * Title: Storage Adapter
4
- * Purpose: Expose a small namespaced storage capability for local and session persistence.
5
- * Author: Stan Nesi
6
- * Created: 2026-04-21
7
- * Updated: 2026-04-21
8
- * Notes: Vibe coded with Codex.
9
- */
10
-
11
- import type { AdapterRegistration, RuntimeContext } from '@urk/core';
12
-
13
- export type StorageArea = 'local' | 'session';
14
-
15
- export interface StorageAdapterOptions {
16
- id?: string;
17
- namespace?: string;
18
- }
19
-
20
- export interface StorageAdapterApi {
21
- getItem<T = unknown>(key: string, area?: StorageArea): T | null;
22
- setItem<T>(key: string, value: T, area?: StorageArea): void;
23
- removeItem(key: string, area?: StorageArea): void;
24
- listKeys(area?: StorageArea): string[];
25
- clear(area?: StorageArea): void;
26
- }
27
-
28
- type StorageBackendLike = {
29
- length: number;
30
- key(index: number): string | null;
31
- getItem(key: string): string | null;
32
- setItem(key: string, value: string): void;
33
- removeItem(key: string): void;
34
- };
35
-
36
- const DEFAULT_NAMESPACE = 'urk';
37
-
38
- function isStorageBackendLike(value: unknown): value is StorageBackendLike {
39
- if (typeof value !== 'object' || value === null) {
40
- return false;
41
- }
42
-
43
- const candidate = value as Partial<StorageBackendLike>;
44
-
45
- return (
46
- typeof candidate.length === 'number' &&
47
- typeof candidate.key === 'function' &&
48
- typeof candidate.getItem === 'function' &&
49
- typeof candidate.setItem === 'function' &&
50
- typeof candidate.removeItem === 'function'
51
- );
52
- }
53
-
54
- function resolveServiceBackend(
55
- ctx: RuntimeContext,
56
- area: StorageArea,
57
- ): StorageBackendLike | null {
58
- const serviceName = area === 'local' ? 'storage:local' : 'storage:session';
59
- const value = ctx.services.get<unknown>(serviceName);
60
-
61
- if (value === undefined) {
62
- return null;
63
- }
64
-
65
- if (!isStorageBackendLike(value)) {
66
- throw new Error(
67
- `Service ${serviceName} must provide a Storage-compatible backend.`,
68
- );
69
- }
70
-
71
- return value;
72
- }
73
-
74
- function resolveWindowBackend(area: StorageArea): StorageBackendLike | null {
75
- if (typeof window === 'undefined') {
76
- return null;
77
- }
78
-
79
- try {
80
- return area === 'local' ? window.localStorage : window.sessionStorage;
81
- } catch (error) {
82
- const message =
83
- error instanceof Error ? error.message : `Unable to access ${area}Storage.`;
84
-
85
- throw new Error(`Storage adapter could not access ${area}Storage: ${message}`);
86
- }
87
- }
88
-
89
- function resolveBackend(ctx: RuntimeContext, area: StorageArea): StorageBackendLike {
90
- const serviceBackend = resolveServiceBackend(ctx, area);
91
-
92
- if (serviceBackend) {
93
- return serviceBackend;
94
- }
95
-
96
- const windowBackend = resolveWindowBackend(area);
97
-
98
- if (windowBackend) {
99
- return windowBackend;
100
- }
101
-
102
- throw new Error(`Storage adapter requires a ${area} storage backend.`);
103
- }
104
-
105
- function getStoragePrefix(namespace: string): string {
106
- return `${namespace}:`;
107
- }
108
-
109
- function getNamespacedKey(namespace: string, key: string): string {
110
- return `${getStoragePrefix(namespace)}${key}`;
111
- }
112
-
113
- function collectNamespacedKeys(
114
- backend: StorageBackendLike,
115
- namespace: string,
116
- ): string[] {
117
- const keys: string[] = [];
118
- const prefix = getStoragePrefix(namespace);
119
-
120
- for (let index = 0; index < backend.length; index += 1) {
121
- const key = backend.key(index);
122
-
123
- if (!key || !key.startsWith(prefix)) {
124
- continue;
125
- }
126
-
127
- keys.push(key.slice(prefix.length));
128
- }
129
-
130
- return keys;
131
- }
132
-
133
- export function createStorageAdapter(
134
- options: StorageAdapterOptions = {},
135
- ): AdapterRegistration<StorageAdapterApi> {
136
- const id = options.id ?? 'storage-adapter';
137
- const namespace = options.namespace?.trim() || DEFAULT_NAMESPACE;
138
-
139
- return {
140
- id,
141
- capability: 'storage',
142
- setup(ctx) {
143
- const backends: Record<StorageArea, StorageBackendLike> = {
144
- local: resolveBackend(ctx, 'local'),
145
- session: resolveBackend(ctx, 'session'),
146
- };
147
-
148
- const getBackend = (area: StorageArea = 'local'): StorageBackendLike => {
149
- return backends[area];
150
- };
151
-
152
- return {
153
- getItem<T = unknown>(key: string, area: StorageArea = 'local'): T | null {
154
- const raw = getBackend(area).getItem(getNamespacedKey(namespace, key));
155
-
156
- if (raw === null) {
157
- return null;
158
- }
159
-
160
- try {
161
- return JSON.parse(raw) as T;
162
- } catch {
163
- throw new Error(
164
- `Stored value for key "${key}" in ${area} storage is not valid JSON.`,
165
- );
166
- }
167
- },
168
- setItem<T>(key: string, value: T, area: StorageArea = 'local'): void {
169
- const serialized = JSON.stringify(value);
170
-
171
- if (serialized === undefined) {
172
- throw new Error(`Storage value for key "${key}" is not JSON serializable.`);
173
- }
174
-
175
- getBackend(area).setItem(getNamespacedKey(namespace, key), serialized);
176
- },
177
- removeItem(key: string, area: StorageArea = 'local'): void {
178
- getBackend(area).removeItem(getNamespacedKey(namespace, key));
179
- },
180
- listKeys(area: StorageArea = 'local'): string[] {
181
- return collectNamespacedKeys(getBackend(area), namespace);
182
- },
183
- clear(area: StorageArea = 'local'): void {
184
- const backend = getBackend(area);
185
-
186
- // Only remove keys owned by this adapter namespace.
187
- for (const key of collectNamespacedKeys(backend, namespace)) {
188
- backend.removeItem(getNamespacedKey(namespace, key));
189
- }
190
- },
191
- };
192
- },
193
- };
194
- }