atmx-react 0.43.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.
@@ -1,236 +0,0 @@
1
- // FILE: src/hooks/useAxiomMutation.ts
2
- import { useState, useCallback, useRef, useEffect } from "react";
3
- import { useAxiom } from "./useAxiom";
4
- import {
5
- AxiomQueryDef,
6
- AxiomState,
7
- getNextReqId,
8
- wasmEngine,
9
- allocString,
10
- allocBytes,
11
- pendingRequests,
12
- buildRequestPath,
13
- Route,
14
- } from "../utils/core-bridge";
15
-
16
- function extractFieldError(error: any, fieldName: string): string | null {
17
- if (error?.code !== "ValidationError" || !error?.details) return null;
18
- const lines = error.details.split("\n");
19
- for (const line of lines) {
20
- if (line.startsWith(fieldName + ":"))
21
- return line.substring(fieldName.length + 1).trim();
22
- }
23
- return null;
24
- }
25
-
26
- export function useAxiomMutation<T, A>(
27
- mutationFn: (args: A) => AxiomQueryDef<T>,
28
- ) {
29
- const { isReady, config } = useAxiom();
30
- const [state, setState] = useState<AxiomState<T>>({
31
- status: "idle",
32
- data: null,
33
- error: null,
34
- isFetching: false,
35
- isMutating: false,
36
- source: null,
37
- });
38
- const isMounted = useRef(true);
39
-
40
- useEffect(() => {
41
- isMounted.current = true;
42
- return () => {
43
- isMounted.current = false;
44
- };
45
- }, []);
46
-
47
- const execute = useCallback(
48
- async (args: A, options?: { headers?: Record<string, string> }) => {
49
- if (!isReady || !config) return;
50
-
51
- const rawDef = mutationFn(args);
52
- rawDef.headers = options?.headers; // ✨ Inject dynamic headers!
53
-
54
- const reqId = getNextReqId();
55
- if (isMounted.current)
56
- setState({
57
- status: "loading",
58
- data: null,
59
- error: null,
60
- isFetching: false,
61
- isMutating: true,
62
- source: null,
63
- });
64
-
65
- // FIX: Bypass router lookup!
66
- const syntheticRoute: Route = {
67
- id: rawDef.endpointId,
68
- namespace: rawDef.namespace,
69
- name: rawDef.name || "unknown",
70
- method: rawDef.method,
71
- pathTemplate: rawDef.path,
72
- isStream: !!rawDef.isStream,
73
- };
74
-
75
- const { path } = buildRequestPath(syntheticRoute, rawDef.args);
76
-
77
- const isFormUrlEncoded = Object.entries(rawDef.headers || {}).some(
78
- ([k, v]) =>
79
- k.toLowerCase() === "content-type" &&
80
- v.includes("application/x-www-form-urlencoded"),
81
- );
82
-
83
- let payloadPtr = 0,
84
- payloadLen = 0,
85
- payloadObj = null;
86
- if (rawDef.payload !== undefined && rawDef.payload !== null) {
87
- payloadObj = rawDef.serializer
88
- ? rawDef.serializer(rawDef.payload)
89
- : rawDef.payload;
90
- let payloadBytes: Uint8Array;
91
-
92
- if (isFormUrlEncoded && typeof payloadObj === "object") {
93
- const params = new URLSearchParams();
94
- for (const [k, v] of Object.entries(payloadObj))
95
- params.append(k, String(v));
96
- payloadBytes = new TextEncoder().encode(params.toString());
97
- } else {
98
- payloadBytes = new TextEncoder().encode(JSON.stringify(payloadObj));
99
- }
100
-
101
- payloadPtr = allocBytes(payloadBytes);
102
- payloadLen = payloadBytes.length;
103
- }
104
-
105
- if (config.debug) {
106
- console.groupCollapsed(
107
- `%c➔ WASM MUTATE [#${reqId}]`,
108
- `color: #7c3aed; font-weight: bold;`,
109
- );
110
- console.log("Namespace:", rawDef.namespace);
111
- console.log("Endpoint ID:", rawDef.endpointId);
112
- console.log("Path:", path);
113
- console.log("Payload:", payloadObj);
114
- console.groupEnd();
115
- }
116
-
117
- pendingRequests.set(reqId, {
118
- isStream: !!rawDef.isStream,
119
- onResponse: (response) => {
120
- if (!isMounted.current) return;
121
-
122
- if (config.debug) {
123
- const evtName =
124
- response.eventType === 1
125
- ? "NetworkSuccess"
126
- : response.eventType === 2
127
- ? "CacheHit"
128
- : response.eventType === 3
129
- ? "CacheHitAndFetching"
130
- : response.eventType === 4
131
- ? "Error"
132
- : "Complete";
133
- console.groupCollapsed(
134
- `%c← WASM RESP [#${reqId}]`,
135
- `color: #059669; font-weight: bold;`,
136
- );
137
- console.log("Event Type:", evtName);
138
- if (response.data) console.log("Data:", response.data);
139
- if (response.error) console.log("Error:", response.error);
140
- console.groupEnd();
141
- }
142
-
143
- if (response.eventType === 4) {
144
- setState({
145
- status: "error",
146
- error: response.error || null,
147
- isFetching: false,
148
- isMutating: false,
149
- data: null,
150
- source: null,
151
- });
152
- return;
153
- }
154
- if (response.data) {
155
- try {
156
- let source: "cache" | "network" = "network";
157
- if (response.eventType === 2 || response.eventType === 3)
158
- source = "cache";
159
-
160
- setState({
161
- status: "success",
162
- data: rawDef.decoder(response.data),
163
- error: null,
164
- isFetching: false,
165
- isMutating: false,
166
- source,
167
- });
168
- } catch (e) {
169
- setState({
170
- status: "error",
171
- error: {
172
- message: "Decoding failed",
173
- details: String(e),
174
- } as any,
175
- isFetching: false,
176
- isMutating: false,
177
- data: null,
178
- source: null,
179
- });
180
- }
181
- }
182
- },
183
- onComplete: () => {
184
- if (isMounted.current)
185
- setState((prev) => ({
186
- ...prev,
187
- isMutating: false,
188
- isFetching: false,
189
- }));
190
- },
191
- });
192
-
193
- const nsStr = allocString(rawDef.namespace);
194
- const mStr = allocString(rawDef.method);
195
- const pStr = allocString(path);
196
- const tpStr = allocString("");
197
- const hStr = allocString(
198
- rawDef.headers ? JSON.stringify(rawDef.headers) : "",
199
- );
200
-
201
- wasmEngine.axiom_wasm_call(
202
- reqId,
203
- nsStr.ptr,
204
- nsStr.len,
205
- rawDef.endpointId,
206
- mStr.ptr,
207
- mStr.len,
208
- pStr.ptr,
209
- pStr.len,
210
- tpStr.ptr,
211
- tpStr.len,
212
- hStr.ptr,
213
- hStr.len,
214
- payloadPtr,
215
- payloadLen,
216
- );
217
-
218
- wasmEngine.axiom_free_memory(nsStr.ptr, nsStr.len);
219
- wasmEngine.axiom_free_memory(mStr.ptr, mStr.len);
220
- wasmEngine.axiom_free_memory(pStr.ptr, pStr.len);
221
- wasmEngine.axiom_free_memory(tpStr.ptr, tpStr.len);
222
- wasmEngine.axiom_free_memory(hStr.ptr, hStr.len);
223
- },
224
- [isReady, config, mutationFn],
225
- );
226
-
227
- return {
228
- execute,
229
- state,
230
- data: state.data,
231
- isMutating: state.isMutating,
232
- error: state.error,
233
- getFieldError: (fieldName: string) =>
234
- extractFieldError(state.error, fieldName),
235
- };
236
- }
@@ -1,41 +0,0 @@
1
- // FILE: src/hooks/useAxiomQuery.ts
2
- import { useEffect, useSyncExternalStore, useCallback } from "react";
3
- import { useAxiom } from "./useAxiom";
4
- import { AxiomQueryDef } from "../utils/core-bridge";
5
- import { axiomQueryManager } from "../core/QueryManager";
6
-
7
- export function useAxiomQuery<T>(
8
- queryDef: AxiomQueryDef<T>,
9
- options: { enabled?: boolean } = {},
10
- ) {
11
- const { isReady, config } = useAxiom();
12
- const { enabled = true } = options;
13
-
14
- const activeQuery = axiomQueryManager.getQuery(queryDef);
15
-
16
- const state = useSyncExternalStore(
17
- useCallback(
18
- (onStoreChange) => activeQuery.subscribe(onStoreChange),
19
- [activeQuery],
20
- ),
21
- () => activeQuery.getState(),
22
- () => activeQuery.getState(),
23
- );
24
-
25
- useEffect(() => {
26
- if (!isReady || !enabled) return;
27
- if (state.status === "idle") {
28
- activeQuery.fetch(false, config?.debug);
29
- }
30
- }, [isReady, enabled, activeQuery, state.status, config?.debug]);
31
-
32
- return {
33
- data: state.data,
34
- isLoading: state.status === "loading",
35
- isFetching: state.isFetching,
36
- source: state.source,
37
- error: state.error,
38
- state: state, // Export raw state for <AxWhen>
39
- refetch: () => activeQuery.fetch(true, config?.debug),
40
- };
41
- }
package/src/index.ts DELETED
@@ -1,39 +0,0 @@
1
- // FILE: src/index.ts
2
- export { setAuthToken, clearAuthToken } from "./utils/core-bridge";
3
- // 1. Context & Provider (Global Lifecycle)
4
- export { AxiomProvider, AxiomContext } from "./context/AxiomProvider";
5
-
6
- // 2. The Context Stack (InheritedWidget Pattern)
7
- // This allows nested components to reach up and grab parent $data
8
- export {
9
- AxiomDataContext,
10
- useAxiomContext,
11
- useAxiomData,
12
- } from "./context/AxiomDataContext";
13
-
14
- // 3. High-Level Declarative Components
15
- // These wrap the hooks to give a "Zero-JS" feel to React
16
- export { AxQuery } from "./components/AxQuery";
17
- export { AxWhen } from "./components/AxWhen";
18
- export { AxTrigger } from "./components/AxTrigger";
19
-
20
- // 4. Core Hooks
21
- // Used primarily by the generated SDK, but available for custom logic
22
- export { useAxiom } from "./hooks/useAxiom";
23
- export { useAxiomQuery } from "./hooks/useAxiomQuery";
24
- export { useAxiomMutation } from "./hooks/useAxiomMutation";
25
-
26
- // 5. Shared State & Registry
27
- // Manages deduplication and syncing across components
28
- export { axiomQueryManager } from "./core/QueryManager";
29
-
30
- // 6. Core Types
31
- // Essential for type-safe development and generated SDKs
32
- export type {
33
- AtmxConfig,
34
- AxiomError,
35
- AxiomResponse,
36
- AxiomQueryDef,
37
- AxiomState,
38
- AxiomStatus,
39
- } from "./utils/core-bridge";
@@ -1,62 +0,0 @@
1
- import {
2
- initWasm,
3
- wasmEngine,
4
- allocBytes,
5
- allocString,
6
- setAuthToken,
7
- clearAuthToken,
8
- } from "atmx-web/src/core/wasm";
9
- import {
10
- resolveRoute,
11
- Route,
12
- buildRequestPath,
13
- } from "atmx-web/src/core/router";
14
- import { pendingRequests } from "atmx-web/src/core/callback";
15
- import {
16
- AtmxConfig,
17
- AxiomResponse,
18
- AxiomError,
19
- EventType,
20
- } from "atmx-web/src/core/types";
21
-
22
- let globalReqCounter = 0;
23
- export const getNextReqId = () => ++globalReqCounter;
24
-
25
- export interface AxiomQueryDef<T> {
26
- namespace: string;
27
- name: string;
28
- endpointId: number;
29
- method: string;
30
- path: string;
31
- payload?: any;
32
- decoder: (json: any) => T;
33
- serializer?: (payload: any) => any;
34
- args: Record<string, any>;
35
- isStream?: boolean;
36
- headers?: Record<string, string>; // ✨ NEW
37
- }
38
-
39
- export type AxiomStatus = "idle" | "loading" | "success" | "error";
40
-
41
- export interface AxiomState<T> {
42
- status: AxiomStatus;
43
- data: T | null;
44
- error: AxiomError | null;
45
- isFetching: boolean;
46
- isMutating: boolean;
47
- source: "cache" | "network" | null;
48
- }
49
-
50
- export {
51
- initWasm,
52
- wasmEngine,
53
- allocBytes,
54
- allocString,
55
- resolveRoute,
56
- buildRequestPath,
57
- pendingRequests,
58
- setAuthToken,
59
- clearAuthToken,
60
- };
61
-
62
- export type { AtmxConfig, AxiomResponse, AxiomError, EventType, Route };
package/tsconfig.json DELETED
@@ -1,27 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "useDefineForClassFields": true,
5
- "lib": [
6
- "ES2020",
7
- "DOM",
8
- "DOM.Iterable"
9
- ],
10
- "module": "ESNext",
11
- "skipLibCheck": true,
12
- "moduleResolution": "bundler",
13
- "allowImportingTsExtensions": true,
14
- "resolveJsonModule": true,
15
- "isolatedModules": true,
16
- "noEmit": true,
17
- "jsx": "react-jsx",
18
- "strict": true,
19
- "noUnusedLocals": true,
20
- "noUnusedParameters": true,
21
- "noFallthroughCasesInSwitch": true,
22
- "declaration": true
23
- },
24
- "include": [
25
- "src"
26
- ]
27
- }
package/tsup.config.ts DELETED
@@ -1,11 +0,0 @@
1
- import { defineConfig } from 'tsup';
2
-
3
- export default defineConfig({
4
- entry: ['src/index.ts'],
5
- format: ['cjs', 'esm'],
6
- dts: true,
7
- splitting: false,
8
- sourcemap: true,
9
- clean: true,
10
- external: ['react', 'react-dom', 'atmx'],
11
- });