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/dist/atmx.es.js +679 -0
- package/{src/core/vendor/axiom_runtime.js → dist/atmx.umd.js} +20 -18
- package/dist/core/callback.d.ts +7 -0
- package/dist/core/context.d.ts +16 -0
- package/dist/core/query.d.ts +17 -0
- package/dist/core/router.d.ts +20 -0
- package/dist/core/types.d.ts +44 -0
- package/dist/core/wasm.d.ts +11 -0
- package/dist/dom/components/engine-status.d.ts +5 -0
- package/dist/dom/indicators.d.ts +2 -0
- package/dist/dom/lifecycle.d.ts +6 -0
- package/dist/dom/scanner.d.ts +2 -0
- package/dist/index.d.ts +18 -0
- package/dist/resolver/evaluator.d.ts +6 -0
- package/dist/resolver/render.d.ts +2 -0
- package/package.json +6 -2
- package/justfile +0 -8
- package/scripts/upload.sh +0 -61
- package/src/core/callback.ts +0 -92
- package/src/core/context.ts +0 -56
- package/src/core/query.ts +0 -162
- package/src/core/router.ts +0 -91
- package/src/core/types.ts +0 -49
- package/src/core/wasm.ts +0 -223
- package/src/dom/components/engine-status.ts +0 -62
- package/src/dom/indicators.ts +0 -36
- package/src/dom/lifecycle.ts +0 -379
- package/src/dom/scanner.ts +0 -125
- package/src/index.ts +0 -78
- package/src/resolver/evaluator.ts +0 -46
- package/src/resolver/render.ts +0 -196
- package/tsconfig.json +0 -25
- package/vite-env.d.ts +0 -11
- package/vite.config.ts +0 -27
- /package/{public → dist}/.axiom +0 -0
- /package/{public → dist}/axiom_runtime.wasm +0 -0
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>();
|
package/src/core/router.ts
DELETED
|
@@ -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
|
-
}
|
package/src/dom/indicators.ts
DELETED
|
@@ -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
|
-
}
|