@adukiorg/anza 0.4.1 → 0.4.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 (43) hide show
  1. package/bin/anza/anza-linux-arm64 +0 -0
  2. package/bin/anza/anza-linux-x64 +0 -0
  3. package/bin/anza/anza-macos-arm64 +0 -0
  4. package/bin/anza/anza-macos-x64 +0 -0
  5. package/bin/anza/anza-windows-x64.exe +0 -0
  6. package/package.json +1 -1
  7. package/src/core/api/index.js +2 -2
  8. package/src/core/offline/sync.js +2 -2
  9. package/src/core/router/cascade.js +23 -18
  10. package/src/core/router/container.js +6 -2
  11. package/src/core/router/graph.js +46 -15
  12. package/src/core/router/index.js +7 -1
  13. package/src/core/router/intercept.js +397 -100
  14. package/src/core/router/match.js +30 -0
  15. package/src/core/router/transitions.js +4 -3
  16. package/src/core/ui/define/container.js +3 -13
  17. package/src/core/ui/define/element.js +58 -8
  18. package/src/core/ui/define/orchestrator.js +8 -4
  19. package/src/core/ui/defs/dock.js +27 -14
  20. package/src/core/ui/defs/page.js +75 -29
  21. package/src/core/ui/defs/spec.js +81 -11
  22. package/src/elements/data/list/style.css +1 -1
  23. package/src/elements/data/table/index.js +2 -1
  24. package/src/elements/data/table/style.css +1 -1
  25. package/src/elements/feedback/alert/style.css +1 -1
  26. package/src/styles/base.css +22 -21
  27. package/src/styles/reset.css +0 -2
  28. package/src/tokens/index.css +1 -4
  29. package/src/tokens/primitives/colors.css +9 -64
  30. package/src/tokens/primitives/motion.css +6 -18
  31. package/src/tokens/primitives/spacing.css +7 -12
  32. package/src/tokens/primitives/typography.css +12 -42
  33. package/src/tokens/registered/colors.css +5 -96
  34. package/src/tokens/registered/dimensions.css +6 -19
  35. package/src/tokens/semantic/contrast.css +8 -36
  36. package/src/tokens/semantic/dark.css +13 -48
  37. package/src/tokens/semantic/light.css +13 -44
  38. package/src/tokens/semantic/transitions.css +26 -13
  39. package/CHANGELOG.md +0 -360
  40. package/src/tokens/primitives/radius.css +0 -16
  41. package/src/tokens/primitives/shadow.css +0 -34
  42. package/src/tokens/primitives/zindex.css +0 -18
  43. package/src/tokens/semantic/components.css +0 -123
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adukiorg/anza",
3
- "version": "0.4.1",
3
+ "version": "0.4.3",
4
4
  "description": "Anza web platform library — reactive state, networking, offline, animations, custom elements. Instant build step. Pure browser ESM.",
5
5
  "author": "fescii",
6
6
  "license": "MIT",
@@ -20,7 +20,7 @@ import { cache as apiCache } from './caches/index.js';
20
20
 
21
21
  let cache = null;
22
22
 
23
- export function sync() {
23
+ function sync() {
24
24
  if (typeof document === 'undefined') return;
25
25
  const el = document.getElementById('anza-state');
26
26
  if (el) {
@@ -156,7 +156,7 @@ async function request(url, method, body, opts = {}) {
156
156
 
157
157
  export const api = {
158
158
  get: (url, opts) => request(url, 'GET', null, opts),
159
- state: () => cache ? JSON.parse(JSON.stringify(cache.__route || {})) : null,
159
+ state: () => cache ? JSON.parse(JSON.stringify(cache.__route || {})) : {},
160
160
  sync,
161
161
  post: (url, body, opts) => request(url, 'POST', body, opts),
162
162
  put: (url, body, opts) => request(url, 'PUT', body, opts),
@@ -15,7 +15,7 @@ export class SyncManager {
15
15
  constructor() {
16
16
  if (
17
17
  typeof navigator !== 'undefined' &&
18
- 'serviceWorker' in navigator
18
+ navigator.serviceWorker
19
19
  ) {
20
20
  navigator.serviceWorker.addEventListener('message', async (event) => {
21
21
  const data = event.data;
@@ -38,7 +38,7 @@ export class SyncManager {
38
38
  async register(tag) {
39
39
  if (
40
40
  typeof navigator !== 'undefined' &&
41
- 'serviceWorker' in navigator
41
+ navigator.serviceWorker
42
42
  ) {
43
43
  try {
44
44
  const registration = await navigator.serviceWorker.ready;
@@ -32,7 +32,16 @@ export async function ensure(target, current = 'main') {
32
32
  if (live) return live;
33
33
 
34
34
  const segments = path(current, target);
35
- if (!segments) throw new Error(`CascadeError: no path '${current}' → '${target}'`);
35
+ if (!segments) {
36
+ // The container graph has no registered path from `current` to `target`.
37
+ // This almost always means a dock() declaration is missing or was imported
38
+ // after the page() that lists it in `via`.
39
+ throw new Error(
40
+ `[Router] CascadeError: cannot mount '${target}' — no graph path from '${current}'.\n` +
41
+ `Ensure dock('${target}', { parent: '${current}' }) is called and its file is imported\n` +
42
+ `before any page() that lists '${target}' in its via chain.`
43
+ );
44
+ }
36
45
 
37
46
  // Find the deepest node on the path that is currently connected.
38
47
  let mounted = null;
@@ -47,26 +56,22 @@ export async function ensure(target, current = 'main') {
47
56
  const start = segments.indexOf(mounted) + 1;
48
57
  for (let i = start; i < segments.length; i++) {
49
58
  const node = segments[i];
59
+ if (resolve(node.name)) continue;
50
60
  const parentEl = node.parent?.ref?.deref() ?? null;
51
- if (!parentEl || !parentEl.isConnected) {
52
- throw new Error(`CascadeError: parent '${node.parent?.name}' is disconnected while mounting '${node.name}'`);
53
- }
54
-
55
- const tag = node.name;
56
- if (tag.includes('-') && typeof customElements !== 'undefined' && !customElements.get(tag)) {
57
- await customElements.whenDefined(tag);
58
- }
59
-
61
+ if (!parentEl || !parentEl.isConnected) throw new Error(`CascadeError: parent '${node.parent?.name}' is disconnected while mounting '${node.name}'`);
62
+ const tag = node.tag;
63
+ if (tag.includes('-') && typeof customElements !== 'undefined' && !customElements.get(tag)) await customElements.whenDefined(tag);
60
64
  const el = document.createElement(tag);
61
- if (typeof parentEl.swap === 'function') {
62
- await parentEl.swap(el, { direction: 'push' });
63
- } else {
64
- parentEl.replaceChildren(el);
65
- }
66
-
67
- // Yield so connectedCallback fires and the dock self-registers in the graph.
68
- await frame();
65
+ parentEl.replaceChildren(el);
69
66
 
67
+ // Wait for the element's connectedCallback to complete, including any
68
+ // async resource loading (template/style fetches). A single frame is
69
+ // not sufficient when preloadResources() hits the network.
70
+ let attempts = 0;
71
+ while (!resolve(node.name) && attempts < 50) {
72
+ await frame();
73
+ attempts++;
74
+ }
70
75
  if (!resolve(node.name)) {
71
76
  throw new Error(`CascadeError: container '${node.name}' failed to register after mount`);
72
77
  }
@@ -62,8 +62,8 @@ function ensureObserver() {
62
62
  * @param {string|null} [parent='main'] - parent container key in the graph.
63
63
  * Pass null explicitly when registering the root (no parent).
64
64
  */
65
- export function registerContainer(name, element, parent = 'main') {
66
- add(name, element, parent);
65
+ export function registerContainer(name, element, parent = 'main', tag = null) {
66
+ add(name, element, parent, tag);
67
67
  }
68
68
 
69
69
  /**
@@ -76,6 +76,10 @@ export function unregisterContainer(name, element) {
76
76
  remove(name, element);
77
77
  }
78
78
 
79
+ export function hasContainer(name) {
80
+ return get(name) !== null;
81
+ }
82
+
79
83
  /**
80
84
  * Retrieves an active layout container by name.
81
85
  *
@@ -14,8 +14,9 @@
14
14
  */
15
15
 
16
16
  class Node {
17
- constructor(name, ref, parent) {
17
+ constructor(name, tag, ref, parent) {
18
18
  this.name = name; // registry key, e.g. 'main' | 'sidebar'
19
+ this.tag = tag; // custom element tag, e.g. 'dock-docs' | 'main'
19
20
  this.ref = ref; // WeakRef<Element> | null (null for virtual root)
20
21
  this.parent = parent; // Node | null
21
22
  this.children = new Set(); // Set<Node>
@@ -33,7 +34,7 @@ const nodes = new Map();
33
34
  // The permanent root node. ref is null at module-evaluation time because
34
35
  // modules may be evaluated before the parser reaches <main id="main">.
35
36
  // boot.js fills in the WeakRef during anchor().
36
- const root = new Node('main', null, null);
37
+ const root = new Node('main', 'main', null, null);
37
38
  root.depth = 0;
38
39
  nodes.set('main', root);
39
40
 
@@ -41,15 +42,23 @@ nodes.set('main', root);
41
42
  // so a just-unmounted container is not confused with a GC'd one (RT bug 8.2).
42
43
  const gone = new Set();
43
44
 
44
- // Prune stale nodes after their element is collected.
45
+ // Prune stale nodes after their element is collected by GC.
46
+ // This is the ONLY place a node is structurally removed from the graph.
47
+ // Normal DOM disconnects (remove()) only null the ref — they preserve topology.
45
48
  const finalizer = typeof FinalizationRegistry !== 'undefined'
46
49
  ? new FinalizationRegistry((name) => {
47
50
  const node = nodes.get(name);
51
+ // Only detach if the node still has no live element (i.e. truly GC'd,
52
+ // not just remounted under a new element instance).
48
53
  if (node && !node.alive()) detach(name);
49
54
  })
50
55
  : { register() {}, unregister() {} };
51
56
 
52
- /** Removes a node from the tree, reparenting its children to its parent. */
57
+ /**
58
+ * Structurally removes a node from the tree, reparenting its children to its
59
+ * parent. Called ONLY from the FinalizationRegistry (GC path). DOM disconnects
60
+ * must NOT call this — they use remove() which only nulls the ref.
61
+ */
53
62
  function detach(name) {
54
63
  const node = nodes.get(name);
55
64
  if (!node || node === root) return;
@@ -68,9 +77,10 @@ function detach(name) {
68
77
  * @param {string} name - unique registry key.
69
78
  * @param {Element} el - the container element.
70
79
  * @param {string} [parent='main'] - parent registry key.
80
+ * @param {string} [tag] - custom element tag; defaults to name.
71
81
  * @returns {Node} the inserted node.
72
82
  */
73
- export function add(name, el, parent = 'main') {
83
+ export function add(name, el, parent = 'main', tag = null) {
74
84
  gone.delete(name);
75
85
 
76
86
  const existing = nodes.get(name);
@@ -78,45 +88,66 @@ export function add(name, el, parent = 'main') {
78
88
  if (existing === root) {
79
89
  // Root re-registration from anchor(): refresh the WeakRef and return.
80
90
  // The root cannot be reparented and must not spawn a duplicate node.
81
- existing.ref = new WeakRef(el);
91
+ if (el) existing.ref = new WeakRef(el);
82
92
  return existing;
83
93
  }
84
94
  const prev = existing.ref?.deref();
85
- if (prev && prev !== el) {
95
+ if (prev && el && prev !== el) {
86
96
  throw new Error(`ContainerError: Singleton violation — '${name}' is already mounted. A second instance cannot register while the first is active.`);
87
97
  }
88
- // Same element re-registering (e.g. HMR): refresh the ref and return.
89
- existing.ref = new WeakRef(el);
98
+ // Re-registering after a disconnect (ref was null) or HMR: refresh the ref.
99
+ // Also re-register with the finalizer so the new element instance is tracked.
100
+ if (el) {
101
+ existing.ref = new WeakRef(el);
102
+ finalizer.register(el, name);
103
+ }
90
104
  return existing;
91
105
  }
92
106
 
93
107
  const parentNode = nodes.get(parent) ?? root;
94
- const node = new Node(name, new WeakRef(el), parentNode);
108
+ const resolvedTag = tag || name;
109
+ const node = new Node(name, resolvedTag, el ? new WeakRef(el) : null, parentNode);
95
110
  parentNode.children.add(node);
96
111
  nodes.set(name, node);
97
- finalizer.register(el, name);
112
+ if (el) finalizer.register(el, name);
98
113
  return node;
99
114
  }
100
115
 
101
116
  /**
102
- * Removes a container node. Marks the name as explicitly gone for one macrotask
103
- * so a lookup during teardown does not fall back to a stale resolution.
117
+ * Unregisters the live element of a container node when the element disconnects
118
+ * from the DOM. The node itself stays in `nodes` to preserve graph topology for
119
+ * future ensure()/lca() calls — only its element ref is cleared.
120
+ *
121
+ * The name is marked `gone` for one macrotask so any lookup that races the
122
+ * disconnect (e.g. an in-flight navigation reading the old element) gets
123
+ * `undefined` rather than the stale ref.
124
+ *
125
+ * Structural removal (nodes.delete) is deferred to the FinalizationRegistry so
126
+ * it only happens after the element is truly GC'd and cannot be remounted.
104
127
  *
105
128
  * @param {string} name - registry key.
106
- * @param {Element} [el] - element guard; if given, only removes when it matches.
129
+ * @param {Element} [el] - element guard; if given, only clears when it matches.
107
130
  */
108
131
  export function remove(name, el) {
109
132
  const node = nodes.get(name);
110
133
  if (!node || node === root) return;
111
134
 
112
135
  const current = node.ref?.deref();
136
+ // Guard: if a specific element is given and it doesn't match the registered
137
+ // one, this is a stale disconnect from a previous instance — ignore.
113
138
  if (el && current && current !== el) return;
114
139
 
140
+ // Unregister from the finalizer (we're clearing the ref manually).
115
141
  if (current) {
116
142
  try { finalizer.unregister(current); } catch (_) {}
117
143
  }
118
- detach(name);
119
144
 
145
+ // Clear the element ref but KEEP the node in the graph.
146
+ // This preserves topology: lca() and path() can still traverse the tree
147
+ // on the next navigation that needs to remount this container.
148
+ node.ref = null;
149
+
150
+ // Mark as transiently gone for one macrotask.
120
151
  gone.add(name);
121
152
  if (typeof setTimeout !== 'undefined') {
122
153
  setTimeout(() => gone.delete(name), 0);
@@ -12,6 +12,7 @@ import { register, match, clear, getRoutes, load } from './match.js';
12
12
  import {
13
13
  setup, destroy,
14
14
  addGuard, setNotFound,
15
+ addTransformer,
15
16
  guardsApi, missApi,
16
17
  on, nav, registerNavigator,
17
18
  getShell, getWin
@@ -44,7 +45,8 @@ import {
44
45
  registerContainer,
45
46
  unregisterContainer,
46
47
  getContainer,
47
- clearContainers
48
+ clearContainers,
49
+ hasContainer
48
50
  } from './container.js';
49
51
 
50
52
  import { cache, prefetch } from './cache.js';
@@ -56,6 +58,7 @@ export const router = {
56
58
  clear,
57
59
  guard: addGuard,
58
60
  notFound: setNotFound,
61
+ transform: addTransformer,
59
62
 
60
63
  // Grouped APIs
61
64
  guards: guardsApi,
@@ -101,6 +104,7 @@ export const router = {
101
104
  unregisterContainer,
102
105
  getContainer,
103
106
  clearContainers,
107
+ hasContainer,
104
108
 
105
109
  // Lifecycle
106
110
  setup,
@@ -151,6 +155,7 @@ export {
151
155
  // intercept sub-module
152
156
  addGuard,
153
157
  setNotFound,
158
+ addTransformer as transform,
154
159
  setup,
155
160
  destroy,
156
161
  on,
@@ -168,6 +173,7 @@ export {
168
173
  unregisterContainer,
169
174
  getContainer,
170
175
  clearContainers,
176
+ hasContainer,
171
177
  // cache sub-module
172
178
  cache,
173
179
  prefetch