@adukiorg/anza 0.2.0 → 0.2.3

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 (83) hide show
  1. package/CHANGELOG.md +90 -4
  2. package/README.md +97 -133
  3. package/bin/anza/anza-linux-arm64 +0 -0
  4. package/bin/anza/anza-linux-x64 +0 -0
  5. package/bin/anza/anza-macos-arm64 +0 -0
  6. package/bin/anza/anza-macos-x64 +0 -0
  7. package/bin/anza/anza-windows-x64.exe +0 -0
  8. package/bin/anza/find.js +35 -0
  9. package/bin/anza/index.js +34 -0
  10. package/bin/anza/launch.js +19 -0
  11. package/bin/common/index.js +7 -0
  12. package/bin/common/logs.js +62 -0
  13. package/bin/create/copy.js +18 -0
  14. package/bin/create/index.js +45 -0
  15. package/bin/create/run.js +210 -0
  16. package/bin/create/write.js +19 -0
  17. package/importmap.json +4 -0
  18. package/package.json +16 -10
  19. package/src/core/offline/{usage.md → notes/usage.md} +11 -1
  20. package/src/core/router/boot.js +82 -0
  21. package/src/core/router/cascade.js +76 -0
  22. package/src/core/router/container.js +63 -72
  23. package/src/core/router/graph.js +144 -0
  24. package/src/core/router/index.js +12 -2
  25. package/src/core/router/intercept.js +26 -7
  26. package/src/core/router/lca.js +58 -0
  27. package/src/core/router/match.js +49 -36
  28. package/src/core/router/notes/audit-old.md +887 -0
  29. package/src/core/router/notes/audti.md +773 -0
  30. package/src/core/router/notes/tasks.md +473 -0
  31. package/src/core/router/{usage.md → notes/usage.md} +57 -35
  32. package/src/core/router/sync/tab.js +6 -4
  33. package/src/core/router/transitions.js +35 -8
  34. package/src/core/router/trie.js +130 -0
  35. package/src/core/security/{usage.md → notes/usage.md} +1 -2
  36. package/src/core/storage/{usage.md → notes/usage.md} +6 -6
  37. package/src/core/theme/index.js +78 -0
  38. package/src/core/ui/define/index.js +2 -1
  39. package/src/core/ui/define/orchestrator.js +10 -4
  40. package/src/core/ui/defs/dock.js +134 -0
  41. package/src/core/ui/defs/index.js +20 -0
  42. package/src/core/ui/defs/page.js +89 -0
  43. package/src/core/ui/defs/part.js +28 -0
  44. package/src/core/ui/defs/spec.js +96 -0
  45. package/src/core/ui/defs/view.js +23 -0
  46. package/src/core/ui/index.js +16 -3
  47. package/src/core/ui/notes/definations.md +979 -0
  48. package/src/tokens/index.css +1 -0
  49. package/src/tokens/semantic/contrast.css +18 -0
  50. package/src/tokens/semantic/transitions.css +32 -0
  51. package/types/core/platform/index.d.ts +39 -10
  52. package/types/core/router/index.d.ts +9 -0
  53. package/types/core/theme/index.d.ts +18 -0
  54. package/types/core/ui/index.d.ts +11 -0
  55. package/types/index.d.ts +1 -0
  56. package/bin/anza.js +0 -63
  57. package/bin/create.js +0 -150
  58. package/src/core/api/plan.md +0 -209
  59. package/src/core/events/missing.md +0 -103
  60. package/src/core/events/plan.md +0 -177
  61. package/src/core/offline/missing.md +0 -89
  62. package/src/core/offline/plan.md +0 -143
  63. package/src/core/platform/missing.md +0 -119
  64. package/src/core/platform/platform.d.ts +0 -88
  65. package/src/core/router/missing.md +0 -716
  66. package/src/core/router/outlet.js +0 -139
  67. package/src/core/router/plan.md +0 -370
  68. package/src/core/security/missing.md +0 -97
  69. package/src/core/state/missing.md +0 -165
  70. package/src/core/storage/missing.md +0 -165
  71. package/src/core/storage/plan.md +0 -69
  72. package/src/core/ui/implementation.md +0 -170
  73. package/src/core/ui/plan.md +0 -510
  74. package/src/core/ui/ui.types.md +0 -890
  75. /package/src/core/animations/{usage.md → notes/usage.md} +0 -0
  76. /package/src/core/api/{usage.md → notes/usage.md} +0 -0
  77. /package/src/core/events/{usage.md → notes/usage.md} +0 -0
  78. /package/src/core/platform/{usage.md → notes/usage.md} +0 -0
  79. /package/src/core/state/{usage.md → notes/usage.md} +0 -0
  80. /package/src/core/ui/{usage.md → notes/usage.md} +0 -0
  81. /package/src/core/ui/{watch.md → notes/watch.md} +0 -0
  82. /package/src/core/workers/{plan.md → notes/plan.md} +0 -0
  83. /package/src/core/workers/{usage.md → notes/usage.md} +0 -0
@@ -24,3 +24,4 @@
24
24
  @import './semantic/dark.css';
25
25
  @import './semantic/contrast.css';
26
26
  @import './semantic/components.css';
27
+ @import './semantic/transitions.css';
@@ -30,4 +30,22 @@
30
30
  --color-border-default: var(--color-neutral-1000);
31
31
  --color-border-strong: var(--color-neutral-1000);
32
32
  --color-border-focus: var(--color-brand-900);
33
+
34
+ /* Automatic theme morph — same timing as light theme */
35
+ transition:
36
+ --color-surface-page 220ms var(--ease-out),
37
+ --color-surface-card 220ms var(--ease-out),
38
+ --color-surface-elevated 220ms var(--ease-out),
39
+ --color-surface-inverse 220ms var(--ease-out),
40
+ --color-content-primary 180ms var(--ease-out),
41
+ --color-content-secondary 180ms var(--ease-out),
42
+ --color-content-disabled 180ms var(--ease-out),
43
+ --color-content-inverse 180ms var(--ease-out),
44
+ --color-content-link 180ms var(--ease-out),
45
+ --color-interactive 180ms var(--ease-out),
46
+ --color-interactive-hover 180ms var(--ease-out),
47
+ --color-interactive-active 180ms var(--ease-out),
48
+ --color-border-default 180ms var(--ease-out),
49
+ --color-border-strong 180ms var(--ease-out),
50
+ --color-border-focus 180ms var(--ease-out);
33
51
  }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * tokens/semantic/transitions.css
3
+ *
4
+ * View Transition pseudo-element theming.
5
+ * Connects the browser's CSS View Transitions API to the semantic token system.
6
+ */
7
+
8
+ :root {
9
+ /* Backdrop behind old and new page snapshots during a transition */
10
+ --transition-bg: var(--color-surface-page);
11
+
12
+ /* Timing tokens used by JS when calling startViewTransition */
13
+ --transition-duration: var(--duration-normal);
14
+ --transition-easing: var(--ease-out);
15
+
16
+ /* Directional modifiers — pushed pages feel different from popped pages */
17
+ --transition-push: var(--ease-out);
18
+ --transition-pop: var(--ease-in);
19
+ --transition-replace: var(--ease-in-out);
20
+ }
21
+
22
+ ::view-transition-old(root),
23
+ ::view-transition-new(root) {
24
+ background: var(--transition-bg);
25
+ }
26
+
27
+ @media (prefers-reduced-motion: reduce) {
28
+ :root {
29
+ --transition-duration: 0ms;
30
+ --transition-easing: linear;
31
+ }
32
+ }
@@ -4,57 +4,86 @@
4
4
  * TypeScript declarations for browser-native feature detection flags and platform guards.
5
5
  */
6
6
 
7
- export const supports: {
7
+ export interface Supports {
8
+ // --- Routing ---
8
9
  readonly navigationAPI: boolean;
9
10
  readonly urlPattern: boolean;
11
+
12
+ // --- Component Model ---
10
13
  readonly declarativeShadowDOM: boolean;
11
14
  readonly customStatePseudo: boolean;
12
15
  readonly formAssociated: boolean;
16
+
17
+ // --- Overlay / Popover ---
13
18
  readonly popoverAPI: boolean;
14
19
  readonly anchorPositioning: boolean;
20
+
21
+ // --- Animation ---
15
22
  readonly viewTransitions: boolean;
16
23
  readonly scrollTimeline: boolean;
17
24
  readonly viewTimeline: boolean;
25
+
26
+ // --- Scheduling ---
18
27
  readonly schedulerPostTask: boolean;
19
28
  readonly schedulerYield: boolean;
29
+
30
+ // --- CSS ---
20
31
  readonly contentVisibility: boolean;
21
32
  readonly cssScope: boolean;
22
33
  readonly cssLayer: boolean;
23
34
  readonly cssModuleScripts: boolean;
35
+
36
+ // --- Module System ---
24
37
  readonly importMaps: boolean;
38
+
39
+ // --- Security ---
25
40
  readonly sanitizerAPI: boolean;
26
41
  readonly trustedTypes: boolean;
27
42
  readonly subtleCrypto: boolean;
43
+
44
+ // --- Storage ---
28
45
  readonly opfs: boolean;
29
46
  readonly storageManager: boolean;
30
47
  readonly fileSystemPickers: boolean;
31
48
  readonly compression: boolean;
32
49
  readonly storagePersistence: boolean;
50
+
51
+ // --- Networking / Workers ---
33
52
  readonly backgroundSync: boolean;
34
53
  readonly speculationRules: boolean;
35
54
  readonly sharedWorker: boolean;
36
55
  readonly webLocks: boolean;
37
56
  readonly offscreenCanvas: boolean;
57
+
58
+ // --- Notifications / Push ---
38
59
  readonly pushAPI: boolean;
39
60
  readonly notificationsAPI: boolean;
61
+
62
+ // --- Device ---
40
63
  readonly screenWakeLock: boolean;
41
64
  readonly idleDetection: boolean;
42
65
  readonly webAuthn: boolean;
43
- };
66
+ }
44
67
 
45
- export function reset(key: keyof typeof supports): void;
68
+ export const supports: Supports;
69
+
70
+ export function reset(key: keyof Supports): void;
71
+
72
+ export function typeGuard(key: keyof Supports, message?: string): void;
46
73
 
47
74
  export interface SanitizerWrapper {
48
75
  sanitizeToString(input: string): string;
49
76
  }
50
77
 
51
- export const guard: {
52
- urlPattern(): Promise<any>;
53
- navigation(): Promise<any>;
78
+ export interface Guard {
79
+ urlPattern(): Promise<typeof URLPattern>;
80
+ navigation(): Promise<Navigation>;
54
81
  popover(): Promise<void>;
55
- shadow(root?: ParentNode): Promise<void>;
56
- anchor(floating: HTMLElement, anchorEl: HTMLElement, options?: any): Promise<void>;
82
+ shadow(root?: Document | ShadowRoot): Promise<void>;
83
+ anchor(floating: HTMLElement, anchorEl: HTMLElement, options?: object): Promise<void>;
57
84
  sanitizer(): Promise<SanitizerWrapper>;
58
- scheduler(): Promise<any>;
85
+ scheduler(): Promise<Scheduler>;
59
86
  yield(): Promise<void>;
60
- };
87
+ }
88
+
89
+ export const guard: Guard;
@@ -192,6 +192,15 @@ export function unregisterContainer(name: string): void;
192
192
  export function getContainer(name: string): Element | null;
193
193
  export function clearContainers(): void;
194
194
 
195
+ export interface TransitionsApi {
196
+ run(
197
+ updateDOM: () => void | Promise<void>,
198
+ options?: { sourceElement?: HTMLElement; name?: string }
199
+ ): Promise<void>;
200
+ }
201
+
202
+ export const transitions: TransitionsApi;
203
+
195
204
  export class RouteOutlet extends HTMLElement {
196
205
  update(detail: { chain: ChainSegment[]; query: Record<string, string>; hash: string }): Promise<void>;
197
206
  }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * types/core/theme/index.d.ts
3
+ *
4
+ * TypeScript declarations for the anza theme API.
5
+ */
6
+
7
+ export interface ThemeApi {
8
+ /** Return the active theme name: light, dark, contrast, or auto. */
9
+ get(): string;
10
+
11
+ /** Apply a theme name and persist it. */
12
+ set(name: string): void;
13
+
14
+ /** Toggle between light and dark. */
15
+ toggle(): void;
16
+ }
17
+
18
+ export const theme: ThemeApi;
@@ -420,6 +420,16 @@ export function scheduleFrame<T>(fn: () => T): Promise<T>;
420
420
 
421
421
  export function yieldTask(): Promise<void>;
422
422
 
423
+ export function dock(
424
+ name: string,
425
+ config?: {
426
+ tag?: string;
427
+ parent?: string;
428
+ template?: { html?: string; css?: string; shadow?: boolean };
429
+ },
430
+ base?: string | URL
431
+ ): void;
432
+
423
433
  export function transition<T>(
424
434
  fn: () => T
425
435
  ): Promise<ViewTransitionLike<T>>;
@@ -435,6 +445,7 @@ export interface UiApi {
435
445
  define: typeof define;
436
446
  element: typeof element;
437
447
  container: typeof container;
448
+ dock: typeof dock;
438
449
  schedule: typeof schedule;
439
450
  scheduleFrame: typeof scheduleFrame;
440
451
  yield: typeof yieldTask;
package/types/index.d.ts CHANGED
@@ -15,4 +15,5 @@ export * as ui from './core/ui/index.d.ts';
15
15
  export * as security from './core/security/index.d.ts';
16
16
  export * as offline from './core/offline/index.d.ts';
17
17
  export * as animations from './core/animations/index.d.ts';
18
+ export * as theme from './core/theme/index.d.ts';
18
19
  export * as elements from './elements/index.d.ts';
package/bin/anza.js DELETED
@@ -1,63 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * library/bin/anza.js
5
- *
6
- * Node.js wrapper for the anza Rust binary.
7
- * Detects the platform and spawns the correct pre-built binary.
8
- *
9
- * In development: uses tools/target/release/anza (two levels up).
10
- * In npm install: uses pre-built platform binary from bin/.
11
- */
12
-
13
- import { fileURLToPath } from 'url';
14
- import { dirname, join } from 'path';
15
- import { existsSync } from 'fs';
16
- import { spawn } from 'child_process';
17
-
18
- const __filename = fileURLToPath(import.meta.url);
19
- const __dirname = dirname(__filename);
20
-
21
- // library/bin/ → library/ → repo root
22
- const library = join(__dirname, '..');
23
- const root = join(library, '..');
24
-
25
- function binary() {
26
- // Development: repo binary (built by `node scripts/build.js`)
27
- const dev = join(root, 'tools', 'target', 'release', 'anza');
28
- if (existsSync(dev)) return dev;
29
-
30
- // npm install: pre-built platform binary alongside this script
31
- const platform = process.platform;
32
- const arch = process.arch;
33
-
34
- const map = {
35
- darwin: { x64: 'anza-macos-x64', arm64: 'anza-macos-arm64' },
36
- linux: { x64: 'anza-linux-x64', arm64: 'anza-linux-arm64' },
37
- win32: { x64: 'anza-windows-x64.exe', arm64: 'anza-windows-arm64.exe' },
38
- };
39
-
40
- const name = map[platform]?.[arch];
41
- if (!name) {
42
- console.error(`Unsupported platform: ${platform} ${arch}`);
43
- process.exit(1);
44
- }
45
-
46
- const prebuilt = join(__dirname, name);
47
- if (existsSync(prebuilt)) return prebuilt;
48
-
49
- console.error(
50
- 'anza binary not found.\n' +
51
- 'Run: node scripts/build.js (from the repo root)'
52
- );
53
- process.exit(1);
54
- }
55
-
56
- const child = spawn(binary(), process.argv.slice(2), { stdio: 'inherit' });
57
-
58
- child.on('error', (err) => {
59
- console.error('Failed to spawn anza:', err.message);
60
- process.exit(1);
61
- });
62
-
63
- child.on('exit', (code) => process.exit(code ?? 0));
package/bin/create.js DELETED
@@ -1,150 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * scripts/create.js (also used as library/bin/create.js → npx anza-create)
4
- *
5
- * Scaffolds a new anza app project in the given directory.
6
- * Usage:
7
- * npx anza-create myapp
8
- * npx anza-create . (current directory)
9
- */
10
-
11
- import { mkdirSync, writeFileSync, existsSync } from 'fs';
12
- import { join, resolve, basename } from 'path';
13
-
14
- const arg = process.argv[2];
15
-
16
- if (!arg || arg === '--help' || arg === '-h') {
17
- console.log('Usage: npx anza-create <name>');
18
- console.log(' npx anza-create . (scaffold in current dir)');
19
- process.exit(arg ? 0 : 1);
20
- }
21
-
22
- const target = resolve(arg);
23
- const name = arg === '.' ? basename(process.cwd()) : basename(target);
24
-
25
- if (existsSync(target) && arg !== '.') {
26
- console.error(`Error: '${target}' already exists.`);
27
- process.exit(1);
28
- }
29
-
30
- console.log(`Scaffolding anza app: ${name}\n`);
31
-
32
- // Directory structure
33
- const dirs = [
34
- '',
35
- 'src',
36
- 'src/elements',
37
- 'src/styles',
38
- 'src/tokens',
39
- ];
40
-
41
- for (const dir of dirs) {
42
- mkdirSync(join(target, dir), { recursive: true });
43
- }
44
-
45
- // src/index.html
46
- writeFileSync(join(target, 'src', 'index.html'), `<!DOCTYPE html>
47
- <html lang="en">
48
- <head>
49
- <meta charset="utf-8" />
50
- <meta name="viewport" content="width=device-width, initial-scale=1" />
51
- <title>${name}</title>
52
-
53
- <!-- Tokens & global styles -->
54
- <link rel="stylesheet" href="/dist/tokens/index.css" />
55
- <link rel="stylesheet" href="/dist/styles/global.css" />
56
-
57
- <!-- App entry -->
58
- <script type="module" src="/dist/index.js"></script>
59
- </head>
60
- <body>
61
- <!-- App root -->
62
- </body>
63
- </html>
64
- `);
65
-
66
- // src/index.js
67
- writeFileSync(join(target, 'src', 'index.js'), `/**
68
- * src/index.js — app entry point
69
- */
70
- import '@adukiorg/anza/ui';
71
-
72
- // Import your elements
73
- // import './elements/dashboard/index.js';
74
- `);
75
-
76
- // src/styles/global.css
77
- writeFileSync(join(target, 'src', 'styles', 'global.css'), `/* global.css — global app styles */
78
-
79
- *,
80
- *::before,
81
- *::after {
82
- box-sizing: border-box;
83
- margin: 0;
84
- padding: 0;
85
- }
86
-
87
- body {
88
- font-family: system-ui, sans-serif;
89
- line-height: 1.5;
90
- }
91
- `);
92
-
93
- // src/tokens/index.css
94
- writeFileSync(join(target, 'src', 'tokens', 'index.css'), `/* tokens/index.css — design tokens */
95
-
96
- :root {
97
- /* Colors */
98
- --color-primary: #6366f1;
99
- --color-surface: #ffffff;
100
- --color-text: #111827;
101
-
102
- /* Spacing */
103
- --space-1: 0.25rem;
104
- --space-2: 0.5rem;
105
- --space-4: 1rem;
106
- --space-8: 2rem;
107
-
108
- /* Radius */
109
- --radius-sm: 0.25rem;
110
- --radius-md: 0.5rem;
111
- --radius-lg: 1rem;
112
-
113
- /* Shadow */
114
- --shadow-sm: 0 1px 2px rgb(0 0 0 / 0.05);
115
- --shadow-md: 0 4px 6px rgb(0 0 0 / 0.07);
116
- }
117
- `);
118
-
119
- // package.json
120
- writeFileSync(join(target, 'package.json'), JSON.stringify({
121
- name,
122
- version: '0.1.0',
123
- private: true,
124
- type: 'module',
125
- scripts: {
126
- dev: 'anza dev',
127
- build: 'anza build',
128
- },
129
- devDependencies: {
130
- '@adukiorg/anza': 'latest',
131
- }
132
- }, null, 2));
133
-
134
- // .gitignore
135
- writeFileSync(join(target, '.gitignore'), `node_modules/
136
- dist/
137
- .anza-build-cache.json
138
- `);
139
-
140
- console.log('Created:');
141
- for (const dir of dirs.filter(Boolean)) {
142
- console.log(` ${name}/${dir}/`);
143
- }
144
- console.log(` ${name}/src/index.html`);
145
- console.log(` ${name}/src/index.js`);
146
- console.log(` ${name}/package.json`);
147
- console.log(`\nNext steps:`);
148
- console.log(` cd ${arg === '.' ? '.' : name}`);
149
- console.log(` npm install`);
150
- console.log(` npm run dev`);
@@ -1,209 +0,0 @@
1
- # API Client & Cache Enhancement Plan
2
-
3
- This document outlines the blueprint for enhancing the `core.api` networking client with localized, fine-grained caching, prefix registration, namespace-level invalidation, and custom network/status-code events.
4
-
5
- ---
6
-
7
- ## 1. Architectural Architecture & Requirements
8
-
9
- We will extend `core.api` to provide:
10
-
11
- 1. **Default Zero-Cache Policy:** All calls default to no cache, running directly against the network.
12
- 2. **TTL/Expiry-based API Caching:** Active caching when `expiry` or `ttl` (Time-To-Live) options are provided. Hits cache first, falls back to the network on a miss, and caches successful responses.
13
- 3. **Namespace-level Glob Invalidation:** Support for clearing the entire cache, a single URL, or a glob namespace pattern (e.g. `/user/*`).
14
- 4. **Outbound Prefix Resolution:** Initializing base prefixes once (optionally) to cleanly rewrite and resolve endpoint URLs.
15
- 5. **Network Event Hub:** An integrated event listener system firing on generic network failures (errors, timeouts) or specific responses (status codes like `401`, `500`, or content types like `json`, `text`).
16
-
17
- ---
18
-
19
- ## 2. Structural & Folder Layout
20
-
21
- In accordance with our naming and folder conventions (`RULE[user_global]`), we will group caching and events into highly structured subfolders under `src/core/api/`:
22
-
23
- ```
24
- src/core/api/
25
- ├── caches/ # Subfolder for caching adapters & glob matching
26
- │ ├── glob.js # Glob/Namespace pattern matching helper
27
- │ └── index.js # Unified local API cache client
28
- ├── events/ # Subfolder for network telemetry events
29
- │ └── index.js # API event emitter implementation
30
- ├── prefixes/ # Subfolder for prefix/base URL resolving
31
- │ └── index.js # Prefix store and path normalization
32
- ├── index.js # Core API client entry point
33
- ├── fetch.js # Network request executor
34
- ├── pipeline.js # Inbound/Outbound pipeline coordinator
35
- ├── retry.js # Exponential backoff retry handler
36
- ├── stream.js # Streams and NDJSON transform pipeline
37
- └── upload.js # XMLHttpRequest-based upload gateway
38
- ```
39
-
40
- ---
41
-
42
- ## 3. Technical Blueprint & Interface Design
43
-
44
- ### Caches & Glob Purging (`src/core/api/caches/`)
45
-
46
- The API cache uses the native browser Cache API with custom header tags to track record creation times and TTL.
47
-
48
- ```javascript
49
- // src/core/api/caches/index.js
50
- export class ApiCache {
51
- constructor(name = 'platform-api-cache') {
52
- this.name = name;
53
- }
54
-
55
- async get(url) {
56
- if (typeof caches === 'undefined') return null;
57
- const store = await caches.open(this.name);
58
- const cached = await store.match(url);
59
- if (!cached) return null;
60
-
61
- const expiresAt = cached.headers.get('x-expires-at');
62
- if (expiresAt && Date.now() > Number(expiresAt)) {
63
- await store.delete(url);
64
- return null;
65
- }
66
- return cached.clone();
67
- }
68
-
69
- async set(url, response, ttlMs) {
70
- if (typeof caches === 'undefined') return;
71
- const store = await caches.open(this.name);
72
- const headers = new Headers(response.headers);
73
- headers.set('x-expires-at', String(Date.now() + ttlMs));
74
-
75
- const cloned = new Response(response.body ? response.clone().body : null, {
76
- status: response.status,
77
- statusText: response.statusText,
78
- headers
79
- });
80
- await store.put(url, cloned);
81
- }
82
-
83
- async delete(pattern) {
84
- if (typeof caches === 'undefined') return;
85
- const store = await caches.open(this.name);
86
-
87
- if (pattern.includes('*')) {
88
- const regex = globToRegex(pattern);
89
- const keys = await store.keys();
90
- for (const req of keys) {
91
- if (regex.test(req.url) || regex.test(new URL(req.url).pathname)) {
92
- await store.delete(req);
93
- }
94
- }
95
- } else {
96
- await store.delete(pattern);
97
- }
98
- }
99
-
100
- async clear() {
101
- if (typeof caches === 'undefined') return;
102
- await caches.delete(this.name);
103
- }
104
- }
105
- ```
106
-
107
- Glob to Regex conversion helper:
108
-
109
- ```javascript
110
- // src/core/api/caches/glob.js
111
- export function globToRegex(pattern) {
112
- const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&');
113
- const wildcarded = escaped.replace(/\*/g, '.*');
114
- return new RegExp(`^${wildcarded}$`);
115
- }
116
- ```
117
-
118
- ### Prefix Resolver (`src/core/api/prefixes/`)
119
-
120
- ```javascript
121
- // src/core/api/prefixes/index.js
122
- export class PrefixRegistry {
123
- #prefixes = new Map();
124
-
125
- add(name, value) {
126
- this.#prefixes.set(name, value);
127
- }
128
-
129
- resolve(url) {
130
- if (url.startsWith('http://') || url.startsWith('https://')) {
131
- return url;
132
- }
133
- for (const [prefix, base] of this.#prefixes.entries()) {
134
- if (url.startsWith(`/${prefix}/`)) {
135
- return base + url.slice(prefix.length + 1);
136
- }
137
- if (url.startsWith(`${prefix}/`)) {
138
- return base + '/' + url.slice(prefix.length);
139
- }
140
- }
141
- // Default fallback to registered root or baseline domain
142
- const root = this.#prefixes.get('root') || this.#prefixes.get('default');
143
- if (root) {
144
- return root + (url.startsWith('/') ? url : '/' + url);
145
- }
146
- return url;
147
- }
148
- }
149
- ```
150
-
151
- ### Telemetry Events (`src/core/api/events/`)
152
-
153
- ```javascript
154
- // src/core/api/events/index.js
155
- export class ApiEventEmitter {
156
- #listeners = new Map();
157
-
158
- on(event, handler, signal) {
159
- if (signal?.aborted) return () => {};
160
- if (!this.#listeners.has(event)) {
161
- this.#listeners.set(event, new Set());
162
- }
163
- const listener = { handler };
164
- this.#listeners.get(event).add(listener);
165
-
166
- const dispose = () => {
167
- const set = this.#listeners.get(event);
168
- if (set) {
169
- set.delete(listener);
170
- if (set.size === 0) this.#listeners.delete(event);
171
- }
172
- };
173
-
174
- if (signal) {
175
- signal.addEventListener('abort', dispose);
176
- }
177
- return dispose;
178
- }
179
-
180
- emit(event, detail) {
181
- const set = this.#listeners.get(event);
182
- if (!set) return;
183
- const customEvent = { type: event, detail };
184
- for (const listener of [...set]) {
185
- try {
186
- listener.handler(customEvent);
187
- } catch (err) {
188
- console.error(`Error in API event listener for "${event}":`, err);
189
- }
190
- }
191
- }
192
- }
193
- ```
194
-
195
- ---
196
-
197
- ## 4. Verification Plan
198
-
199
- ### Automated Verification
200
-
201
- - Write comprehensive test coverage validating:
202
- - Cache hits on active TTL/Expiry options.
203
- - Glob namespace clearing matches (e.g. `/user/*` successfully purges `/user/profile` and `/user/settings`).
204
- - Outbound path prefix expansion.
205
- - Correct event dispatching for generic `'error'`, `'timeout'`, specific codes (`status:401`), and content types (`type:json`).
206
-
207
- ### Integration Check
208
-
209
- - Assert network trace outputs and test page lifecycle triggers inside the browser environment.