rastack 0.0.16 → 0.0.19

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 (105) hide show
  1. package/CHANGELOG.md +43874 -43013
  2. package/dist/.prettierrc +7 -7
  3. package/dist/compile/analyze.js +19 -11
  4. package/dist/compile/index.js +26 -17
  5. package/dist/compile/manifest.js +2 -3
  6. package/dist/compile/openapi.js +5 -8
  7. package/dist/compile/program.js +19 -10
  8. package/dist/csv-schema.js +5 -6
  9. package/dist/define/index.js +2 -2
  10. package/dist/entity-generation/delete-method.js +8 -10
  11. package/dist/entity-generation/form.js +80 -82
  12. package/dist/entity-generation/get-method.js +35 -36
  13. package/dist/entity-generation/imports.js +24 -20
  14. package/dist/entity-generation/sync-method.d.ts +1 -1
  15. package/dist/entity-generation/sync-method.js +45 -46
  16. package/dist/entity-generation/update-method.js +9 -10
  17. package/dist/rad-wasm-build.d.ts +1 -1
  18. package/dist/rad-wasm-build.js +15 -6
  19. package/dist/rad.js +9 -2
  20. package/dist/schema/camel-to-pastel.js +1 -2
  21. package/dist/schema/capitalise-first-letter.js +1 -2
  22. package/dist/schema/extract-response.js +3 -4
  23. package/dist/schema/fetch-schema.js +4 -16
  24. package/dist/schema/remove-non-model-paths.js +1 -2
  25. package/dist/schema/to-camel-case.js +1 -2
  26. package/dist/schema/to-pastel-case.js +1 -2
  27. package/dist/schema-convert.js +22 -33
  28. package/dist/schema-entities.js +177 -189
  29. package/dist/schema-fetch.js +46 -63
  30. package/dist/schema-full.js +23 -36
  31. package/dist/schema-index.js +16 -27
  32. package/dist/schema-params.js +11 -22
  33. package/dist/seed.js +95 -97
  34. package/hooks/form/form.ts +207 -207
  35. package/hooks/form/index.ts +8 -8
  36. package/hooks/form/interfaces.ts +217 -217
  37. package/hooks/form/structure.ts +39 -39
  38. package/hooks/form/validate-schema.ts +49 -49
  39. package/hooks/index.ts +3 -3
  40. package/hooks/query/api.ts +42 -42
  41. package/hooks/query/delete.ts +45 -45
  42. package/hooks/query/fetch.ts +48 -48
  43. package/hooks/query/index.ts +21 -21
  44. package/hooks/query/interfaces.ts +111 -111
  45. package/hooks/query/list.ts +286 -286
  46. package/hooks/query/update.ts +88 -88
  47. package/hooks/query/url.ts +31 -31
  48. package/hooks/real-time/index.ts +1 -1
  49. package/hooks/real-time/pusher.ts +43 -43
  50. package/jest.config.cjs +6 -6
  51. package/package.json +57 -57
  52. package/provider/index.ts +13 -13
  53. package/provider/provider.tsx +187 -187
  54. package/provider/types.ts +114 -114
  55. package/provider/warehouse.ts +167 -167
  56. package/provider/wasm.ts +124 -124
  57. package/runtime.ts +3 -3
  58. package/src/.prettierrc +7 -7
  59. package/src/compile/analyze.ts +224 -224
  60. package/src/compile/index.ts +86 -86
  61. package/src/compile/manifest.ts +10 -10
  62. package/src/compile/model.ts +69 -69
  63. package/src/compile/openapi.ts +266 -266
  64. package/src/compile/program.ts +40 -40
  65. package/src/csv-schema.ts +187 -187
  66. package/src/define/index.ts +162 -162
  67. package/src/entity-generation/delete-method.ts +37 -37
  68. package/src/entity-generation/form.ts +169 -169
  69. package/src/entity-generation/get-method.ts +80 -80
  70. package/src/entity-generation/imports.ts +110 -110
  71. package/src/entity-generation/sync-method.ts +222 -222
  72. package/src/entity-generation/update-method.ts +38 -38
  73. package/src/index.ts +77 -77
  74. package/src/rad-compile.ts +88 -88
  75. package/src/rad-wasm-build.ts +102 -102
  76. package/src/rad.ts +101 -101
  77. package/src/scan.ts +89 -89
  78. package/src/schema/camel-to-pastel.ts +3 -3
  79. package/src/schema/capitalise-first-letter.ts +3 -3
  80. package/src/schema/extract-response.ts +38 -38
  81. package/src/schema/fetch-schema.ts +12 -12
  82. package/src/schema/remove-non-model-paths.ts +13 -13
  83. package/src/schema/to-camel-case.ts +7 -7
  84. package/src/schema/to-pastel-case.ts +6 -6
  85. package/src/schema-convert.ts +61 -61
  86. package/src/schema-entities.ts +401 -401
  87. package/src/schema-fetch.ts +82 -82
  88. package/src/schema-full.ts +38 -38
  89. package/src/schema-index.ts +29 -29
  90. package/src/schema-params.ts +116 -116
  91. package/src/seed.ts +297 -297
  92. package/sync/engine.ts +392 -392
  93. package/sync/hooks.ts +450 -450
  94. package/sync/index.ts +8 -8
  95. package/sync/persistence.ts +237 -237
  96. package/sync/provider.tsx +91 -91
  97. package/sync/registry.ts +36 -36
  98. package/sync/store.ts +126 -126
  99. package/sync/transactions.ts +300 -300
  100. package/sync/types.ts +94 -94
  101. package/test/compile.spec.ts +192 -192
  102. package/test/csv-schema.spec.ts +143 -143
  103. package/test/schema-entities.spec.ts +688 -688
  104. package/tsconfig.json +24 -24
  105. package/types.ts +18 -18
@@ -1,187 +1,187 @@
1
- import React, {
2
- createContext,
3
- ReactNode,
4
- useContext,
5
- useEffect,
6
- useMemo,
7
- useRef,
8
- useState,
9
- } from "react";
10
- import { API, configureApi, resetApi } from "../hooks/query/api";
11
- import {
12
- RadClient,
13
- RadEngine,
14
- RAStackProviderConfig,
15
- RadStatus,
16
- WarehouseSource,
17
- } from "./types";
18
- import { createEngine, createWasmAdapter, defaultEngineLoader } from "./wasm";
19
- import {
20
- debounce,
21
- downloadBlob,
22
- manifestResources,
23
- seedFromIceberg,
24
- WarehousePersistence,
25
- } from "./warehouse";
26
-
27
- const RadContext = createContext<RadClient | null>(null);
28
-
29
- /**
30
- * The one place a React app configures *where the API runs* — over HTTP
31
- * (`remote`), in the browser via WebAssembly (`local`), or in the browser
32
- * against S3 (`s3`). It reconfigures the shared axios client in place, so every
33
- * generated hook keeps working with no changes.
34
- *
35
- * ```tsx
36
- * <RAStackProvider mode="local" manifest={schema} warehouseBaseUrl="/data/warehouse/">
37
- * <App />
38
- * </RAStackProvider>
39
- * ```
40
- */
41
- export function RAStackProvider({
42
- children,
43
- ...config
44
- }: RAStackProviderConfig & { children: ReactNode }): JSX.Element {
45
- const [status, setStatus] = useState<RadStatus>("idle");
46
- const engineRef = useRef<RadEngine | null>(null);
47
- const persistRef = useRef<(() => void) & { flush: () => void }>();
48
-
49
- const setStatusSafe = (next: RadStatus) => {
50
- setStatus(next);
51
- config.onStatusChange?.(next);
52
- };
53
-
54
- useEffect(() => {
55
- let cancelled = false;
56
- let ejectToken: number | undefined;
57
-
58
- async function setup() {
59
- // Bearer-token interceptor works in every mode.
60
- if (config.getToken) {
61
- ejectToken = API.interceptors.request.use(async (request) => {
62
- const token = await config.getToken!();
63
- request.headers = request.headers || ({} as any);
64
- (request.headers as any).Authorization = `Bearer ${token}`;
65
- return request;
66
- });
67
- }
68
-
69
- if (config.mode === "remote") {
70
- configureApi({ baseURL: config.baseURL ?? "", adapter: null });
71
- if (config.baseURL === undefined) resetApi();
72
- setStatusSafe("ready");
73
- return;
74
- }
75
-
76
- // -- WASM modes (local / s3) --
77
- if (!config.manifest) {
78
- throw new Error("RAStackProvider: `manifest` is required in local/s3 mode.");
79
- }
80
-
81
- setStatusSafe("loading");
82
- const loader = config.loadEngine ?? defaultEngineLoader;
83
- const mod = await loader();
84
- if (cancelled) return;
85
- const engine = createEngine(mod, config.manifest, config.openapi);
86
-
87
- // Warehouse persistence: IndexedDB for `local`, an S3 sink for `s3`.
88
- const persistence = pickPersistence(config);
89
-
90
- setStatusSafe("seeding");
91
- // A persisted local copy (with the user's edits) wins; otherwise seed
92
- // from the committed Iceberg warehouse — a directory of files walked over
93
- // HTTP, not a blob.
94
- const existing = await persistence.load();
95
- if (existing) {
96
- await engine.importWarehouse(existing);
97
- } else {
98
- const base = config.warehouseBaseUrl ?? "/data/warehouse/";
99
- await seedFromIceberg(engine, base, manifestResources(config.manifest));
100
- }
101
- if (cancelled) return;
102
-
103
- const persist = debounce(() => {
104
- void engine
105
- .exportWarehouse()
106
- .then((blob) => persistence.save(blob))
107
- .catch((error) => config.onError?.(error));
108
- }, 400);
109
- persistRef.current = persist;
110
-
111
- engineRef.current = engine;
112
- configureApi({ baseURL: "", adapter: createWasmAdapter(engine, persist) });
113
- setStatusSafe("ready");
114
- }
115
-
116
- setup().catch((error) => {
117
- if (cancelled) return;
118
- config.onError?.(error);
119
- setStatusSafe("error");
120
- });
121
-
122
- return () => {
123
- cancelled = true;
124
- persistRef.current?.flush();
125
- if (ejectToken !== undefined) API.interceptors.request.eject(ejectToken);
126
- resetApi();
127
- engineRef.current = null;
128
- };
129
- // eslint-disable-next-line react-hooks/exhaustive-deps
130
- }, [config.mode, config.baseURL, config.warehouseBaseUrl]);
131
-
132
- const client = useMemo<RadClient>(
133
- () => ({
134
- mode: config.mode,
135
- status,
136
- get engine() {
137
- return engineRef.current;
138
- },
139
- async exportWarehouse() {
140
- if (!engineRef.current) {
141
- throw new Error("exportWarehouse is only available in local/s3 mode.");
142
- }
143
- return engineRef.current.exportWarehouse();
144
- },
145
- async downloadWarehouse(filename = "warehouse-snapshot.json") {
146
- const blob = await this.exportWarehouse();
147
- downloadBlob(blob, filename);
148
- },
149
- async persistNow() {
150
- persistRef.current?.flush();
151
- },
152
- }),
153
- [config.mode, status],
154
- );
155
-
156
- return <RadContext.Provider value={client}>{children}</RadContext.Provider>;
157
- }
158
-
159
- function pickPersistence(config: RAStackProviderConfig): WarehouseSource {
160
- if (config.mode === "s3") {
161
- if (!config.warehouse) {
162
- throw new Error(
163
- "RAStackProvider: `warehouse` (a WarehouseSource over S3) is required in s3 mode.",
164
- );
165
- }
166
- return config.warehouse;
167
- }
168
- if (config.persist === false) {
169
- return { load: async () => null, save: async () => {} };
170
- }
171
- const store = new WarehousePersistence(config.databaseName || "rad-warehouse");
172
- return { load: () => store.load(), save: (blob) => store.save(blob) };
173
- }
174
-
175
- /** Access the imperative Rad client (engine, status, warehouse helpers). */
176
- export function useRadClient(): RadClient {
177
- const client = useContext(RadContext);
178
- if (!client) {
179
- throw new Error("useRadClient must be used within a <RAStackProvider>.");
180
- }
181
- return client;
182
- }
183
-
184
- /** Just the current status — handy for a loading gate while WASM boots. */
185
- export function useRadStatus(): RadStatus {
186
- return useRadClient().status;
187
- }
1
+ import React, {
2
+ createContext,
3
+ ReactNode,
4
+ useContext,
5
+ useEffect,
6
+ useMemo,
7
+ useRef,
8
+ useState,
9
+ } from "react";
10
+ import { API, configureApi, resetApi } from "../hooks/query/api";
11
+ import {
12
+ RadClient,
13
+ RadEngine,
14
+ RAStackProviderConfig,
15
+ RadStatus,
16
+ WarehouseSource,
17
+ } from "./types";
18
+ import { createEngine, createWasmAdapter, defaultEngineLoader } from "./wasm";
19
+ import {
20
+ debounce,
21
+ downloadBlob,
22
+ manifestResources,
23
+ seedFromIceberg,
24
+ WarehousePersistence,
25
+ } from "./warehouse";
26
+
27
+ const RadContext = createContext<RadClient | null>(null);
28
+
29
+ /**
30
+ * The one place a React app configures *where the API runs* — over HTTP
31
+ * (`remote`), in the browser via WebAssembly (`local`), or in the browser
32
+ * against S3 (`s3`). It reconfigures the shared axios client in place, so every
33
+ * generated hook keeps working with no changes.
34
+ *
35
+ * ```tsx
36
+ * <RAStackProvider mode="local" manifest={schema} warehouseBaseUrl="/data/warehouse/">
37
+ * <App />
38
+ * </RAStackProvider>
39
+ * ```
40
+ */
41
+ export function RAStackProvider({
42
+ children,
43
+ ...config
44
+ }: RAStackProviderConfig & { children: ReactNode }): JSX.Element {
45
+ const [status, setStatus] = useState<RadStatus>("idle");
46
+ const engineRef = useRef<RadEngine | null>(null);
47
+ const persistRef = useRef<(() => void) & { flush: () => void }>();
48
+
49
+ const setStatusSafe = (next: RadStatus) => {
50
+ setStatus(next);
51
+ config.onStatusChange?.(next);
52
+ };
53
+
54
+ useEffect(() => {
55
+ let cancelled = false;
56
+ let ejectToken: number | undefined;
57
+
58
+ async function setup() {
59
+ // Bearer-token interceptor works in every mode.
60
+ if (config.getToken) {
61
+ ejectToken = API.interceptors.request.use(async (request) => {
62
+ const token = await config.getToken!();
63
+ request.headers = request.headers || ({} as any);
64
+ (request.headers as any).Authorization = `Bearer ${token}`;
65
+ return request;
66
+ });
67
+ }
68
+
69
+ if (config.mode === "remote") {
70
+ configureApi({ baseURL: config.baseURL ?? "", adapter: null });
71
+ if (config.baseURL === undefined) resetApi();
72
+ setStatusSafe("ready");
73
+ return;
74
+ }
75
+
76
+ // -- WASM modes (local / s3) --
77
+ if (!config.manifest) {
78
+ throw new Error("RAStackProvider: `manifest` is required in local/s3 mode.");
79
+ }
80
+
81
+ setStatusSafe("loading");
82
+ const loader = config.loadEngine ?? defaultEngineLoader;
83
+ const mod = await loader();
84
+ if (cancelled) return;
85
+ const engine = createEngine(mod, config.manifest, config.openapi);
86
+
87
+ // Warehouse persistence: IndexedDB for `local`, an S3 sink for `s3`.
88
+ const persistence = pickPersistence(config);
89
+
90
+ setStatusSafe("seeding");
91
+ // A persisted local copy (with the user's edits) wins; otherwise seed
92
+ // from the committed Iceberg warehouse — a directory of files walked over
93
+ // HTTP, not a blob.
94
+ const existing = await persistence.load();
95
+ if (existing) {
96
+ await engine.importWarehouse(existing);
97
+ } else {
98
+ const base = config.warehouseBaseUrl ?? "/data/warehouse/";
99
+ await seedFromIceberg(engine, base, manifestResources(config.manifest));
100
+ }
101
+ if (cancelled) return;
102
+
103
+ const persist = debounce(() => {
104
+ void engine
105
+ .exportWarehouse()
106
+ .then((blob) => persistence.save(blob))
107
+ .catch((error) => config.onError?.(error));
108
+ }, 400);
109
+ persistRef.current = persist;
110
+
111
+ engineRef.current = engine;
112
+ configureApi({ baseURL: "", adapter: createWasmAdapter(engine, persist) });
113
+ setStatusSafe("ready");
114
+ }
115
+
116
+ setup().catch((error) => {
117
+ if (cancelled) return;
118
+ config.onError?.(error);
119
+ setStatusSafe("error");
120
+ });
121
+
122
+ return () => {
123
+ cancelled = true;
124
+ persistRef.current?.flush();
125
+ if (ejectToken !== undefined) API.interceptors.request.eject(ejectToken);
126
+ resetApi();
127
+ engineRef.current = null;
128
+ };
129
+ // eslint-disable-next-line react-hooks/exhaustive-deps
130
+ }, [config.mode, config.baseURL, config.warehouseBaseUrl]);
131
+
132
+ const client = useMemo<RadClient>(
133
+ () => ({
134
+ mode: config.mode,
135
+ status,
136
+ get engine() {
137
+ return engineRef.current;
138
+ },
139
+ async exportWarehouse() {
140
+ if (!engineRef.current) {
141
+ throw new Error("exportWarehouse is only available in local/s3 mode.");
142
+ }
143
+ return engineRef.current.exportWarehouse();
144
+ },
145
+ async downloadWarehouse(filename = "warehouse-snapshot.json") {
146
+ const blob = await this.exportWarehouse();
147
+ downloadBlob(blob, filename);
148
+ },
149
+ async persistNow() {
150
+ persistRef.current?.flush();
151
+ },
152
+ }),
153
+ [config.mode, status],
154
+ );
155
+
156
+ return <RadContext.Provider value={client}>{children}</RadContext.Provider>;
157
+ }
158
+
159
+ function pickPersistence(config: RAStackProviderConfig): WarehouseSource {
160
+ if (config.mode === "s3") {
161
+ if (!config.warehouse) {
162
+ throw new Error(
163
+ "RAStackProvider: `warehouse` (a WarehouseSource over S3) is required in s3 mode.",
164
+ );
165
+ }
166
+ return config.warehouse;
167
+ }
168
+ if (config.persist === false) {
169
+ return { load: async () => null, save: async () => {} };
170
+ }
171
+ const store = new WarehousePersistence(config.databaseName || "rad-warehouse");
172
+ return { load: () => store.load(), save: (blob) => store.save(blob) };
173
+ }
174
+
175
+ /** Access the imperative Rad client (engine, status, warehouse helpers). */
176
+ export function useRadClient(): RadClient {
177
+ const client = useContext(RadContext);
178
+ if (!client) {
179
+ throw new Error("useRadClient must be used within a <RAStackProvider>.");
180
+ }
181
+ return client;
182
+ }
183
+
184
+ /** Just the current status — handy for a loading gate while WASM boots. */
185
+ export function useRadStatus(): RadStatus {
186
+ return useRadClient().status;
187
+ }
package/provider/types.ts CHANGED
@@ -1,114 +1,114 @@
1
- /**
2
- * Configuration for `<RAStackProvider>` — the single place a React app chooses
3
- * *where the API runs*:
4
- *
5
- * - `remote` — talk to the Rust API over HTTP (Lambda in prod, `rad serve`
6
- * locally). The classic client/server split.
7
- * - `local` — compile-free local development: the entire API runs in the
8
- * browser via WebAssembly over an in-memory Iceberg warehouse, seeded from a
9
- * repo `data/` folder and persisted to IndexedDB. No server, no network,
10
- * instant reads/writes.
11
- * - `s3` — full client mode: the same WASM engine, but the warehouse is
12
- * loaded from and saved back to S3, so a static site reads and writes the
13
- * object-storage database directly.
14
- */
15
- export type RadMode = "remote" | "local" | "s3";
16
-
17
- /** The subset of the WASM `RadApi` class the provider uses. */
18
- export interface RadEngine {
19
- handle(
20
- method: string,
21
- path: string,
22
- query: Record<string, string> | undefined,
23
- body: unknown,
24
- ): Promise<{ status: number; body: any }>;
25
- /** Seed one Iceberg file (metadata or `.parquet` data file) at its key. */
26
- seedFile(key: string, bytes: Uint8Array): Promise<void>;
27
- importWarehouse(blob: Uint8Array): Promise<void>;
28
- exportWarehouse(): Promise<Uint8Array>;
29
- }
30
-
31
- /** Constructor of the generated `RadApi` class. */
32
- export interface RadEngineCtor {
33
- new (manifestJson: string, openapiJson?: string): RadEngine;
34
- }
35
-
36
- /** What a WASM bundle loader must resolve to. */
37
- export interface RadWasmModule {
38
- /** wasm-pack `web` target exposes an init fn that fetches the `.wasm`. */
39
- default?: (input?: any) => Promise<any>;
40
- init?: (input?: any) => Promise<any>;
41
- RadApi: RadEngineCtor;
42
- }
43
-
44
- /** Load and initialise the compiled WASM engine module. */
45
- export type RadEngineLoader = () => Promise<RadWasmModule>;
46
-
47
- /** Where to read/write the warehouse blob in `s3` mode (or any custom sink). */
48
- export interface WarehouseSource {
49
- /** Fetch the current warehouse blob, or `null` if none exists yet. */
50
- load(): Promise<Uint8Array | null>;
51
- /** Persist the warehouse blob (called debounced after writes). */
52
- save(blob: Uint8Array): Promise<void>;
53
- }
54
-
55
- export interface RAStackProviderConfig {
56
- mode: RadMode;
57
-
58
- // -- remote mode --
59
- /** HTTP base URL. Defaults to `NEXT_PUBLIC_BASE_URL` or localhost:8000. */
60
- baseURL?: string;
61
- /** Attach a Bearer token to every request. */
62
- getToken?: () => string | Promise<string>;
63
-
64
- // -- local / s3 mode --
65
- /** `schema.rad.json` — object or JSON string. Required for WASM modes. */
66
- manifest?: object | string;
67
- /** `openapi.json` — object or JSON string. Optional (`/api/schema/`). */
68
- openapi?: object | string;
69
- /**
70
- * Base URL of the committed Iceberg warehouse (a *directory of files*, not a
71
- * blob) to seed `local` mode from. The provider walks the tree —
72
- * `version-hint.text` → snapshot metadata → `.parquet` data files — exactly
73
- * as the server walks it over object storage. Defaults to `/data/warehouse/`.
74
- * Ignored once a persisted local copy exists.
75
- */
76
- warehouseBaseUrl?: string;
77
- /** Persist the local warehouse to IndexedDB between reloads (default true). */
78
- persist?: boolean;
79
- /** IndexedDB database name for the local warehouse. */
80
- databaseName?: string;
81
- /** `s3` mode: how to load/save the warehouse blob from object storage. */
82
- warehouse?: WarehouseSource;
83
- /**
84
- * Override how the WASM module is loaded. Supply this from app code (where
85
- * your bundler can resolve the generated bundle) if the default loader can't
86
- * find it. Defaults to importing `rastack/wasm/rad_wasm.js`.
87
- */
88
- loadEngine?: RadEngineLoader;
89
-
90
- // -- lifecycle --
91
- onStatusChange?: (status: RadStatus) => void;
92
- onError?: (error: unknown) => void;
93
- }
94
-
95
- export type RadStatus =
96
- | "idle"
97
- | "loading" // fetching/instantiating the WASM engine
98
- | "seeding" // importing the warehouse
99
- | "ready"
100
- | "error";
101
-
102
- /** Imperative handle exposed via `useRadClient()`. */
103
- export interface RadClient {
104
- mode: RadMode;
105
- status: RadStatus;
106
- /** The live WASM engine (null in `remote` mode). */
107
- engine: RadEngine | null;
108
- /** Snapshot the local warehouse to a portable blob (WASM modes only). */
109
- exportWarehouse(): Promise<Uint8Array>;
110
- /** Download that snapshot as a file — a portable backup of local edits. */
111
- downloadWarehouse(filename?: string): Promise<void>;
112
- /** Force-persist now (IndexedDB and/or the `s3` sink). */
113
- persistNow(): Promise<void>;
114
- }
1
+ /**
2
+ * Configuration for `<RAStackProvider>` — the single place a React app chooses
3
+ * *where the API runs*:
4
+ *
5
+ * - `remote` — talk to the Rust API over HTTP (Lambda in prod, `rad serve`
6
+ * locally). The classic client/server split.
7
+ * - `local` — compile-free local development: the entire API runs in the
8
+ * browser via WebAssembly over an in-memory Iceberg warehouse, seeded from a
9
+ * repo `data/` folder and persisted to IndexedDB. No server, no network,
10
+ * instant reads/writes.
11
+ * - `s3` — full client mode: the same WASM engine, but the warehouse is
12
+ * loaded from and saved back to S3, so a static site reads and writes the
13
+ * object-storage database directly.
14
+ */
15
+ export type RadMode = "remote" | "local" | "s3";
16
+
17
+ /** The subset of the WASM `RadApi` class the provider uses. */
18
+ export interface RadEngine {
19
+ handle(
20
+ method: string,
21
+ path: string,
22
+ query: Record<string, string> | undefined,
23
+ body: unknown,
24
+ ): Promise<{ status: number; body: any }>;
25
+ /** Seed one Iceberg file (metadata or `.parquet` data file) at its key. */
26
+ seedFile(key: string, bytes: Uint8Array): Promise<void>;
27
+ importWarehouse(blob: Uint8Array): Promise<void>;
28
+ exportWarehouse(): Promise<Uint8Array>;
29
+ }
30
+
31
+ /** Constructor of the generated `RadApi` class. */
32
+ export interface RadEngineCtor {
33
+ new (manifestJson: string, openapiJson?: string): RadEngine;
34
+ }
35
+
36
+ /** What a WASM bundle loader must resolve to. */
37
+ export interface RadWasmModule {
38
+ /** wasm-pack `web` target exposes an init fn that fetches the `.wasm`. */
39
+ default?: (input?: any) => Promise<any>;
40
+ init?: (input?: any) => Promise<any>;
41
+ RadApi: RadEngineCtor;
42
+ }
43
+
44
+ /** Load and initialise the compiled WASM engine module. */
45
+ export type RadEngineLoader = () => Promise<RadWasmModule>;
46
+
47
+ /** Where to read/write the warehouse blob in `s3` mode (or any custom sink). */
48
+ export interface WarehouseSource {
49
+ /** Fetch the current warehouse blob, or `null` if none exists yet. */
50
+ load(): Promise<Uint8Array | null>;
51
+ /** Persist the warehouse blob (called debounced after writes). */
52
+ save(blob: Uint8Array): Promise<void>;
53
+ }
54
+
55
+ export interface RAStackProviderConfig {
56
+ mode: RadMode;
57
+
58
+ // -- remote mode --
59
+ /** HTTP base URL. Defaults to `NEXT_PUBLIC_BASE_URL` or localhost:8000. */
60
+ baseURL?: string;
61
+ /** Attach a Bearer token to every request. */
62
+ getToken?: () => string | Promise<string>;
63
+
64
+ // -- local / s3 mode --
65
+ /** `schema.rad.json` — object or JSON string. Required for WASM modes. */
66
+ manifest?: object | string;
67
+ /** `openapi.json` — object or JSON string. Optional (`/api/schema/`). */
68
+ openapi?: object | string;
69
+ /**
70
+ * Base URL of the committed Iceberg warehouse (a *directory of files*, not a
71
+ * blob) to seed `local` mode from. The provider walks the tree —
72
+ * `version-hint.text` → snapshot metadata → `.parquet` data files — exactly
73
+ * as the server walks it over object storage. Defaults to `/data/warehouse/`.
74
+ * Ignored once a persisted local copy exists.
75
+ */
76
+ warehouseBaseUrl?: string;
77
+ /** Persist the local warehouse to IndexedDB between reloads (default true). */
78
+ persist?: boolean;
79
+ /** IndexedDB database name for the local warehouse. */
80
+ databaseName?: string;
81
+ /** `s3` mode: how to load/save the warehouse blob from object storage. */
82
+ warehouse?: WarehouseSource;
83
+ /**
84
+ * Override how the WASM module is loaded. Supply this from app code (where
85
+ * your bundler can resolve the generated bundle) if the default loader can't
86
+ * find it. Defaults to importing `rastack/wasm/rad_wasm.js`.
87
+ */
88
+ loadEngine?: RadEngineLoader;
89
+
90
+ // -- lifecycle --
91
+ onStatusChange?: (status: RadStatus) => void;
92
+ onError?: (error: unknown) => void;
93
+ }
94
+
95
+ export type RadStatus =
96
+ | "idle"
97
+ | "loading" // fetching/instantiating the WASM engine
98
+ | "seeding" // importing the warehouse
99
+ | "ready"
100
+ | "error";
101
+
102
+ /** Imperative handle exposed via `useRadClient()`. */
103
+ export interface RadClient {
104
+ mode: RadMode;
105
+ status: RadStatus;
106
+ /** The live WASM engine (null in `remote` mode). */
107
+ engine: RadEngine | null;
108
+ /** Snapshot the local warehouse to a portable blob (WASM modes only). */
109
+ exportWarehouse(): Promise<Uint8Array>;
110
+ /** Download that snapshot as a file — a portable backup of local edits. */
111
+ downloadWarehouse(filename?: string): Promise<void>;
112
+ /** Force-persist now (IndexedDB and/or the `s3` sink). */
113
+ persistNow(): Promise<void>;
114
+ }