atmx-web 0.44.0 → 0.47.0

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/src/core/query.ts DELETED
@@ -1,162 +0,0 @@
1
- // FILE: src/core/query.ts
2
- import { wasmEngine, allocString, allocBytes } from "./wasm";
3
- import { pendingRequests } from "./callback";
4
- import { Route, buildRequestPath } from "./router";
5
- import { AtmxContextData } from "./context";
6
- import { EventType } from "./types";
7
- import { atmx } from "../index";
8
-
9
- let globalReqCounter = 0;
10
- type Listener = (state: Partial<AtmxContextData>) => void;
11
-
12
- export class ActiveQuery {
13
- key: string;
14
- route: Route;
15
- args: Record<string, any>;
16
- state: Partial<AtmxContextData>;
17
- listeners: Set<Listener> = new Set();
18
- currentReqId: number | null = null;
19
-
20
- constructor(key: string, route: Route, args: Record<string, any>) {
21
- this.key = key;
22
- this.route = route;
23
- this.args = args;
24
- this.state = {
25
- state: "idle",
26
- isFetching: false,
27
- source: null,
28
- data: null,
29
- error: null,
30
- };
31
- }
32
-
33
- subscribe(listener: Listener) {
34
- this.listeners.add(listener);
35
- listener(this.state); // Instantly hydrate the DOM element with current state
36
- }
37
-
38
- update(newState: Partial<AtmxContextData>) {
39
- this.state = { ...this.state, ...newState };
40
- this.listeners.forEach((l) => l(this.state)); // Broadcast to all DOM elements!
41
- }
42
-
43
- fetch(force: boolean = false) {
44
- if (this.state.isFetching && !force) return;
45
-
46
- this.update({
47
- state: this.state.data ? "data" : "loading",
48
- isFetching: true,
49
- error: null,
50
- });
51
-
52
- const reqId = ++globalReqCounter;
53
- this.currentReqId = reqId;
54
-
55
- const { path, remainingArgs } = buildRequestPath(this.route, this.args);
56
-
57
- let payloadBytes = new Uint8Array(0);
58
- if (Object.keys(remainingArgs).length > 0) {
59
- payloadBytes = new TextEncoder().encode(JSON.stringify(remainingArgs));
60
- }
61
-
62
- if (atmx.debug) {
63
- console.groupCollapsed(
64
- `%c➔ WASM QUERY [#${reqId}]`,
65
- `color: #7c3aed; font-weight: bold;`,
66
- );
67
- console.log("Namespace:", this.route.namespace);
68
- console.log("Endpoint ID:", this.route.id);
69
- console.log("Path:", path);
70
- console.groupEnd();
71
- }
72
-
73
- pendingRequests.set(reqId, {
74
- isStream: this.route.isStream,
75
- onResponse: (res) => {
76
- if (this.currentReqId !== reqId) return;
77
-
78
- if (atmx.debug) {
79
- const evtName =
80
- res.eventType === 1
81
- ? "NetworkSuccess"
82
- : res.eventType === 2
83
- ? "CacheHit"
84
- : res.eventType === 3
85
- ? "CacheHitAndFetching"
86
- : res.eventType === 4
87
- ? "Error"
88
- : "Complete";
89
- console.groupCollapsed(
90
- `%c← WASM RESP [#${reqId}]`,
91
- `color: #059669; font-weight: bold;`,
92
- );
93
- console.log("Event Type:", evtName);
94
- if (res.data) console.log("Data:", res.data);
95
- if (res.error) console.log("Error:", res.error);
96
- console.groupEnd();
97
- }
98
-
99
- if (res.eventType === EventType.Error) {
100
- this.update({ state: "error", error: res.error, isFetching: false });
101
- return;
102
- }
103
-
104
- if (res.data) {
105
- let source: "cache" | "network" = "network";
106
- let isFetching = false;
107
-
108
- if (res.eventType === EventType.CacheHit) {
109
- source = "cache";
110
- isFetching = false;
111
- } else if (res.eventType === EventType.CacheHitAndFetching) {
112
- source = "cache";
113
- isFetching = true;
114
- } else if (res.eventType === EventType.NetworkSuccess) {
115
- source = "network";
116
- isFetching = false;
117
- }
118
-
119
- this.update({ state: "data", data: res.data, source, isFetching });
120
- }
121
- },
122
- onComplete: () => {
123
- if (this.currentReqId !== reqId) return;
124
- if (atmx.debug)
125
- console.log(`%c✔ Stream Complete [#${reqId}]`, "color: #94a3b8;");
126
- this.update({ isFetching: false });
127
- },
128
- });
129
-
130
- const nsStr = allocString(this.route.namespace);
131
- const mStr = allocString(this.route.method);
132
- const pStr = allocString(path);
133
- const payloadPtr = allocBytes(payloadBytes);
134
- const tpStr = allocString(""); // Traceparent (empty for now)
135
- const hStr = allocString(""); // Headers (empty for now)
136
-
137
- wasmEngine.axiom_wasm_call(
138
- reqId,
139
- nsStr.ptr,
140
- nsStr.len,
141
- this.route.id,
142
- mStr.ptr,
143
- mStr.len,
144
- pStr.ptr,
145
- pStr.len,
146
- tpStr.ptr,
147
- tpStr.len,
148
- hStr.ptr,
149
- hStr.len,
150
- payloadPtr,
151
- payloadBytes.length,
152
- );
153
-
154
- wasmEngine.axiom_free_memory(nsStr.ptr, nsStr.len);
155
- wasmEngine.axiom_free_memory(mStr.ptr, mStr.len);
156
- wasmEngine.axiom_free_memory(pStr.ptr, pStr.len);
157
- wasmEngine.axiom_free_memory(tpStr.ptr, tpStr.len);
158
- wasmEngine.axiom_free_memory(hStr.ptr, hStr.len);
159
- }
160
- }
161
-
162
- export const queryRegistry = new Map<string, ActiveQuery>();
@@ -1,91 +0,0 @@
1
- // FILE: src/core/router.ts
2
-
3
- export interface RouteAuth {
4
- required: boolean;
5
- methods: any[];
6
- }
7
-
8
- export interface Route {
9
- id: number;
10
- namespace: string;
11
- name: string;
12
- method: string;
13
- pathTemplate: string;
14
- isStream: boolean;
15
- auth?: RouteAuth | null;
16
- bodyParam?: string | null; // <-- NEW
17
- }
18
-
19
- const routes = new Map<string, Route>();
20
-
21
- export function initRouter(namespace: string, contractJson: any) {
22
- const endpoints = contractJson?.ir?.endpoints;
23
- if (!endpoints) return;
24
-
25
- const epList = Array.isArray(endpoints)
26
- ? endpoints
27
- : Object.values(endpoints);
28
-
29
- epList.forEach((ep: any) => {
30
- const routeKey = `${namespace}.${ep.name}`;
31
-
32
- // ✨ Detect which param belongs in the body
33
- const bodyParam =
34
- ep.parameters?.find((p: any) => p.source === "body")?.name || null;
35
-
36
- routes.set(routeKey, {
37
- id: ep.id,
38
- namespace: namespace,
39
- name: ep.name,
40
- method: ep.method ? ep.method.toUpperCase() : "GET",
41
- pathTemplate: ep.path,
42
- isStream: ep.isStream || false,
43
- auth: ep.auth || null,
44
- bodyParam: bodyParam, // <-- NEW
45
- });
46
- });
47
- console.log(
48
- `🛣️ ATMX Router: Mapped ${epList.length} endpoints for '${namespace}'.`,
49
- );
50
- }
51
-
52
- export function resolveRoute(rpcCall: string): Route | null {
53
- return routes.get(rpcCall.trim()) || null;
54
- }
55
-
56
- export function buildRequestPath(
57
- route: Route,
58
- args: Record<string, any>,
59
- ): { path: string; remainingArgs: Record<string, any> } {
60
- let finalPath = route.pathTemplate;
61
- const remainingArgs = { ...args };
62
-
63
- const pathMatches = finalPath.match(/\{([^}]+)\}/g);
64
- if (pathMatches) {
65
- pathMatches.forEach((match) => {
66
- const key = match.replace(/[{}]/g, "");
67
- if (remainingArgs[key] !== undefined) {
68
- finalPath = finalPath.replace(
69
- match,
70
- encodeURIComponent(String(remainingArgs[key])),
71
- );
72
- delete remainingArgs[key];
73
- }
74
- });
75
- }
76
-
77
- if (route.method === "GET" && Object.keys(remainingArgs).length > 0) {
78
- const params = new URLSearchParams();
79
- for (const [key, value] of Object.entries(remainingArgs)) {
80
- if (value !== undefined && value !== null) {
81
- params.append(key, String(value));
82
- }
83
- }
84
- const separator = finalPath.includes("?") ? "&" : "?";
85
- finalPath += `${separator}${params.toString()}`;
86
-
87
- for (const key of Object.keys(remainingArgs)) delete remainingArgs[key];
88
- }
89
-
90
- return { path: finalPath, remainingArgs };
91
- }
package/src/core/types.ts DELETED
@@ -1,49 +0,0 @@
1
- export interface AtmxContractConfig {
2
- contractUrl: string;
3
- baseUrl: string;
4
- }
5
-
6
- export interface AtmxConfig {
7
- contracts: Record<string, AtmxContractConfig>;
8
- debug?: boolean; // Enable verbose logging
9
- }
10
-
11
- export enum EventType {
12
- Complete = 0,
13
- NetworkSuccess = 1,
14
- CacheHit = 2,
15
- CacheHitAndFetching = 3,
16
- Error = 4,
17
- }
18
-
19
- export interface AxiomError {
20
- stage: string;
21
- category: string;
22
- code: string | Record<string, any>;
23
- message: string;
24
- retryable: boolean;
25
- details?: string;
26
- }
27
-
28
- export interface AxiomResponse {
29
- eventType: EventType;
30
- data?: any;
31
- error?: AxiomError;
32
- isFetching?: boolean;
33
- isStreamChunk?: boolean;
34
- }
35
-
36
- export interface InitResult {
37
- ok: boolean;
38
- error?: AxiomError;
39
- contracts: Record<
40
- string,
41
- { loaded: boolean; endpointCount: number; error?: string }
42
- >;
43
- initDurationMs: number;
44
- }
45
-
46
- export interface ContractLoadedEvent {
47
- namespace: string;
48
- endpointCount: number;
49
- }
package/src/core/wasm.ts DELETED
@@ -1,223 +0,0 @@
1
- // FILE: src/core/wasm.ts
2
- import { setupWebCallback } from "./callback";
3
- import { AtmxConfig } from "../core/types";
4
- import { initRouter } from "./router";
5
- import { InitResult } from "./types";
6
-
7
- // @ts-ignore
8
- import wasmGlueCode from "./vendor/axiom_runtime.js?raw";
9
-
10
- export let wasmEngine: any = null;
11
-
12
- export function allocBytes(bytes: Uint8Array): number {
13
- if (bytes.length === 0) return 0;
14
- const ptr = wasmEngine.axiom_malloc(bytes.length);
15
- const mem = new Uint8Array(wasmEngine.memory.buffer);
16
- mem.set(bytes, ptr);
17
- return ptr;
18
- }
19
-
20
- export function allocString(str: string): { ptr: number; len: number } {
21
- if (!str) return { ptr: 0, len: 0 };
22
- const bytes = new TextEncoder().encode(str);
23
- const ptr = allocBytes(bytes);
24
- return { ptr, len: bytes.length };
25
- }
26
-
27
- function getWasmUrl(): string {
28
- return "https://atmx.axiomcore.dev/latest/axiom_runtime.wasm";
29
- }
30
-
31
- export async function initWasm(config: AtmxConfig): Promise<InitResult> {
32
- const startTime = performance.now();
33
- const result: InitResult = {
34
- ok: false,
35
- contracts: {},
36
- initDurationMs: 0,
37
- };
38
-
39
- try {
40
- if (!(window as any).wasm_bindgen) {
41
- const script = document.createElement("script");
42
- script.type = "text/javascript";
43
- script.text = `${wasmGlueCode}\nwindow.wasm_bindgen = wasm_bindgen;`;
44
- document.head.appendChild(script);
45
- }
46
-
47
- const wasmBindgen = (window as any).wasm_bindgen;
48
- const wasmLocation = getWasmUrl();
49
- const fetchUrl = `${wasmLocation}?t=${Date.now()}`;
50
-
51
- try {
52
- wasmEngine = await wasmBindgen(fetch(fetchUrl));
53
- } catch (e: any) {
54
- throw {
55
- stage: "ffiBoundary",
56
- category: "network",
57
- code: "WasmFetchFailed",
58
- message: `Failed to download WASM engine: ${e.message}`,
59
- retryable: true,
60
- };
61
- }
62
-
63
- setupWebCallback(wasmEngine);
64
-
65
- setInterval(() => {
66
- if (wasmEngine) wasmEngine.axiom_process_responses();
67
- }, 16);
68
-
69
- const dbPath = allocString("");
70
- const initResultCode = wasmEngine.axiom_wasm_initialize(
71
- dbPath.ptr,
72
- dbPath.len,
73
- );
74
- wasmEngine.axiom_free_memory(dbPath.ptr, dbPath.len);
75
-
76
- if (initResultCode !== 0) {
77
- throw {
78
- stage: "runtime",
79
- category: "server",
80
- code: "InitFailed",
81
- message: `Engine init failed (Code: ${initResultCode})`,
82
- retryable: false,
83
- };
84
- }
85
-
86
- for (const [namespace, contractConfig] of Object.entries(
87
- config.contracts,
88
- )) {
89
- try {
90
- const contractRes = await fetch(contractConfig.contractUrl);
91
- if (!contractRes.ok) {
92
- throw new Error(`HTTP ${contractRes.status}`);
93
- }
94
-
95
- const contractArrayBuffer = await contractRes.arrayBuffer();
96
- let endpointCount = 0;
97
-
98
- try {
99
- const jsonStr = new TextDecoder().decode(contractArrayBuffer);
100
- const contractJson = JSON.parse(jsonStr);
101
- const endpoints = contractJson?.ir?.endpoints;
102
- if (endpoints) {
103
- endpointCount = Array.isArray(endpoints)
104
- ? endpoints.length
105
- : Object.keys(endpoints).length;
106
- }
107
- initRouter(namespace, contractJson);
108
- } catch (e) {
109
- console.error(
110
- `ATMX: Failed to parse .axiom contract for '${namespace}'`,
111
- e,
112
- );
113
- }
114
-
115
- const contractBytes = new Uint8Array(contractArrayBuffer);
116
- const cPtr = allocBytes(contractBytes);
117
- const nsStr = allocString(namespace);
118
- const urlStr = allocString(contractConfig.baseUrl);
119
-
120
- const loadResult = wasmEngine.axiom_wasm_load_contract(
121
- nsStr.ptr,
122
- nsStr.len,
123
- urlStr.ptr,
124
- urlStr.len,
125
- cPtr,
126
- contractBytes.length,
127
- 0,
128
- 0,
129
- 0,
130
- 0,
131
- );
132
-
133
- wasmEngine.axiom_free_memory(cPtr, contractBytes.length);
134
- wasmEngine.axiom_free_memory(nsStr.ptr, nsStr.len);
135
- wasmEngine.axiom_free_memory(urlStr.ptr, urlStr.len);
136
-
137
- if (loadResult !== 0 && loadResult !== -1) {
138
- throw new Error(`Load error (Code: ${loadResult})`);
139
- }
140
-
141
- if (config.debug) {
142
- console.log(
143
- `%c🔒 Axiom Contract Loaded: ${namespace} (${endpointCount} endpoints)`,
144
- "color: #10b981; font-weight: bold;",
145
- );
146
- }
147
-
148
- result.contracts[namespace] = { loaded: true, endpointCount };
149
-
150
- // ✨ EVENT: Emit contract loaded
151
- document.dispatchEvent(
152
- new CustomEvent("atmx:contract-loaded", {
153
- detail: { namespace, endpointCount },
154
- }),
155
- );
156
- } catch (contractErr: any) {
157
- result.contracts[namespace] = {
158
- loaded: false,
159
- endpointCount: 0,
160
- error: contractErr.message,
161
- };
162
- console.error(`ATMX: Contract failed for ${namespace}`, contractErr);
163
- }
164
- }
165
-
166
- result.ok = true;
167
- } catch (fatalError: any) {
168
- result.ok = false;
169
- // Map native JS errors into structured AxiomErrors
170
- result.error = fatalError.stage
171
- ? fatalError
172
- : {
173
- stage: "runtime",
174
- category: "unknown",
175
- code: "FatalError",
176
- message: fatalError.message || String(fatalError),
177
- retryable: false,
178
- };
179
- }
180
-
181
- result.initDurationMs = Math.round(performance.now() - startTime);
182
- return result;
183
- }
184
-
185
- export function setAuthToken(
186
- namespace: string,
187
- methodName: string,
188
- token: string,
189
- ) {
190
- if (!wasmEngine) return;
191
- const nStr = allocString(namespace);
192
- const mStr = allocString(methodName);
193
- const tStr = allocString(token);
194
-
195
- wasmEngine.axiom_wasm_set_auth_token(
196
- nStr.ptr,
197
- nStr.len,
198
- mStr.ptr,
199
- mStr.len,
200
- tStr.ptr,
201
- tStr.len,
202
- );
203
-
204
- wasmEngine.axiom_free_memory(nStr.ptr, nStr.len);
205
- wasmEngine.axiom_free_memory(mStr.ptr, mStr.len);
206
- wasmEngine.axiom_free_memory(tStr.ptr, tStr.len);
207
- }
208
-
209
- export function clearAuthToken(namespace: string, methodName: string) {
210
- if (!wasmEngine) return;
211
- const nStr = allocString(namespace);
212
- const mStr = allocString(methodName);
213
-
214
- wasmEngine.axiom_wasm_clear_auth_token(
215
- nStr.ptr,
216
- nStr.len,
217
- mStr.ptr,
218
- mStr.len,
219
- );
220
-
221
- wasmEngine.axiom_free_memory(nStr.ptr, nStr.len);
222
- wasmEngine.axiom_free_memory(mStr.ptr, mStr.len);
223
- }
@@ -1,62 +0,0 @@
1
- // FILE: src/dom/components/engine-status.ts
2
-
3
- export class AxEngineStatusElement extends HTMLElement {
4
- constructor() {
5
- super();
6
- }
7
-
8
- connectedCallback() {
9
- const loadingText = this.getAttribute("loading-text") || "Initializing...";
10
- const readyText = this.getAttribute("ready-text") || "Online";
11
- const errorText = this.getAttribute("error-text") || "Failed to Connect";
12
-
13
- // Initial state
14
- this.renderState("loading", loadingText, "text-amber-500 animate-pulse");
15
-
16
- // Listen for ATMX engine events globally
17
- document.addEventListener("atmx:ready", () => {
18
- this.renderState("ready", readyText, "text-green-600");
19
- });
20
-
21
- document.addEventListener("atmx:error", (e: Event) => {
22
- const customEvent = e as CustomEvent;
23
- // Check if this is a fatal initialization error vs a normal query error
24
- if (customEvent.detail && customEvent.detail.stage === "runtime") {
25
- this.renderState(
26
- "error",
27
- `${errorText}: ${customEvent.detail.message}`,
28
- "text-red-600",
29
- );
30
- }
31
- });
32
- }
33
-
34
- private renderState(state: string, text: string, defaultClasses: string) {
35
- this.setAttribute("data-state", state);
36
-
37
- // If developer is using raw ax-engine-status directly with inner HTML template matching, we allow it.
38
- // Otherwise, we just swap the raw text node.
39
- const stateElements = this.querySelectorAll(`[data-ax-when]`);
40
-
41
- if (stateElements.length > 0) {
42
- stateElements.forEach((el) => {
43
- if (el.getAttribute("data-ax-when") === state) {
44
- el.removeAttribute("hidden");
45
- } else {
46
- el.setAttribute("hidden", "");
47
- }
48
- });
49
- } else {
50
- // No custom inner templates, just replace text
51
- this.innerText = text;
52
- if (!this.getAttribute("class")) {
53
- this.className = `text-xs font-bold ${defaultClasses}`;
54
- }
55
- }
56
- }
57
- }
58
-
59
- // Automatically register the web component
60
- if (typeof window !== "undefined" && !customElements.get("ax-engine-status")) {
61
- customElements.define("ax-engine-status", AxEngineStatusElement);
62
- }
@@ -1,36 +0,0 @@
1
- const activeRequests = new Map<number, { trigger: HTMLElement, indicators: HTMLElement[] }>();
2
-
3
- export function startRequest(reqId: number, triggerEl: HTMLElement) {
4
- // 1. Add 'ax-request' to the element that triggered the call (e.g., the button)
5
- triggerEl.classList.add('ax-request');
6
-
7
- // 2. Find any associated loading indicators
8
- let indicators: HTMLElement[] = [];
9
- const indicatorSelector = triggerEl.getAttribute('ax-indicator');
10
-
11
- if (indicatorSelector) {
12
- // If ax-indicator="#spinner" is explicitly defined
13
- indicators = Array.from(document.querySelectorAll(indicatorSelector));
14
- } else {
15
- // Fallback: Look for any children inside the trigger with the class 'ax-indicator'
16
- indicators = Array.from(triggerEl.querySelectorAll('.ax-indicator'));
17
- }
18
-
19
- // 3. Turn on the indicators
20
- indicators.forEach(el => el.classList.add('ax-loading'));
21
-
22
- // 4. Save state so we can turn them off later
23
- activeRequests.set(reqId, { trigger: triggerEl, indicators });
24
- }
25
-
26
- export function endRequest(reqId: number) {
27
- const req = activeRequests.get(reqId);
28
- if (!req) return;
29
-
30
- // Turn off all classes
31
- req.trigger.classList.remove('ax-request');
32
- req.indicators.forEach(el => el.classList.remove('ax-loading'));
33
-
34
- // Clean up
35
- activeRequests.delete(reqId);
36
- }