@adukiorg/anza 0.4.0 → 0.4.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.
Files changed (41) 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 +15 -20
  10. package/src/core/router/container.js +4 -0
  11. package/src/core/router/graph.js +40 -12
  12. package/src/core/router/index.js +7 -1
  13. package/src/core/router/intercept.js +400 -100
  14. package/src/core/router/match.js +30 -0
  15. package/src/core/ui/define/element.js +64 -11
  16. package/src/core/ui/define/orchestrator.js +8 -4
  17. package/src/core/ui/define/utils.js +13 -7
  18. package/src/core/ui/defs/dock.js +18 -1
  19. package/src/core/ui/defs/page.js +75 -29
  20. package/src/core/ui/defs/spec.js +81 -11
  21. package/src/elements/data/list/style.css +1 -1
  22. package/src/elements/data/table/index.js +2 -1
  23. package/src/elements/data/table/style.css +1 -1
  24. package/src/elements/feedback/alert/style.css +1 -1
  25. package/src/styles/base.css +22 -21
  26. package/src/tokens/index.css +1 -4
  27. package/src/tokens/primitives/colors.css +9 -64
  28. package/src/tokens/primitives/motion.css +6 -18
  29. package/src/tokens/primitives/spacing.css +7 -12
  30. package/src/tokens/primitives/typography.css +12 -42
  31. package/src/tokens/registered/colors.css +5 -96
  32. package/src/tokens/registered/dimensions.css +6 -19
  33. package/src/tokens/semantic/contrast.css +8 -36
  34. package/src/tokens/semantic/dark.css +13 -48
  35. package/src/tokens/semantic/light.css +13 -44
  36. package/src/tokens/semantic/transitions.css +22 -12
  37. package/CHANGELOG.md +0 -352
  38. package/src/tokens/primitives/radius.css +0 -16
  39. package/src/tokens/primitives/shadow.css +0 -34
  40. package/src/tokens/primitives/zindex.css +0 -18
  41. 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.0",
3
+ "version": "0.4.2",
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,29 +56,15 @@ 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
-
61
+ if (!parentEl || !parentEl.isConnected) throw new Error(`CascadeError: parent '${node.parent?.name}' is disconnected while mounting '${node.name}'`);
55
62
  const tag = node.name;
56
- if (tag.includes('-') && typeof customElements !== 'undefined' && !customElements.get(tag)) {
57
- await customElements.whenDefined(tag);
58
- }
59
-
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.
65
+ parentEl.replaceChildren(el);
68
66
  await frame();
69
-
70
- if (!resolve(node.name)) {
71
- throw new Error(`CascadeError: container '${node.name}' failed to register after mount`);
72
- }
67
+ if (!resolve(node.name)) throw new Error(`CascadeError: container '${node.name}' failed to register after mount`);
73
68
  }
74
69
 
75
70
  return resolve(target);
@@ -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
  *
@@ -41,15 +41,23 @@ nodes.set('main', root);
41
41
  // so a just-unmounted container is not confused with a GC'd one (RT bug 8.2).
42
42
  const gone = new Set();
43
43
 
44
- // Prune stale nodes after their element is collected.
44
+ // Prune stale nodes after their element is collected by GC.
45
+ // This is the ONLY place a node is structurally removed from the graph.
46
+ // Normal DOM disconnects (remove()) only null the ref — they preserve topology.
45
47
  const finalizer = typeof FinalizationRegistry !== 'undefined'
46
48
  ? new FinalizationRegistry((name) => {
47
49
  const node = nodes.get(name);
50
+ // Only detach if the node still has no live element (i.e. truly GC'd,
51
+ // not just remounted under a new element instance).
48
52
  if (node && !node.alive()) detach(name);
49
53
  })
50
54
  : { register() {}, unregister() {} };
51
55
 
52
- /** Removes a node from the tree, reparenting its children to its parent. */
56
+ /**
57
+ * Structurally removes a node from the tree, reparenting its children to its
58
+ * parent. Called ONLY from the FinalizationRegistry (GC path). DOM disconnects
59
+ * must NOT call this — they use remove() which only nulls the ref.
60
+ */
53
61
  function detach(name) {
54
62
  const node = nodes.get(name);
55
63
  if (!node || node === root) return;
@@ -78,45 +86,65 @@ export function add(name, el, parent = 'main') {
78
86
  if (existing === root) {
79
87
  // Root re-registration from anchor(): refresh the WeakRef and return.
80
88
  // The root cannot be reparented and must not spawn a duplicate node.
81
- existing.ref = new WeakRef(el);
89
+ if (el) existing.ref = new WeakRef(el);
82
90
  return existing;
83
91
  }
84
92
  const prev = existing.ref?.deref();
85
- if (prev && prev !== el) {
93
+ if (prev && el && prev !== el) {
86
94
  throw new Error(`ContainerError: Singleton violation — '${name}' is already mounted. A second instance cannot register while the first is active.`);
87
95
  }
88
- // Same element re-registering (e.g. HMR): refresh the ref and return.
89
- existing.ref = new WeakRef(el);
96
+ // Re-registering after a disconnect (ref was null) or HMR: refresh the ref.
97
+ // Also re-register with the finalizer so the new element instance is tracked.
98
+ if (el) {
99
+ existing.ref = new WeakRef(el);
100
+ finalizer.register(el, name);
101
+ }
90
102
  return existing;
91
103
  }
92
104
 
93
105
  const parentNode = nodes.get(parent) ?? root;
94
- const node = new Node(name, new WeakRef(el), parentNode);
106
+ const node = new Node(name, el ? new WeakRef(el) : null, parentNode);
95
107
  parentNode.children.add(node);
96
108
  nodes.set(name, node);
97
- finalizer.register(el, name);
109
+ if (el) finalizer.register(el, name);
98
110
  return node;
99
111
  }
100
112
 
101
113
  /**
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.
114
+ * Unregisters the live element of a container node when the element disconnects
115
+ * from the DOM. The node itself stays in `nodes` to preserve graph topology for
116
+ * future ensure()/lca() calls — only its element ref is cleared.
117
+ *
118
+ * The name is marked `gone` for one macrotask so any lookup that races the
119
+ * disconnect (e.g. an in-flight navigation reading the old element) gets
120
+ * `undefined` rather than the stale ref.
121
+ *
122
+ * Structural removal (nodes.delete) is deferred to the FinalizationRegistry so
123
+ * it only happens after the element is truly GC'd and cannot be remounted.
104
124
  *
105
125
  * @param {string} name - registry key.
106
- * @param {Element} [el] - element guard; if given, only removes when it matches.
126
+ * @param {Element} [el] - element guard; if given, only clears when it matches.
107
127
  */
108
128
  export function remove(name, el) {
109
129
  const node = nodes.get(name);
110
130
  if (!node || node === root) return;
111
131
 
112
132
  const current = node.ref?.deref();
133
+ // Guard: if a specific element is given and it doesn't match the registered
134
+ // one, this is a stale disconnect from a previous instance — ignore.
113
135
  if (el && current && current !== el) return;
114
136
 
137
+ // Unregister from the finalizer (we're clearing the ref manually).
115
138
  if (current) {
116
139
  try { finalizer.unregister(current); } catch (_) {}
117
140
  }
118
- detach(name);
119
141
 
142
+ // Clear the element ref but KEEP the node in the graph.
143
+ // This preserves topology: lca() and path() can still traverse the tree
144
+ // on the next navigation that needs to remount this container.
145
+ node.ref = null;
146
+
147
+ // Mark as transiently gone for one macrotask.
120
148
  gone.add(name);
121
149
  if (typeof setTimeout !== 'undefined') {
122
150
  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