@archlast/client 0.0.1
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/LICENSE +21 -0
- package/README.md +60 -0
- package/dist/admin/index.cjs +93 -0
- package/dist/admin/index.cjs.map +1 -0
- package/dist/admin/index.d.cts +51 -0
- package/dist/admin/index.d.ts +51 -0
- package/dist/admin/index.js +59 -0
- package/dist/admin/index.js.map +1 -0
- package/dist/auth/index.cjs +174 -0
- package/dist/auth/index.cjs.map +1 -0
- package/dist/auth/index.d.cts +128 -0
- package/dist/auth/index.d.ts +128 -0
- package/dist/auth/index.js +141 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/client.cjs +677 -0
- package/dist/client.cjs.map +1 -0
- package/dist/client.d.cts +84 -0
- package/dist/client.d.ts +84 -0
- package/dist/client.js +642 -0
- package/dist/client.js.map +1 -0
- package/dist/function-reference.cjs +50 -0
- package/dist/function-reference.cjs.map +1 -0
- package/dist/function-reference.d.cts +22 -0
- package/dist/function-reference.d.ts +22 -0
- package/dist/function-reference.js +24 -0
- package/dist/function-reference.js.map +1 -0
- package/dist/index.cjs +1163 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +12 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.js +1111 -0
- package/dist/index.js.map +1 -0
- package/dist/react.cjs +455 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +137 -0
- package/dist/react.d.ts +137 -0
- package/dist/react.js +410 -0
- package/dist/react.js.map +1 -0
- package/dist/storage/index.cjs +150 -0
- package/dist/storage/index.cjs.map +1 -0
- package/dist/storage/index.d.cts +59 -0
- package/dist/storage/index.d.ts +59 -0
- package/dist/storage/index.js +117 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/trpc.cjs +66 -0
- package/dist/trpc.cjs.map +1 -0
- package/dist/trpc.d.cts +59 -0
- package/dist/trpc.d.ts +59 -0
- package/dist/trpc.js +41 -0
- package/dist/trpc.js.map +1 -0
- package/package.json +90 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,1163 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
|
|
9
|
+
var __export = (target, all) => {
|
|
10
|
+
for (var name in all)
|
|
11
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
+
};
|
|
13
|
+
var __copyProps = (to, from, except, desc) => {
|
|
14
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
+
for (let key of __getOwnPropNames(from))
|
|
16
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
+
}
|
|
19
|
+
return to;
|
|
20
|
+
};
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
+
mod
|
|
28
|
+
));
|
|
29
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
+
var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
|
|
31
|
+
|
|
32
|
+
// src/index.ts
|
|
33
|
+
var index_exports = {};
|
|
34
|
+
__export(index_exports, {
|
|
35
|
+
ArchlastAuthClient: () => ArchlastAuthClient,
|
|
36
|
+
ArchlastClient: () => ArchlastClient,
|
|
37
|
+
ArchlastContext: () => ArchlastContext,
|
|
38
|
+
ArchlastProvider: () => ArchlastProvider,
|
|
39
|
+
StorageClient: () => StorageClient,
|
|
40
|
+
createArchlastTRPCClient: () => createArchlastTRPCClient,
|
|
41
|
+
makeFunctionReference: () => makeFunctionReference,
|
|
42
|
+
makeRpcReference: () => makeRpcReference,
|
|
43
|
+
useArchlast: () => useArchlast,
|
|
44
|
+
useAuth: () => useAuth,
|
|
45
|
+
useClientPagination: () => useClientPagination,
|
|
46
|
+
useDownload: () => useDownload,
|
|
47
|
+
useMutation: () => useMutation,
|
|
48
|
+
usePagination: () => usePagination,
|
|
49
|
+
useQuery: () => useQuery,
|
|
50
|
+
useRequestPasswordReset: () => useRequestPasswordReset,
|
|
51
|
+
useResetPassword: () => useResetPassword,
|
|
52
|
+
useStorageDelete: () => useStorageDelete,
|
|
53
|
+
useStorageDeleteFile: () => useStorageDeleteFile,
|
|
54
|
+
useStorageHelpers: () => useStorageHelpers,
|
|
55
|
+
useStorageList: () => useStorageList,
|
|
56
|
+
useStorageMetadata: () => useStorageMetadata,
|
|
57
|
+
useStorageUpload: () => useStorageUpload,
|
|
58
|
+
useUpload: () => useUpload
|
|
59
|
+
});
|
|
60
|
+
module.exports = __toCommonJS(index_exports);
|
|
61
|
+
|
|
62
|
+
// src/auth/index.ts
|
|
63
|
+
var import_axios = __toESM(require("axios"), 1);
|
|
64
|
+
function resolveBaseUrl(explicit) {
|
|
65
|
+
if (explicit && explicit.trim()) return explicit.replace(/\/+$/, "");
|
|
66
|
+
if (typeof window !== "undefined" && window.location?.origin) return window.location.origin;
|
|
67
|
+
return "";
|
|
68
|
+
}
|
|
69
|
+
var ArchlastAuthClient = class {
|
|
70
|
+
constructor(options = {}) {
|
|
71
|
+
__publicField(this, "baseUrl");
|
|
72
|
+
__publicField(this, "axios");
|
|
73
|
+
__publicField(this, "appId");
|
|
74
|
+
__publicField(this, "apiKey");
|
|
75
|
+
this.baseUrl = resolveBaseUrl(options.baseUrl);
|
|
76
|
+
this.appId = options.appId;
|
|
77
|
+
this.apiKey = options.apiKey;
|
|
78
|
+
this.axios = import_axios.default.create({
|
|
79
|
+
baseURL: this.baseUrl,
|
|
80
|
+
withCredentials: !options.apiKey,
|
|
81
|
+
// Only use cookies if no API key
|
|
82
|
+
headers: {
|
|
83
|
+
"content-type": "application/json",
|
|
84
|
+
...this.appId ? { "x-archlast-app-id": this.appId } : {},
|
|
85
|
+
...options.apiKey ? { "x-api-key": options.apiKey } : {}
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Get current authentication state
|
|
91
|
+
* Uses Better-Auth's getSession endpoint
|
|
92
|
+
*/
|
|
93
|
+
async getState() {
|
|
94
|
+
try {
|
|
95
|
+
const response = await this.axios.get("/api/auth/get-session");
|
|
96
|
+
const { user, session } = response.data;
|
|
97
|
+
return {
|
|
98
|
+
user,
|
|
99
|
+
session,
|
|
100
|
+
isAuthenticated: !!user
|
|
101
|
+
};
|
|
102
|
+
} catch (error) {
|
|
103
|
+
return {
|
|
104
|
+
user: null,
|
|
105
|
+
session: null,
|
|
106
|
+
isAuthenticated: false
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Sign up new user
|
|
112
|
+
* Uses Better-Auth's email signUp endpoint
|
|
113
|
+
*/
|
|
114
|
+
async signUp(input) {
|
|
115
|
+
const response = await this.axios.post("/api/auth/sign-up/email", {
|
|
116
|
+
email: input.email,
|
|
117
|
+
password: input.password,
|
|
118
|
+
name: input.name,
|
|
119
|
+
username: input.username
|
|
120
|
+
});
|
|
121
|
+
return {
|
|
122
|
+
user: response.data.user,
|
|
123
|
+
session: null,
|
|
124
|
+
// Session is managed via cookies
|
|
125
|
+
isAuthenticated: !!response.data.user
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Sign in with email/username and password
|
|
130
|
+
* Uses Better-Auth's credential sign-in endpoint
|
|
131
|
+
*/
|
|
132
|
+
async signIn(input) {
|
|
133
|
+
if (!input.email && !input.username) {
|
|
134
|
+
throw new Error("Either email or username is required");
|
|
135
|
+
}
|
|
136
|
+
const response = await this.axios.post("/api/auth/sign-in/email", {
|
|
137
|
+
email: input.email,
|
|
138
|
+
username: input.username,
|
|
139
|
+
password: input.password
|
|
140
|
+
});
|
|
141
|
+
return {
|
|
142
|
+
user: response.data.user,
|
|
143
|
+
session: null,
|
|
144
|
+
// Session is managed via cookies
|
|
145
|
+
isAuthenticated: !!response.data.user
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Sign out and revoke session
|
|
150
|
+
* Uses Better-Auth's sign-out endpoint
|
|
151
|
+
*/
|
|
152
|
+
async signOut() {
|
|
153
|
+
const response = await this.axios.post(
|
|
154
|
+
"/api/auth/sign-out",
|
|
155
|
+
{},
|
|
156
|
+
{ withCredentials: true }
|
|
157
|
+
);
|
|
158
|
+
return response.data;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Request password reset email
|
|
162
|
+
* Uses Better-Auth's password reset flow
|
|
163
|
+
*/
|
|
164
|
+
async requestPasswordReset(email, callbackURL) {
|
|
165
|
+
const response = await this.axios.post("/api/auth/forgot-password", {
|
|
166
|
+
email,
|
|
167
|
+
redirectTo: callbackURL
|
|
168
|
+
});
|
|
169
|
+
return response.data;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Reset password with token
|
|
173
|
+
* Uses Better-Auth's reset password endpoint
|
|
174
|
+
*/
|
|
175
|
+
async resetPassword(token, password) {
|
|
176
|
+
const response = await this.axios.post("/api/auth/reset-password", {
|
|
177
|
+
token,
|
|
178
|
+
password
|
|
179
|
+
});
|
|
180
|
+
return response.data;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Verify email with token
|
|
184
|
+
* Uses Better-Auth's email verification endpoint
|
|
185
|
+
*/
|
|
186
|
+
async verifyEmail(token) {
|
|
187
|
+
const response = await this.axios.post("/api/auth/verify-email", {
|
|
188
|
+
token
|
|
189
|
+
});
|
|
190
|
+
return response.data;
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
// src/storage/index.ts
|
|
195
|
+
var import_axios2 = __toESM(require("axios"), 1);
|
|
196
|
+
var StorageClient = class {
|
|
197
|
+
constructor(baseUrl, appId, options) {
|
|
198
|
+
__publicField(this, "axios");
|
|
199
|
+
this.axios = import_axios2.default.create({
|
|
200
|
+
baseURL: baseUrl,
|
|
201
|
+
withCredentials: !options?.apiKey,
|
|
202
|
+
// Only use cookies if no API key
|
|
203
|
+
headers: {
|
|
204
|
+
...appId ? { "x-archlast-app-id": appId } : {},
|
|
205
|
+
...options?.apiKey ? { "x-api-key": options.apiKey } : {}
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Upload a file
|
|
211
|
+
*/
|
|
212
|
+
async upload(file, contentType) {
|
|
213
|
+
let body = file;
|
|
214
|
+
let headers = {};
|
|
215
|
+
if (file instanceof File) {
|
|
216
|
+
const formData = new FormData();
|
|
217
|
+
formData.append("file", file);
|
|
218
|
+
body = formData;
|
|
219
|
+
} else {
|
|
220
|
+
if (contentType) {
|
|
221
|
+
headers["Content-Type"] = contentType;
|
|
222
|
+
}
|
|
223
|
+
if (file instanceof Uint8Array) {
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
const response = await this.axios.post(
|
|
227
|
+
"/_archlast/storage/upload",
|
|
228
|
+
body,
|
|
229
|
+
{
|
|
230
|
+
headers
|
|
231
|
+
}
|
|
232
|
+
);
|
|
233
|
+
const data = response.data;
|
|
234
|
+
if (!data.id) throw new Error("Upload failed: No ID returned");
|
|
235
|
+
return this.hydrate(data);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* List files
|
|
239
|
+
*/
|
|
240
|
+
async list(limit = 20, offset = 0) {
|
|
241
|
+
const response = await this.axios.get("/_archlast/storage/list", {
|
|
242
|
+
params: { limit, offset }
|
|
243
|
+
});
|
|
244
|
+
return {
|
|
245
|
+
items: response.data.items.map((item) => this.hydrate(item))
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Get file metadata
|
|
250
|
+
*/
|
|
251
|
+
async getMetadata(id) {
|
|
252
|
+
const response = await this.axios.get(`/_archlast/storage/files/${id}`);
|
|
253
|
+
return this.hydrate(response.data);
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Generate a presigned download URL
|
|
257
|
+
*/
|
|
258
|
+
async presign(id, expiresInSeconds = 300) {
|
|
259
|
+
const response = await this.axios.post("/_archlast/storage/presign", {
|
|
260
|
+
id,
|
|
261
|
+
expiresInSeconds
|
|
262
|
+
});
|
|
263
|
+
return response.data;
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Delete file
|
|
267
|
+
*/
|
|
268
|
+
async delete(id) {
|
|
269
|
+
const response = await this.axios.delete(
|
|
270
|
+
`/_archlast/storage/files/${id}`
|
|
271
|
+
);
|
|
272
|
+
return response.data;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Get public URL for a file
|
|
276
|
+
*/
|
|
277
|
+
getPublicUrl(id) {
|
|
278
|
+
const base = this.axios.defaults.baseURL?.replace(/\/+$/, "") || "";
|
|
279
|
+
return `${base}/_storage/${id}`;
|
|
280
|
+
}
|
|
281
|
+
getDownloadUrl(id) {
|
|
282
|
+
const base = this.axios.defaults.baseURL?.replace(/\/+$/, "") || "";
|
|
283
|
+
return `${base}/_archlast/storage/files/${id}/download`;
|
|
284
|
+
}
|
|
285
|
+
hydrate(file) {
|
|
286
|
+
if (!file.id) throw new Error("Invalid file object: missing id");
|
|
287
|
+
return {
|
|
288
|
+
id: file.id,
|
|
289
|
+
name: file.name ?? file.fileName ?? "Untitled",
|
|
290
|
+
// Handle aliasing
|
|
291
|
+
fileName: file.fileName ?? file.name ?? null,
|
|
292
|
+
url: file.url ?? this.getPublicUrl(file.id),
|
|
293
|
+
downloadUrl: file.downloadUrl ?? this.getDownloadUrl(file.id),
|
|
294
|
+
hash: file.hash ?? "",
|
|
295
|
+
contentType: file.contentType ?? "application/octet-stream",
|
|
296
|
+
size: file.size ?? 0,
|
|
297
|
+
createdAt: file.createdAt ?? Date.now(),
|
|
298
|
+
updatedAt: file.updatedAt ?? Date.now(),
|
|
299
|
+
refCount: file.refCount ?? 1
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
|
|
304
|
+
// src/admin/index.ts
|
|
305
|
+
var import_axios3 = __toESM(require("axios"), 1);
|
|
306
|
+
var AdminAuthClient = class {
|
|
307
|
+
constructor(baseUrl, options) {
|
|
308
|
+
__publicField(this, "axios");
|
|
309
|
+
this.axios = import_axios3.default.create({
|
|
310
|
+
baseURL: baseUrl,
|
|
311
|
+
withCredentials: !options?.apiKey,
|
|
312
|
+
headers: {
|
|
313
|
+
...options?.apiKey ? { "x-api-key": options.apiKey } : {}
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
async signIn(email, password) {
|
|
318
|
+
const response = await this.axios.post("/_archlast/admin/auth/sign-in", {
|
|
319
|
+
email,
|
|
320
|
+
password
|
|
321
|
+
});
|
|
322
|
+
return response.data;
|
|
323
|
+
}
|
|
324
|
+
async signOut() {
|
|
325
|
+
const response = await this.axios.post("/_archlast/admin/auth/sign-out");
|
|
326
|
+
return response.data;
|
|
327
|
+
}
|
|
328
|
+
async getProfile() {
|
|
329
|
+
const response = await this.axios.get("/_archlast/admin/auth/me");
|
|
330
|
+
return response.data;
|
|
331
|
+
}
|
|
332
|
+
async getSessions() {
|
|
333
|
+
const response = await this.axios.get("/_archlast/admin/auth/sessions");
|
|
334
|
+
return response.data;
|
|
335
|
+
}
|
|
336
|
+
async revokeSession(sessionId) {
|
|
337
|
+
const response = await this.axios.post("/_archlast/admin/auth/revoke-session", {
|
|
338
|
+
sessionId
|
|
339
|
+
});
|
|
340
|
+
return response.data;
|
|
341
|
+
}
|
|
342
|
+
async signOutAll() {
|
|
343
|
+
const response = await this.axios.post("/_archlast/admin/auth/sign-out-all");
|
|
344
|
+
return response.data;
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
var AdminClient = class {
|
|
348
|
+
// Add other admin clients here (users, tenants, etc.)
|
|
349
|
+
constructor(baseUrl, options) {
|
|
350
|
+
__publicField(this, "auth");
|
|
351
|
+
this.auth = new AdminAuthClient(baseUrl, options);
|
|
352
|
+
}
|
|
353
|
+
};
|
|
354
|
+
|
|
355
|
+
// src/client.ts
|
|
356
|
+
var ArchlastClient = class {
|
|
357
|
+
/**
|
|
358
|
+
* @param url WebSocket URL
|
|
359
|
+
* @param httpUrl Backend HTTP URL (used for WS derivation fallback)
|
|
360
|
+
* @param appId App ID for session isolation
|
|
361
|
+
* @param authUrl Optional same-origin URL for auth requests (to avoid cross-origin cookie issues)
|
|
362
|
+
* @param options Configuration options
|
|
363
|
+
*/
|
|
364
|
+
constructor(url, httpUrl, appId, authUrl, options = {}) {
|
|
365
|
+
__publicField(this, "ws", null);
|
|
366
|
+
__publicField(this, "url");
|
|
367
|
+
__publicField(this, "listeners", /* @__PURE__ */ new Map());
|
|
368
|
+
__publicField(this, "subscriptions", /* @__PURE__ */ new Map());
|
|
369
|
+
__publicField(this, "pendingMutations", /* @__PURE__ */ new Map());
|
|
370
|
+
__publicField(this, "messageQueue", []);
|
|
371
|
+
__publicField(this, "isConnected", false);
|
|
372
|
+
__publicField(this, "reconnectAttempts", 0);
|
|
373
|
+
__publicField(this, "maxReconnectAttempts", 5);
|
|
374
|
+
__publicField(this, "reconnectDelay", 1e3);
|
|
375
|
+
__publicField(this, "reconnectTimeout", null);
|
|
376
|
+
__publicField(this, "isExplicitlyClosed", false);
|
|
377
|
+
__publicField(this, "sessionId", null);
|
|
378
|
+
// Event listeners
|
|
379
|
+
__publicField(this, "connectionListeners", /* @__PURE__ */ new Set());
|
|
380
|
+
__publicField(this, "disconnectionListeners", /* @__PURE__ */ new Set());
|
|
381
|
+
__publicField(this, "errorListeners", /* @__PURE__ */ new Set());
|
|
382
|
+
__publicField(this, "auth");
|
|
383
|
+
__publicField(this, "storage");
|
|
384
|
+
__publicField(this, "admin");
|
|
385
|
+
__publicField(this, "baseUrl");
|
|
386
|
+
__publicField(this, "appId");
|
|
387
|
+
__publicField(this, "isAdmin");
|
|
388
|
+
__publicField(this, "apiKey");
|
|
389
|
+
__publicField(this, "betterAuthCookies");
|
|
390
|
+
__publicField(this, "heartbeatInterval", null);
|
|
391
|
+
this.url = url;
|
|
392
|
+
this.appId = appId;
|
|
393
|
+
this.isAdmin = options.isAdmin ?? false;
|
|
394
|
+
this.apiKey = options.apiKey;
|
|
395
|
+
this.betterAuthCookies = options.betterAuthCookies;
|
|
396
|
+
const derivedHttpUrl = httpUrl !== void 0 ? httpUrl : url.replace(/^ws/, "http");
|
|
397
|
+
const authBaseUrl = authUrl !== void 0 ? authUrl : derivedHttpUrl;
|
|
398
|
+
this.baseUrl = authBaseUrl;
|
|
399
|
+
this.auth = new ArchlastAuthClient({ baseUrl: authBaseUrl, appId, apiKey: options.apiKey });
|
|
400
|
+
this.storage = new StorageClient(authBaseUrl, appId, { apiKey: options.apiKey });
|
|
401
|
+
this.admin = new AdminClient(authBaseUrl, { apiKey: options.apiKey });
|
|
402
|
+
if (options.autoConnect !== false) {
|
|
403
|
+
this.connect();
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Update Better-Auth cookies (call this when session changes)
|
|
408
|
+
*/
|
|
409
|
+
setBetterAuthCookies(cookies) {
|
|
410
|
+
this.betterAuthCookies = cookies;
|
|
411
|
+
if (this.isConnected) {
|
|
412
|
+
this.connect();
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Update Better-Auth API key (call this when API key changes)
|
|
417
|
+
*/
|
|
418
|
+
setApiKey(apiKey) {
|
|
419
|
+
this.apiKey = apiKey;
|
|
420
|
+
if (this.isConnected) {
|
|
421
|
+
this.connect();
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
connect() {
|
|
425
|
+
if (typeof window === "undefined") {
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
this.isExplicitlyClosed = false;
|
|
429
|
+
if (this.ws) {
|
|
430
|
+
if (this.ws.readyState === WebSocket.OPEN || this.ws.readyState === WebSocket.CONNECTING) {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
try {
|
|
434
|
+
this.ws.close();
|
|
435
|
+
} catch (e) {
|
|
436
|
+
}
|
|
437
|
+
this.ws = null;
|
|
438
|
+
}
|
|
439
|
+
try {
|
|
440
|
+
this.ws = new WebSocket(this.url);
|
|
441
|
+
} catch (e) {
|
|
442
|
+
this.handleClose();
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
this.ws.onopen = async () => {
|
|
446
|
+
console.log("Connected to Archlast Server");
|
|
447
|
+
this.isConnected = true;
|
|
448
|
+
this.reconnectAttempts = 0;
|
|
449
|
+
const headers = {};
|
|
450
|
+
if (this.appId) {
|
|
451
|
+
headers["x-archlast-app-id"] = this.appId;
|
|
452
|
+
}
|
|
453
|
+
if (this.apiKey) {
|
|
454
|
+
headers["x-api-key"] = this.apiKey;
|
|
455
|
+
}
|
|
456
|
+
const cookies = {};
|
|
457
|
+
if (this.betterAuthCookies) {
|
|
458
|
+
Object.assign(cookies, this.betterAuthCookies);
|
|
459
|
+
}
|
|
460
|
+
this.sendMessage({
|
|
461
|
+
type: "connect",
|
|
462
|
+
auth: {
|
|
463
|
+
headers: Object.keys(headers).length > 0 ? headers : void 0,
|
|
464
|
+
cookies: Object.keys(cookies).length > 0 ? cookies : void 0
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
if (this.isAdmin) {
|
|
468
|
+
this.sendMessage({
|
|
469
|
+
type: "subscribe_logs"
|
|
470
|
+
});
|
|
471
|
+
this.sendMessage({
|
|
472
|
+
type: "subscribe_admin_events"
|
|
473
|
+
});
|
|
474
|
+
} else {
|
|
475
|
+
try {
|
|
476
|
+
const state = await this.auth.getState();
|
|
477
|
+
console.log("Auth state:", state);
|
|
478
|
+
if (state.isAuthenticated) {
|
|
479
|
+
this.sendMessage({
|
|
480
|
+
type: "subscribe_logs"
|
|
481
|
+
});
|
|
482
|
+
this.sendMessage({
|
|
483
|
+
type: "subscribe_admin_events"
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
} catch (err) {
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
this.flushQueue();
|
|
490
|
+
this.resubscribeAll();
|
|
491
|
+
this.startHeartbeat();
|
|
492
|
+
};
|
|
493
|
+
this.ws.onmessage = (event) => this.handleMessage(event);
|
|
494
|
+
this.ws.onclose = () => this.handleClose();
|
|
495
|
+
this.ws.onerror = (err) => {
|
|
496
|
+
if (!this.isExplicitlyClosed) {
|
|
497
|
+
console.error("WebSocket error:", err);
|
|
498
|
+
this.ws?.close();
|
|
499
|
+
}
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
startHeartbeat() {
|
|
503
|
+
this.stopHeartbeat();
|
|
504
|
+
this.heartbeatInterval = setInterval(() => {
|
|
505
|
+
if (this.isConnected) {
|
|
506
|
+
this.sendMessage({ type: "ping" });
|
|
507
|
+
}
|
|
508
|
+
}, 3e4);
|
|
509
|
+
}
|
|
510
|
+
stopHeartbeat() {
|
|
511
|
+
if (this.heartbeatInterval) {
|
|
512
|
+
clearInterval(this.heartbeatInterval);
|
|
513
|
+
this.heartbeatInterval = null;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
handleClose() {
|
|
517
|
+
this.isConnected = false;
|
|
518
|
+
this.sessionId = null;
|
|
519
|
+
this.disconnectionListeners.forEach((l) => l("Disconnected"));
|
|
520
|
+
if (this.isExplicitlyClosed) return;
|
|
521
|
+
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
|
522
|
+
const delay = this.reconnectDelay * Math.pow(2, this.reconnectAttempts);
|
|
523
|
+
console.log(`Disconnected. Reconnecting in ${delay}ms...`);
|
|
524
|
+
this.reconnectTimeout = setTimeout(() => {
|
|
525
|
+
this.reconnectAttempts++;
|
|
526
|
+
this.connect();
|
|
527
|
+
}, delay);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
handleMessage(event) {
|
|
531
|
+
try {
|
|
532
|
+
const msg = JSON.parse(event.data);
|
|
533
|
+
if (msg.type === "data" || msg.type === "patch") {
|
|
534
|
+
const queryId = msg.queryId;
|
|
535
|
+
const data = msg.data || msg.patch;
|
|
536
|
+
const listeners = this.listeners.get(queryId);
|
|
537
|
+
if (listeners) {
|
|
538
|
+
listeners.forEach((l) => l(data));
|
|
539
|
+
}
|
|
540
|
+
} else if (msg.type === "mutationResponse") {
|
|
541
|
+
const pending = this.pendingMutations.get(msg.mutationId);
|
|
542
|
+
if (pending) {
|
|
543
|
+
clearTimeout(pending.timeout);
|
|
544
|
+
this.pendingMutations.delete(msg.mutationId);
|
|
545
|
+
if (msg.success) {
|
|
546
|
+
pending.resolve(msg.data);
|
|
547
|
+
} else {
|
|
548
|
+
pending.reject(new Error(msg.error));
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
} else if (msg.type === "connected") {
|
|
552
|
+
console.log("Session established:", msg.sessionId);
|
|
553
|
+
this.sessionId = msg.sessionId;
|
|
554
|
+
this.isConnected = true;
|
|
555
|
+
this.reconnectAttempts = 0;
|
|
556
|
+
this.connectionListeners.forEach((l) => l(msg.sessionId));
|
|
557
|
+
} else if (msg.type === "error") {
|
|
558
|
+
console.error("Server error:", msg.message);
|
|
559
|
+
this.errorListeners.forEach((l) => l(msg.message));
|
|
560
|
+
if (msg.message === "Authentication required") {
|
|
561
|
+
this.isExplicitlyClosed = true;
|
|
562
|
+
this.ws?.close();
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
} catch (e) {
|
|
566
|
+
console.error("Error handling message:", e);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
getWs() {
|
|
570
|
+
return this.ws;
|
|
571
|
+
}
|
|
572
|
+
onConnected(cb) {
|
|
573
|
+
this.connectionListeners.add(cb);
|
|
574
|
+
return () => this.connectionListeners.delete(cb);
|
|
575
|
+
}
|
|
576
|
+
onDisconnected(cb) {
|
|
577
|
+
this.disconnectionListeners.add(cb);
|
|
578
|
+
return () => this.disconnectionListeners.delete(cb);
|
|
579
|
+
}
|
|
580
|
+
onError(cb) {
|
|
581
|
+
this.errorListeners.add(cb);
|
|
582
|
+
return () => this.errorListeners.delete(cb);
|
|
583
|
+
}
|
|
584
|
+
getStatus() {
|
|
585
|
+
return this.isConnected ? "connected" : "disconnected";
|
|
586
|
+
}
|
|
587
|
+
getSessionId() {
|
|
588
|
+
return this.sessionId;
|
|
589
|
+
}
|
|
590
|
+
sendMessage(msg) {
|
|
591
|
+
const data = JSON.stringify(msg);
|
|
592
|
+
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
|
|
593
|
+
this.ws.send(data);
|
|
594
|
+
} else {
|
|
595
|
+
this.messageQueue.push(data);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
flushQueue() {
|
|
599
|
+
while (this.messageQueue.length > 0) {
|
|
600
|
+
const msg = this.messageQueue.shift();
|
|
601
|
+
if (msg) this.ws?.send(msg);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
resubscribeAll() {
|
|
605
|
+
for (const [queryId, sub] of Array.from(this.subscriptions.entries())) {
|
|
606
|
+
this.sendMessage({
|
|
607
|
+
type: "query",
|
|
608
|
+
queryId,
|
|
609
|
+
name: sub.name,
|
|
610
|
+
args: sub.args
|
|
611
|
+
});
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
getQueryId(name, args) {
|
|
615
|
+
const stableArgs = JSON.stringify(args, Object.keys(args || {}).sort());
|
|
616
|
+
return `${name}:${stableArgs}`;
|
|
617
|
+
}
|
|
618
|
+
subscribe(queryName, args, onUpdate) {
|
|
619
|
+
const queryId = this.getQueryId(queryName, args);
|
|
620
|
+
if (!this.listeners.has(queryId)) {
|
|
621
|
+
this.listeners.set(queryId, /* @__PURE__ */ new Set());
|
|
622
|
+
this.subscriptions.set(queryId, { name: queryName, args });
|
|
623
|
+
this.sendMessage({
|
|
624
|
+
type: "query",
|
|
625
|
+
queryId,
|
|
626
|
+
name: queryName,
|
|
627
|
+
args
|
|
628
|
+
});
|
|
629
|
+
}
|
|
630
|
+
this.listeners.get(queryId).add(onUpdate);
|
|
631
|
+
return () => {
|
|
632
|
+
const set = this.listeners.get(queryId);
|
|
633
|
+
if (set) {
|
|
634
|
+
set.delete(onUpdate);
|
|
635
|
+
if (set.size === 0) {
|
|
636
|
+
this.listeners.delete(queryId);
|
|
637
|
+
this.subscriptions.delete(queryId);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
async fetch(name, args) {
|
|
643
|
+
const queryId = this.getQueryId(name, args);
|
|
644
|
+
return new Promise((resolve, reject) => {
|
|
645
|
+
let resolved = false;
|
|
646
|
+
const unsubscribe = this.subscribe(name, args, (data) => {
|
|
647
|
+
if (!resolved) {
|
|
648
|
+
resolved = true;
|
|
649
|
+
unsubscribe();
|
|
650
|
+
resolve(data);
|
|
651
|
+
}
|
|
652
|
+
});
|
|
653
|
+
setTimeout(() => {
|
|
654
|
+
if (!resolved) {
|
|
655
|
+
resolved = true;
|
|
656
|
+
unsubscribe();
|
|
657
|
+
reject(new Error("Timeout waiting for data"));
|
|
658
|
+
}
|
|
659
|
+
}, 5e3);
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
async mutate(name, args) {
|
|
663
|
+
return new Promise((resolve, reject) => {
|
|
664
|
+
const mutationId = Math.random().toString(36).slice(2);
|
|
665
|
+
const timeout = setTimeout(() => {
|
|
666
|
+
this.pendingMutations.delete(mutationId);
|
|
667
|
+
reject(new Error("Mutation timeout"));
|
|
668
|
+
}, 1e4);
|
|
669
|
+
this.pendingMutations.set(mutationId, { resolve, reject, timeout });
|
|
670
|
+
this.sendMessage({
|
|
671
|
+
type: "mutation",
|
|
672
|
+
mutationId,
|
|
673
|
+
name,
|
|
674
|
+
args
|
|
675
|
+
});
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
close() {
|
|
679
|
+
this.isExplicitlyClosed = true;
|
|
680
|
+
if (this.reconnectTimeout) {
|
|
681
|
+
clearTimeout(this.reconnectTimeout);
|
|
682
|
+
this.reconnectTimeout = null;
|
|
683
|
+
}
|
|
684
|
+
if (this.ws) {
|
|
685
|
+
this.ws.onclose = null;
|
|
686
|
+
this.ws.onopen = null;
|
|
687
|
+
this.ws.onerror = null;
|
|
688
|
+
this.ws.onmessage = null;
|
|
689
|
+
this.ws.close();
|
|
690
|
+
this.ws = null;
|
|
691
|
+
}
|
|
692
|
+
this.isConnected = false;
|
|
693
|
+
this.stopHeartbeat();
|
|
694
|
+
}
|
|
695
|
+
};
|
|
696
|
+
|
|
697
|
+
// src/react.tsx
|
|
698
|
+
var import_react = require("react");
|
|
699
|
+
var import_react_query = require("@tanstack/react-query");
|
|
700
|
+
var import_axios4 = __toESM(require("axios"), 1);
|
|
701
|
+
var import_jsx_runtime = require("react/jsx-runtime");
|
|
702
|
+
var ArchlastContext = (0, import_react.createContext)(null);
|
|
703
|
+
var useArchlast = () => {
|
|
704
|
+
const client = (0, import_react.useContext)(ArchlastContext);
|
|
705
|
+
if (!client) throw new Error("ArchlastProvider not found");
|
|
706
|
+
return client;
|
|
707
|
+
};
|
|
708
|
+
function ArchlastProvider({ client, children }) {
|
|
709
|
+
const [queryClient] = (0, import_react.useState)(() => new import_react_query.QueryClient());
|
|
710
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArchlastContext.Provider, { value: client, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react_query.QueryClientProvider, { client: queryClient, children }) });
|
|
711
|
+
}
|
|
712
|
+
function useQuery(queryRef, args, options) {
|
|
713
|
+
const client = useArchlast();
|
|
714
|
+
const queryClient = (0, import_react_query.useQueryClient)();
|
|
715
|
+
const queryKey = [queryRef._name, args];
|
|
716
|
+
(0, import_react.useEffect)(() => {
|
|
717
|
+
const unsubscribe = client.subscribe(queryRef._name, args, (data) => {
|
|
718
|
+
queryClient.setQueryData(queryKey, data);
|
|
719
|
+
});
|
|
720
|
+
return () => unsubscribe();
|
|
721
|
+
}, [client, queryRef._name, JSON.stringify(args), queryClient]);
|
|
722
|
+
const query = (0, import_react_query.useQuery)({
|
|
723
|
+
queryKey,
|
|
724
|
+
queryFn: async () => {
|
|
725
|
+
const result = await client.fetch(queryRef._name, args);
|
|
726
|
+
return result === void 0 ? null : result;
|
|
727
|
+
},
|
|
728
|
+
// staleTime: 0,
|
|
729
|
+
// gcTime: 1000 * 60 * 5,
|
|
730
|
+
...options
|
|
731
|
+
});
|
|
732
|
+
return query.data;
|
|
733
|
+
}
|
|
734
|
+
function useMutation(mutationRef) {
|
|
735
|
+
const client = useArchlast();
|
|
736
|
+
const queryClient = (0, import_react_query.useQueryClient)();
|
|
737
|
+
const mutation = (0, import_react_query.useMutation)({
|
|
738
|
+
mutationFn: async (args) => {
|
|
739
|
+
return client.mutate(mutationRef._name, args);
|
|
740
|
+
},
|
|
741
|
+
onSuccess: () => {
|
|
742
|
+
queryClient.invalidateQueries();
|
|
743
|
+
}
|
|
744
|
+
});
|
|
745
|
+
return mutation.mutateAsync;
|
|
746
|
+
}
|
|
747
|
+
function useUpload(options) {
|
|
748
|
+
const client = useArchlast();
|
|
749
|
+
const [isUploading, setUploading] = (0, import_react.useState)(false);
|
|
750
|
+
const [error, setError] = (0, import_react.useState)(null);
|
|
751
|
+
const [result, setResult] = (0, import_react.useState)(null);
|
|
752
|
+
const upload = (0, import_react.useCallback)(
|
|
753
|
+
async (file, contentType) => {
|
|
754
|
+
setUploading(true);
|
|
755
|
+
setError(null);
|
|
756
|
+
setResult(null);
|
|
757
|
+
try {
|
|
758
|
+
const result2 = await client.storage.upload(file, contentType);
|
|
759
|
+
setResult({ id: result2.id, url: result2.url });
|
|
760
|
+
return { id: result2.id, url: result2.url };
|
|
761
|
+
} catch (err) {
|
|
762
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
763
|
+
setError(e);
|
|
764
|
+
throw e;
|
|
765
|
+
} finally {
|
|
766
|
+
setUploading(false);
|
|
767
|
+
}
|
|
768
|
+
},
|
|
769
|
+
[client]
|
|
770
|
+
);
|
|
771
|
+
const presign = (0, import_react.useCallback)(
|
|
772
|
+
async (id, expiresInSeconds = 300) => {
|
|
773
|
+
const { url: relativeUrl } = await client.storage.presign(id, expiresInSeconds);
|
|
774
|
+
const absoluteUrl = relativeUrl.startsWith("http") ? relativeUrl : `${options?.baseUrl ?? ""}${relativeUrl.startsWith("/") ? "" : "/"}${relativeUrl}`;
|
|
775
|
+
setResult((prev) => prev ? { ...prev, url: absoluteUrl } : { id, url: absoluteUrl });
|
|
776
|
+
return absoluteUrl;
|
|
777
|
+
},
|
|
778
|
+
[client, options?.baseUrl]
|
|
779
|
+
);
|
|
780
|
+
return { upload, presign, isUploading, error, result };
|
|
781
|
+
}
|
|
782
|
+
var DEFAULT_AUTH_STATE = {
|
|
783
|
+
isAuthenticated: false,
|
|
784
|
+
user: null,
|
|
785
|
+
session: null
|
|
786
|
+
};
|
|
787
|
+
function useAuth(options) {
|
|
788
|
+
const client = useArchlast();
|
|
789
|
+
const queryClient = (0, import_react_query.useQueryClient)();
|
|
790
|
+
const enabled = options?.enabled ?? true;
|
|
791
|
+
const pollInterval = options?.pollIntervalMs ?? 3e4;
|
|
792
|
+
const {
|
|
793
|
+
data: authState,
|
|
794
|
+
isLoading,
|
|
795
|
+
error,
|
|
796
|
+
refetch
|
|
797
|
+
} = (0, import_react_query.useQuery)({
|
|
798
|
+
queryKey: ["auth", "state"],
|
|
799
|
+
queryFn: async () => {
|
|
800
|
+
try {
|
|
801
|
+
return await client.auth.getState();
|
|
802
|
+
} catch (err) {
|
|
803
|
+
if (import_axios4.default.isAxiosError(err) && err.response?.status === 401) {
|
|
804
|
+
return DEFAULT_AUTH_STATE;
|
|
805
|
+
}
|
|
806
|
+
throw err;
|
|
807
|
+
}
|
|
808
|
+
},
|
|
809
|
+
retry: false,
|
|
810
|
+
// Don't retry auth checks, fail fast
|
|
811
|
+
staleTime: 0,
|
|
812
|
+
// Auth state is volatile, consider it always stale
|
|
813
|
+
refetchInterval: enabled ? pollInterval : false,
|
|
814
|
+
refetchOnWindowFocus: true,
|
|
815
|
+
// Security: re-check when user tabbs back
|
|
816
|
+
refetchOnReconnect: true,
|
|
817
|
+
enabled,
|
|
818
|
+
initialData: DEFAULT_AUTH_STATE
|
|
819
|
+
// Optimistic default
|
|
820
|
+
});
|
|
821
|
+
const invalidateAuth = (0, import_react.useCallback)(() => {
|
|
822
|
+
return queryClient.invalidateQueries({ queryKey: ["auth", "state"] });
|
|
823
|
+
}, [queryClient]);
|
|
824
|
+
const signIn = (0, import_react.useCallback)(
|
|
825
|
+
async (input) => {
|
|
826
|
+
await client.auth.signIn(input);
|
|
827
|
+
await invalidateAuth();
|
|
828
|
+
},
|
|
829
|
+
[client, invalidateAuth]
|
|
830
|
+
);
|
|
831
|
+
const signUp = (0, import_react.useCallback)(
|
|
832
|
+
async (input) => {
|
|
833
|
+
await client.auth.signUp(input);
|
|
834
|
+
await invalidateAuth();
|
|
835
|
+
},
|
|
836
|
+
[client, invalidateAuth]
|
|
837
|
+
);
|
|
838
|
+
const signOut = (0, import_react.useCallback)(async () => {
|
|
839
|
+
try {
|
|
840
|
+
await client.auth.signOut();
|
|
841
|
+
} finally {
|
|
842
|
+
queryClient.setQueryData(["auth", "state"], DEFAULT_AUTH_STATE);
|
|
843
|
+
await invalidateAuth();
|
|
844
|
+
}
|
|
845
|
+
}, [client, queryClient, invalidateAuth]);
|
|
846
|
+
const currentState = authState ?? DEFAULT_AUTH_STATE;
|
|
847
|
+
return {
|
|
848
|
+
...currentState,
|
|
849
|
+
// Ensure booleans
|
|
850
|
+
isAuthenticated: !!currentState.isAuthenticated,
|
|
851
|
+
isLoading,
|
|
852
|
+
error,
|
|
853
|
+
refresh: async () => {
|
|
854
|
+
await refetch();
|
|
855
|
+
},
|
|
856
|
+
signIn,
|
|
857
|
+
signUp,
|
|
858
|
+
signOut
|
|
859
|
+
};
|
|
860
|
+
}
|
|
861
|
+
function useRequestPasswordReset() {
|
|
862
|
+
const client = useArchlast();
|
|
863
|
+
return (0, import_react_query.useMutation)({
|
|
864
|
+
mutationFn: async (email) => {
|
|
865
|
+
return client.auth.requestPasswordReset(email);
|
|
866
|
+
}
|
|
867
|
+
});
|
|
868
|
+
}
|
|
869
|
+
function useResetPassword() {
|
|
870
|
+
const client = useArchlast();
|
|
871
|
+
return (0, import_react_query.useMutation)({
|
|
872
|
+
mutationFn: async ({ token, password }) => {
|
|
873
|
+
return client.auth.resetPassword(token, password);
|
|
874
|
+
}
|
|
875
|
+
});
|
|
876
|
+
}
|
|
877
|
+
function useDownload(options) {
|
|
878
|
+
const client = useArchlast();
|
|
879
|
+
const [isDownloading, setDownloading] = (0, import_react.useState)(false);
|
|
880
|
+
const [error, setError] = (0, import_react.useState)(null);
|
|
881
|
+
const presign = (0, import_react.useCallback)(
|
|
882
|
+
async (id, expiresInSeconds = 300) => {
|
|
883
|
+
const { url: relativeUrl } = await client.storage.presign(id, expiresInSeconds);
|
|
884
|
+
const absoluteUrl = relativeUrl.startsWith("http") ? relativeUrl : `${options?.baseUrl ?? client.baseUrl ?? ""}${relativeUrl.startsWith("/") ? "" : "/"}${relativeUrl}`;
|
|
885
|
+
return absoluteUrl;
|
|
886
|
+
},
|
|
887
|
+
[client, options?.baseUrl]
|
|
888
|
+
);
|
|
889
|
+
const download = (0, import_react.useCallback)(
|
|
890
|
+
async (id, expiresInSeconds = 300) => {
|
|
891
|
+
setDownloading(true);
|
|
892
|
+
setError(null);
|
|
893
|
+
try {
|
|
894
|
+
const signedUrl = await presign(id, expiresInSeconds);
|
|
895
|
+
const response = await import_axios4.default.get(signedUrl, {
|
|
896
|
+
responseType: "blob"
|
|
897
|
+
});
|
|
898
|
+
const blob = response.data;
|
|
899
|
+
const objectUrl = URL.createObjectURL(blob);
|
|
900
|
+
return { blob, url: objectUrl };
|
|
901
|
+
} catch (err) {
|
|
902
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
903
|
+
setError(e);
|
|
904
|
+
throw e;
|
|
905
|
+
} finally {
|
|
906
|
+
setDownloading(false);
|
|
907
|
+
}
|
|
908
|
+
},
|
|
909
|
+
[presign]
|
|
910
|
+
);
|
|
911
|
+
return { getUrl: presign, download, isDownloading, error };
|
|
912
|
+
}
|
|
913
|
+
function useStorageDelete(options) {
|
|
914
|
+
const client = useArchlast();
|
|
915
|
+
const [isDeleting, setDeleting] = (0, import_react.useState)(false);
|
|
916
|
+
const [error, setError] = (0, import_react.useState)(null);
|
|
917
|
+
const deleteFile = (0, import_react.useCallback)(
|
|
918
|
+
async (id) => {
|
|
919
|
+
setDeleting(true);
|
|
920
|
+
setError(null);
|
|
921
|
+
try {
|
|
922
|
+
const deleteUrl = `${options?.baseUrl ?? ""}/_archlast/storage/files/${encodeURIComponent(id)}`;
|
|
923
|
+
await import_axios4.default.delete(deleteUrl, {
|
|
924
|
+
withCredentials: true
|
|
925
|
+
});
|
|
926
|
+
} catch (err) {
|
|
927
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
928
|
+
setError(e);
|
|
929
|
+
throw e;
|
|
930
|
+
} finally {
|
|
931
|
+
setDeleting(false);
|
|
932
|
+
}
|
|
933
|
+
},
|
|
934
|
+
[options?.baseUrl]
|
|
935
|
+
);
|
|
936
|
+
return { deleteFile, isDeleting, error };
|
|
937
|
+
}
|
|
938
|
+
function useClientPagination(data, initialPageSize = 10) {
|
|
939
|
+
const [page, setPage] = (0, import_react.useState)(1);
|
|
940
|
+
const [pageSize, setPageSize] = (0, import_react.useState)(initialPageSize);
|
|
941
|
+
const items = data || [];
|
|
942
|
+
const total = items.length;
|
|
943
|
+
const totalPages = Math.ceil(total / pageSize);
|
|
944
|
+
const startIndex = (page - 1) * pageSize;
|
|
945
|
+
const endIndex = startIndex + pageSize;
|
|
946
|
+
const paginatedItems = items.slice(startIndex, endIndex);
|
|
947
|
+
const hasMore = page < totalPages;
|
|
948
|
+
const hasPrevious = page > 1;
|
|
949
|
+
const nextPage = () => {
|
|
950
|
+
if (hasMore) setPage((p) => p + 1);
|
|
951
|
+
};
|
|
952
|
+
const previousPage = () => {
|
|
953
|
+
if (hasPrevious) setPage((p) => p - 1);
|
|
954
|
+
};
|
|
955
|
+
const handleSetPage = (newPage) => {
|
|
956
|
+
const validPage = Math.max(1, Math.min(newPage, totalPages || 1));
|
|
957
|
+
setPage(validPage);
|
|
958
|
+
};
|
|
959
|
+
const handleSetPageSize = (size) => {
|
|
960
|
+
setPageSize(size);
|
|
961
|
+
setPage(1);
|
|
962
|
+
};
|
|
963
|
+
(0, import_react.useEffect)(() => {
|
|
964
|
+
setPage(1);
|
|
965
|
+
}, [data?.length]);
|
|
966
|
+
return {
|
|
967
|
+
items: paginatedItems,
|
|
968
|
+
page,
|
|
969
|
+
pageSize,
|
|
970
|
+
total,
|
|
971
|
+
totalPages,
|
|
972
|
+
hasMore,
|
|
973
|
+
hasPrevious,
|
|
974
|
+
nextPage,
|
|
975
|
+
previousPage,
|
|
976
|
+
setPage: handleSetPage,
|
|
977
|
+
setPageSize: handleSetPageSize
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
function usePagination(queryRef, baseArgs, options) {
|
|
981
|
+
const client = useArchlast();
|
|
982
|
+
const queryClient = (0, import_react_query.useQueryClient)();
|
|
983
|
+
const [cursor, setCursor] = (0, import_react.useState)(null);
|
|
984
|
+
const [allItems, setAllItems] = (0, import_react.useState)([]);
|
|
985
|
+
const limit = options?.limit || 20;
|
|
986
|
+
const args = { ...baseArgs, cursor, limit };
|
|
987
|
+
const queryKey = [queryRef._name, args];
|
|
988
|
+
(0, import_react.useEffect)(() => {
|
|
989
|
+
const unsubscribe = client.subscribe(queryRef._name, args, (data2) => {
|
|
990
|
+
queryClient.setQueryData(queryKey, data2);
|
|
991
|
+
});
|
|
992
|
+
return () => unsubscribe();
|
|
993
|
+
}, [client, queryRef._name, JSON.stringify(args), queryClient]);
|
|
994
|
+
const query = (0, import_react_query.useQuery)({
|
|
995
|
+
queryKey,
|
|
996
|
+
queryFn: async () => {
|
|
997
|
+
const result = await client.fetch(queryRef._name, args);
|
|
998
|
+
return result === void 0 ? { items: [], continueCursor: null, isDone: true } : result;
|
|
999
|
+
},
|
|
1000
|
+
// staleTime: 0,
|
|
1001
|
+
// gcTime: 1000 * 60 * 5,
|
|
1002
|
+
enabled: options?.enabled !== false
|
|
1003
|
+
});
|
|
1004
|
+
const data = query.data;
|
|
1005
|
+
(0, import_react.useEffect)(() => {
|
|
1006
|
+
if (data?.items) {
|
|
1007
|
+
if (cursor === null) {
|
|
1008
|
+
setAllItems(data.items);
|
|
1009
|
+
} else {
|
|
1010
|
+
setAllItems((prev) => [...prev, ...data.items]);
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
}, [data, cursor]);
|
|
1014
|
+
const loadMore = () => {
|
|
1015
|
+
if (data && !data.isDone && data.continueCursor) {
|
|
1016
|
+
setCursor(data.continueCursor);
|
|
1017
|
+
}
|
|
1018
|
+
};
|
|
1019
|
+
const refresh = () => {
|
|
1020
|
+
setCursor(null);
|
|
1021
|
+
setAllItems([]);
|
|
1022
|
+
queryClient.invalidateQueries({ queryKey: [queryRef._name] });
|
|
1023
|
+
};
|
|
1024
|
+
return {
|
|
1025
|
+
items: allItems,
|
|
1026
|
+
cursor: data?.continueCursor ?? null,
|
|
1027
|
+
isDone: data?.isDone ?? false,
|
|
1028
|
+
page: data?.page,
|
|
1029
|
+
pageSize: data?.pageSize,
|
|
1030
|
+
total: data?.total,
|
|
1031
|
+
hasMore: !data?.isDone && !!data?.continueCursor,
|
|
1032
|
+
loadMore,
|
|
1033
|
+
refresh,
|
|
1034
|
+
isLoading: query.isLoading
|
|
1035
|
+
};
|
|
1036
|
+
}
|
|
1037
|
+
function useStorageList(limit = 20, offset = 0) {
|
|
1038
|
+
const client = useArchlast();
|
|
1039
|
+
return (0, import_react_query.useQuery)({
|
|
1040
|
+
queryKey: ["storage", "list", limit, offset],
|
|
1041
|
+
queryFn: () => client.storage.list(limit, offset)
|
|
1042
|
+
});
|
|
1043
|
+
}
|
|
1044
|
+
function useStorageMetadata(id) {
|
|
1045
|
+
const client = useArchlast();
|
|
1046
|
+
return (0, import_react_query.useQuery)({
|
|
1047
|
+
queryKey: ["storage", "file", id],
|
|
1048
|
+
queryFn: () => client.storage.getMetadata(id),
|
|
1049
|
+
enabled: !!id
|
|
1050
|
+
});
|
|
1051
|
+
}
|
|
1052
|
+
function useStorageUpload() {
|
|
1053
|
+
const client = useArchlast();
|
|
1054
|
+
const queryClient = (0, import_react_query.useQueryClient)();
|
|
1055
|
+
return (0, import_react_query.useMutation)({
|
|
1056
|
+
mutationFn: (file) => client.storage.upload(file),
|
|
1057
|
+
onSuccess: () => {
|
|
1058
|
+
queryClient.invalidateQueries({ queryKey: ["storage", "list"] });
|
|
1059
|
+
}
|
|
1060
|
+
});
|
|
1061
|
+
}
|
|
1062
|
+
function useStorageDeleteFile() {
|
|
1063
|
+
const client = useArchlast();
|
|
1064
|
+
const queryClient = (0, import_react_query.useQueryClient)();
|
|
1065
|
+
return (0, import_react_query.useMutation)({
|
|
1066
|
+
mutationFn: (id) => client.storage.delete(id),
|
|
1067
|
+
onSuccess: () => {
|
|
1068
|
+
queryClient.invalidateQueries({ queryKey: ["storage", "list"] });
|
|
1069
|
+
}
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
function useStorageHelpers() {
|
|
1073
|
+
const client = useArchlast();
|
|
1074
|
+
return {
|
|
1075
|
+
getPublicUrl: (id) => client.storage.getPublicUrl(id)
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
// src/function-reference.ts
|
|
1080
|
+
function makeFunctionReference(name) {
|
|
1081
|
+
return {
|
|
1082
|
+
_type: "query",
|
|
1083
|
+
_name: name,
|
|
1084
|
+
_args: void 0,
|
|
1085
|
+
_return: void 0,
|
|
1086
|
+
_auth: void 0
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1089
|
+
function makeRpcReference(name) {
|
|
1090
|
+
return {
|
|
1091
|
+
_procedureType: "query",
|
|
1092
|
+
_name: name,
|
|
1093
|
+
_args: void 0,
|
|
1094
|
+
_return: void 0,
|
|
1095
|
+
_auth: void 0
|
|
1096
|
+
};
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
// src/trpc.ts
|
|
1100
|
+
var import_client = require("@trpc/client");
|
|
1101
|
+
var BETTER_AUTH_API_KEY_PREFIX = "arch_";
|
|
1102
|
+
function isBetterAuthApiKey(token) {
|
|
1103
|
+
return token.startsWith(BETTER_AUTH_API_KEY_PREFIX);
|
|
1104
|
+
}
|
|
1105
|
+
function createArchlastTRPCClient(options = {}) {
|
|
1106
|
+
const {
|
|
1107
|
+
baseUrl = "http://localhost:4000",
|
|
1108
|
+
apiKey,
|
|
1109
|
+
sessionToken,
|
|
1110
|
+
batch = true,
|
|
1111
|
+
headers: customHeaders = {}
|
|
1112
|
+
} = options;
|
|
1113
|
+
const headers = {
|
|
1114
|
+
"Content-Type": "application/json",
|
|
1115
|
+
...customHeaders
|
|
1116
|
+
};
|
|
1117
|
+
if (apiKey) {
|
|
1118
|
+
if (isBetterAuthApiKey(apiKey)) {
|
|
1119
|
+
headers["x-api-key"] = apiKey;
|
|
1120
|
+
} else {
|
|
1121
|
+
headers["Authorization"] = `Bearer ${apiKey}`;
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
if (sessionToken) {
|
|
1125
|
+
headers["Cookie"] = `archlast-session=${sessionToken}`;
|
|
1126
|
+
}
|
|
1127
|
+
return (0, import_client.createTRPCClient)({
|
|
1128
|
+
links: [
|
|
1129
|
+
(0, import_client.httpBatchLink)({
|
|
1130
|
+
url: `${baseUrl}/api/trpc`,
|
|
1131
|
+
headers
|
|
1132
|
+
})
|
|
1133
|
+
]
|
|
1134
|
+
});
|
|
1135
|
+
}
|
|
1136
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
1137
|
+
0 && (module.exports = {
|
|
1138
|
+
ArchlastAuthClient,
|
|
1139
|
+
ArchlastClient,
|
|
1140
|
+
ArchlastContext,
|
|
1141
|
+
ArchlastProvider,
|
|
1142
|
+
StorageClient,
|
|
1143
|
+
createArchlastTRPCClient,
|
|
1144
|
+
makeFunctionReference,
|
|
1145
|
+
makeRpcReference,
|
|
1146
|
+
useArchlast,
|
|
1147
|
+
useAuth,
|
|
1148
|
+
useClientPagination,
|
|
1149
|
+
useDownload,
|
|
1150
|
+
useMutation,
|
|
1151
|
+
usePagination,
|
|
1152
|
+
useQuery,
|
|
1153
|
+
useRequestPasswordReset,
|
|
1154
|
+
useResetPassword,
|
|
1155
|
+
useStorageDelete,
|
|
1156
|
+
useStorageDeleteFile,
|
|
1157
|
+
useStorageHelpers,
|
|
1158
|
+
useStorageList,
|
|
1159
|
+
useStorageMetadata,
|
|
1160
|
+
useStorageUpload,
|
|
1161
|
+
useUpload
|
|
1162
|
+
});
|
|
1163
|
+
//# sourceMappingURL=index.cjs.map
|