@basictech/react 0.8.0-beta.1 → 0.8.0-beta.3
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/.turbo/turbo-build.log +10 -10
- package/changelog.md +12 -0
- package/dist/index.d.mts +59 -43
- package/dist/index.d.ts +59 -43
- package/dist/index.js +1015 -200
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1006 -193
- package/dist/index.mjs.map +1 -1
- package/package.json +3 -1
- package/readme.md +33 -0
- package/src/AuthContext.tsx +157 -177
- package/src/context.tsx +104 -0
- package/src/core/auth/AuthManager.ts +64 -40
- package/src/dev/BasicDevToolbar.tsx +665 -0
- package/src/index.ts +3 -2
- package/src/sync/syncProtocol.js +30 -0
- package/src/utils/network.ts +69 -16
package/dist/index.js
CHANGED
|
@@ -159,14 +159,37 @@ var init_syncProtocol = __esm({
|
|
|
159
159
|
onError("Authentication failed: " + (err.message || err), RECONNECT_DELAY);
|
|
160
160
|
}
|
|
161
161
|
};
|
|
162
|
+
function handleVisibilityResume() {
|
|
163
|
+
if (document.visibilityState === "visible" && ws.readyState === WebSocket.OPEN) {
|
|
164
|
+
log("Page became visible - refreshing token for WebSocket");
|
|
165
|
+
resolveGetToken()({ forceRefresh: true }).then(function(newToken) {
|
|
166
|
+
if (ws.readyState === WebSocket.OPEN) {
|
|
167
|
+
ws.send(JSON.stringify({ type: "tokenUpdate", authToken: newToken }));
|
|
168
|
+
scheduleTokenRefresh(newToken);
|
|
169
|
+
}
|
|
170
|
+
}).catch(function(err) {
|
|
171
|
+
log("Token refresh on visibility resume failed:", err);
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (typeof document !== "undefined") {
|
|
176
|
+
document.addEventListener("visibilitychange", handleVisibilityResume);
|
|
177
|
+
}
|
|
178
|
+
function cleanupVisibilityListener() {
|
|
179
|
+
if (typeof document !== "undefined") {
|
|
180
|
+
document.removeEventListener("visibilitychange", handleVisibilityResume);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
162
183
|
ws.onerror = function(event) {
|
|
163
184
|
clearRefreshTimer();
|
|
185
|
+
cleanupVisibilityListener();
|
|
164
186
|
ws.close();
|
|
165
187
|
log("ws.onerror", event);
|
|
166
188
|
onError(event?.message, RECONNECT_DELAY);
|
|
167
189
|
};
|
|
168
190
|
ws.onclose = function(event) {
|
|
169
191
|
clearRefreshTimer();
|
|
192
|
+
cleanupVisibilityListener();
|
|
170
193
|
onError("Socket closed: " + event.reason, RECONNECT_DELAY);
|
|
171
194
|
};
|
|
172
195
|
var isFirstRound = true;
|
|
@@ -203,6 +226,7 @@ var init_syncProtocol = __esm({
|
|
|
203
226
|
},
|
|
204
227
|
disconnect: function() {
|
|
205
228
|
clearRefreshTimer();
|
|
229
|
+
cleanupVisibilityListener();
|
|
206
230
|
ws.close();
|
|
207
231
|
}
|
|
208
232
|
});
|
|
@@ -238,9 +262,812 @@ var init_syncProtocol = __esm({
|
|
|
238
262
|
}
|
|
239
263
|
});
|
|
240
264
|
|
|
265
|
+
// package.json
|
|
266
|
+
var version;
|
|
267
|
+
var init_package = __esm({
|
|
268
|
+
"package.json"() {
|
|
269
|
+
version = "0.8.0-beta.3";
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
// src/utils/network.ts
|
|
274
|
+
function isDevelopment(debug) {
|
|
275
|
+
if (debug === true) return true;
|
|
276
|
+
if (typeof process !== "undefined" && process.env.NODE_ENV === "development") return true;
|
|
277
|
+
if (typeof window === "undefined" || !window.location) return false;
|
|
278
|
+
const host = window.location.hostname;
|
|
279
|
+
return host === "localhost" || host === "127.0.0.1" || host.includes("localhost") || host.includes("127.0.0.1") || host.includes(".local");
|
|
280
|
+
}
|
|
281
|
+
function normalizeVersion(v) {
|
|
282
|
+
if (v == null) return null;
|
|
283
|
+
const t = String(v).trim();
|
|
284
|
+
return t.length ? t : null;
|
|
285
|
+
}
|
|
286
|
+
function versionsMatch(a, b) {
|
|
287
|
+
const na = a.trim();
|
|
288
|
+
const nb = b.trim();
|
|
289
|
+
if (na === nb) return true;
|
|
290
|
+
const va = import_semver.default.valid(na);
|
|
291
|
+
const vb = import_semver.default.valid(nb);
|
|
292
|
+
if (va && vb) return import_semver.default.eq(va, vb);
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
function usesBetaDistTag(version2) {
|
|
296
|
+
const pre = import_semver.default.prerelease(version2);
|
|
297
|
+
const id = pre?.[0];
|
|
298
|
+
return typeof id === "string" && id.toLowerCase() === "beta";
|
|
299
|
+
}
|
|
300
|
+
async function checkForNewVersion() {
|
|
301
|
+
try {
|
|
302
|
+
const currentVersion = normalizeVersion(version);
|
|
303
|
+
if (!currentVersion) {
|
|
304
|
+
return { hasNewVersion: false, latestVersion: null, currentVersion: null };
|
|
305
|
+
}
|
|
306
|
+
const response = await fetch("https://registry.npmjs.org/@basictech/react", {
|
|
307
|
+
headers: { Accept: "application/vnd.npm.install-v1+json" }
|
|
308
|
+
});
|
|
309
|
+
if (!response.ok) {
|
|
310
|
+
throw new Error("Failed to fetch version from npm");
|
|
311
|
+
}
|
|
312
|
+
const data = await response.json();
|
|
313
|
+
const distTags = data["dist-tags"] ?? {};
|
|
314
|
+
const rawRegistry = usesBetaDistTag(currentVersion) ? distTags.beta ?? distTags.latest : distTags.latest;
|
|
315
|
+
const latestVersion = normalizeVersion(rawRegistry ?? null);
|
|
316
|
+
if (!latestVersion) {
|
|
317
|
+
throw new Error("Missing dist-tags from npm registry");
|
|
318
|
+
}
|
|
319
|
+
const same = versionsMatch(currentVersion, latestVersion);
|
|
320
|
+
if (!same && isDevelopment()) {
|
|
321
|
+
log("[basic] version check mismatch:", {
|
|
322
|
+
currentVersion,
|
|
323
|
+
registryVersion: latestVersion,
|
|
324
|
+
channel: usesBetaDistTag(currentVersion) ? "beta" : "latest"
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
if (!same) {
|
|
328
|
+
console.warn("[basic] New version available:", latestVersion, `
|
|
329
|
+
run "npm install @basictech/react@${latestVersion}" to update`);
|
|
330
|
+
}
|
|
331
|
+
if (usesBetaDistTag(currentVersion)) {
|
|
332
|
+
log("thank you for being on basictech/react beta :)");
|
|
333
|
+
}
|
|
334
|
+
return {
|
|
335
|
+
hasNewVersion: !same,
|
|
336
|
+
latestVersion,
|
|
337
|
+
currentVersion
|
|
338
|
+
};
|
|
339
|
+
} catch (error) {
|
|
340
|
+
log("Error checking for new version:", error);
|
|
341
|
+
return {
|
|
342
|
+
hasNewVersion: false,
|
|
343
|
+
latestVersion: null,
|
|
344
|
+
currentVersion: null
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
function cleanOAuthParamsFromUrl() {
|
|
349
|
+
if (window.location.search.includes("code") || window.location.search.includes("state")) {
|
|
350
|
+
const url = new URL(window.location.href);
|
|
351
|
+
url.searchParams.delete("code");
|
|
352
|
+
url.searchParams.delete("state");
|
|
353
|
+
window.history.replaceState({}, document.title, url.pathname + url.search);
|
|
354
|
+
log("Cleaned OAuth parameters from URL");
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
function getSyncStatus(statusCode) {
|
|
358
|
+
switch (statusCode) {
|
|
359
|
+
case -1:
|
|
360
|
+
return "ERROR";
|
|
361
|
+
case 0:
|
|
362
|
+
return "OFFLINE";
|
|
363
|
+
case 1:
|
|
364
|
+
return "CONNECTING";
|
|
365
|
+
case 2:
|
|
366
|
+
return "ONLINE";
|
|
367
|
+
case 3:
|
|
368
|
+
return "SYNCING";
|
|
369
|
+
case 4:
|
|
370
|
+
return "ERROR_WILL_RETRY";
|
|
371
|
+
default:
|
|
372
|
+
return "UNKNOWN";
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
var import_semver;
|
|
376
|
+
var init_network = __esm({
|
|
377
|
+
"src/utils/network.ts"() {
|
|
378
|
+
"use strict";
|
|
379
|
+
import_semver = __toESM(require("semver"));
|
|
380
|
+
init_config();
|
|
381
|
+
init_package();
|
|
382
|
+
}
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
// src/context.tsx
|
|
386
|
+
function useBasic() {
|
|
387
|
+
return (0, import_react.useContext)(BasicContext);
|
|
388
|
+
}
|
|
389
|
+
var import_react, DBStatus, noDb, BasicContext;
|
|
390
|
+
var init_context = __esm({
|
|
391
|
+
"src/context.tsx"() {
|
|
392
|
+
"use strict";
|
|
393
|
+
import_react = require("react");
|
|
394
|
+
DBStatus = /* @__PURE__ */ ((DBStatus2) => {
|
|
395
|
+
DBStatus2["LOADING"] = "LOADING";
|
|
396
|
+
DBStatus2["OFFLINE"] = "OFFLINE";
|
|
397
|
+
DBStatus2["CONNECTING"] = "CONNECTING";
|
|
398
|
+
DBStatus2["ONLINE"] = "ONLINE";
|
|
399
|
+
DBStatus2["SYNCING"] = "SYNCING";
|
|
400
|
+
DBStatus2["ERROR"] = "ERROR";
|
|
401
|
+
DBStatus2["ERROR_WILL_RETRY"] = "ERROR_WILL_RETRY";
|
|
402
|
+
DBStatus2["ERROR_TOKEN_EXPIRED"] = "ERROR_TOKEN_EXPIRED";
|
|
403
|
+
return DBStatus2;
|
|
404
|
+
})(DBStatus || {});
|
|
405
|
+
noDb = {
|
|
406
|
+
collection: () => {
|
|
407
|
+
throw new Error("no basicdb found - initialization failed. double check your schema.");
|
|
408
|
+
}
|
|
409
|
+
};
|
|
410
|
+
BasicContext = (0, import_react.createContext)({
|
|
411
|
+
isReady: false,
|
|
412
|
+
isSignedIn: false,
|
|
413
|
+
user: null,
|
|
414
|
+
did: null,
|
|
415
|
+
scope: null,
|
|
416
|
+
hasScope: () => false,
|
|
417
|
+
missingScopes: () => [],
|
|
418
|
+
signIn: () => Promise.resolve(),
|
|
419
|
+
signInWithHandle: () => Promise.resolve(),
|
|
420
|
+
signOut: () => Promise.resolve(),
|
|
421
|
+
signInWithCode: () => Promise.resolve({ success: false }),
|
|
422
|
+
getToken: (_options) => Promise.reject(new Error("no token")),
|
|
423
|
+
getSignInUrl: () => Promise.resolve(""),
|
|
424
|
+
db: noDb,
|
|
425
|
+
dbStatus: "LOADING" /* LOADING */,
|
|
426
|
+
dbMode: "sync",
|
|
427
|
+
devInfo: null,
|
|
428
|
+
refreshSchemaStatus: async () => {
|
|
429
|
+
},
|
|
430
|
+
isAuthReady: false,
|
|
431
|
+
signin: () => Promise.resolve(),
|
|
432
|
+
signout: () => Promise.resolve(),
|
|
433
|
+
signinWithCode: () => Promise.resolve({ success: false }),
|
|
434
|
+
getSignInLink: () => Promise.resolve("")
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
});
|
|
438
|
+
|
|
439
|
+
// src/dev/BasicDevToolbar.tsx
|
|
440
|
+
var BasicDevToolbar_exports = {};
|
|
441
|
+
__export(BasicDevToolbar_exports, {
|
|
442
|
+
BasicDevToolbar: () => BasicDevToolbar
|
|
443
|
+
});
|
|
444
|
+
function toneForAuth(isReady, isSignedIn) {
|
|
445
|
+
if (!isReady) return "muted";
|
|
446
|
+
if (isSignedIn) return "ok";
|
|
447
|
+
return "warn";
|
|
448
|
+
}
|
|
449
|
+
function toneForDb(dbMode, dbStatus) {
|
|
450
|
+
if (dbMode === "remote") return dbStatus === "ONLINE" /* ONLINE */ ? "ok" : "warn";
|
|
451
|
+
if (dbStatus === "ONLINE" /* ONLINE */ || dbStatus === "SYNCING" /* SYNCING */) return "ok";
|
|
452
|
+
if (dbStatus === "CONNECTING" /* CONNECTING */ || dbStatus === "LOADING" /* LOADING */) return "warn";
|
|
453
|
+
if (dbStatus === "OFFLINE" /* OFFLINE */) return "muted";
|
|
454
|
+
return "bad";
|
|
455
|
+
}
|
|
456
|
+
function toneForSchema(info) {
|
|
457
|
+
if (!info) return "muted";
|
|
458
|
+
if (info.valid && info.status === "current") return "ok";
|
|
459
|
+
if (info.status === "unpublished") return "warn";
|
|
460
|
+
if (info.status === "no_schema") return "muted";
|
|
461
|
+
return "bad";
|
|
462
|
+
}
|
|
463
|
+
function dbStatusLabel(status) {
|
|
464
|
+
switch (status) {
|
|
465
|
+
case "LOADING" /* LOADING */:
|
|
466
|
+
return "Initializing";
|
|
467
|
+
case "OFFLINE" /* OFFLINE */:
|
|
468
|
+
return "Offline";
|
|
469
|
+
case "CONNECTING" /* CONNECTING */:
|
|
470
|
+
return "Connecting";
|
|
471
|
+
case "ONLINE" /* ONLINE */:
|
|
472
|
+
return "Connected";
|
|
473
|
+
case "SYNCING" /* SYNCING */:
|
|
474
|
+
return "Syncing";
|
|
475
|
+
case "ERROR" /* ERROR */:
|
|
476
|
+
return "Error";
|
|
477
|
+
case "ERROR_WILL_RETRY" /* ERROR_WILL_RETRY */:
|
|
478
|
+
return "Retrying";
|
|
479
|
+
case "ERROR_TOKEN_EXPIRED" /* ERROR_TOKEN_EXPIRED */:
|
|
480
|
+
return "Token refresh";
|
|
481
|
+
default:
|
|
482
|
+
return String(status);
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
function chipColor(tone) {
|
|
486
|
+
switch (tone) {
|
|
487
|
+
case "ok":
|
|
488
|
+
return "#22c55e";
|
|
489
|
+
case "warn":
|
|
490
|
+
return "#eab308";
|
|
491
|
+
case "bad":
|
|
492
|
+
return "#ef4444";
|
|
493
|
+
default:
|
|
494
|
+
return "#71717a";
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
function displayDid(did) {
|
|
498
|
+
return did || "\u2014";
|
|
499
|
+
}
|
|
500
|
+
function displayUserLine(user) {
|
|
501
|
+
const parts = [];
|
|
502
|
+
if (user.sub) parts.push(`sub: ${user.sub}`);
|
|
503
|
+
if (user.email) parts.push(`email: ${user.email}`);
|
|
504
|
+
if (user.name) parts.push(`name: ${user.name}`);
|
|
505
|
+
return parts.length ? parts.join(" \xB7 ") : "\u2014";
|
|
506
|
+
}
|
|
507
|
+
function ClipboardIcon() {
|
|
508
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
509
|
+
"svg",
|
|
510
|
+
{
|
|
511
|
+
width: "14",
|
|
512
|
+
height: "14",
|
|
513
|
+
viewBox: "0 0 24 24",
|
|
514
|
+
fill: "none",
|
|
515
|
+
stroke: "currentColor",
|
|
516
|
+
strokeWidth: "2",
|
|
517
|
+
strokeLinecap: "round",
|
|
518
|
+
strokeLinejoin: "round",
|
|
519
|
+
"aria-hidden": true,
|
|
520
|
+
children: [
|
|
521
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }),
|
|
522
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })
|
|
523
|
+
]
|
|
524
|
+
}
|
|
525
|
+
);
|
|
526
|
+
}
|
|
527
|
+
function SectionHeader({ children }) {
|
|
528
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
529
|
+
"div",
|
|
530
|
+
{
|
|
531
|
+
style: {
|
|
532
|
+
fontSize: 10,
|
|
533
|
+
fontWeight: 700,
|
|
534
|
+
color: "#e4e4e7",
|
|
535
|
+
letterSpacing: "0.07em",
|
|
536
|
+
textTransform: "uppercase",
|
|
537
|
+
marginBottom: 8
|
|
538
|
+
},
|
|
539
|
+
children
|
|
540
|
+
}
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
function SectionRule() {
|
|
544
|
+
const bleed = PANEL_PAD_X;
|
|
545
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
546
|
+
"div",
|
|
547
|
+
{
|
|
548
|
+
role: "separator",
|
|
549
|
+
style: {
|
|
550
|
+
height: 1,
|
|
551
|
+
background: "rgba(255, 255, 255, 0.055)",
|
|
552
|
+
marginLeft: -bleed,
|
|
553
|
+
marginRight: -bleed,
|
|
554
|
+
marginTop: 14,
|
|
555
|
+
marginBottom: 10,
|
|
556
|
+
width: `calc(100% + ${bleed * 2}px)`
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
function CopyableRow({
|
|
562
|
+
rowKey,
|
|
563
|
+
label,
|
|
564
|
+
copyText,
|
|
565
|
+
copiedKey,
|
|
566
|
+
onCopied,
|
|
567
|
+
children
|
|
568
|
+
}) {
|
|
569
|
+
const [hover, setHover] = (0, import_react2.useState)(false);
|
|
570
|
+
const canCopy = copyText.length > 0;
|
|
571
|
+
const handleClick = (0, import_react2.useCallback)(
|
|
572
|
+
(e) => {
|
|
573
|
+
e.stopPropagation();
|
|
574
|
+
if (!canCopy) return;
|
|
575
|
+
void navigator.clipboard.writeText(copyText).then(() => onCopied(rowKey));
|
|
576
|
+
},
|
|
577
|
+
[canCopy, copyText, onCopied, rowKey]
|
|
578
|
+
);
|
|
579
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
580
|
+
"div",
|
|
581
|
+
{
|
|
582
|
+
role: canCopy ? "button" : void 0,
|
|
583
|
+
tabIndex: canCopy ? 0 : void 0,
|
|
584
|
+
onClick: canCopy ? handleClick : void 0,
|
|
585
|
+
onKeyDown: canCopy ? (e) => {
|
|
586
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
587
|
+
e.preventDefault();
|
|
588
|
+
handleClick(e);
|
|
589
|
+
}
|
|
590
|
+
} : void 0,
|
|
591
|
+
onMouseEnter: () => setHover(true),
|
|
592
|
+
onMouseLeave: () => setHover(false),
|
|
593
|
+
style: {
|
|
594
|
+
display: "flex",
|
|
595
|
+
gap: 8,
|
|
596
|
+
marginBottom: 6,
|
|
597
|
+
alignItems: "flex-start",
|
|
598
|
+
borderRadius: 6,
|
|
599
|
+
padding: "4px 6px",
|
|
600
|
+
marginLeft: -6,
|
|
601
|
+
marginRight: -6,
|
|
602
|
+
cursor: canCopy ? "pointer" : "default",
|
|
603
|
+
background: hover && canCopy ? "rgba(255,255,255,0.06)" : "transparent",
|
|
604
|
+
transition: "background 0.12s ease"
|
|
605
|
+
},
|
|
606
|
+
children: [
|
|
607
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "#a1a1aa", minWidth: 88, flexShrink: 0, paddingTop: 2 }, children: label }),
|
|
608
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
609
|
+
"span",
|
|
610
|
+
{
|
|
611
|
+
style: {
|
|
612
|
+
flex: 1,
|
|
613
|
+
minWidth: 0,
|
|
614
|
+
wordBreak: "break-all",
|
|
615
|
+
paddingTop: 2,
|
|
616
|
+
lineHeight: 1.35
|
|
617
|
+
},
|
|
618
|
+
children
|
|
619
|
+
}
|
|
620
|
+
),
|
|
621
|
+
canCopy && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
622
|
+
"span",
|
|
623
|
+
{
|
|
624
|
+
style: {
|
|
625
|
+
flexShrink: 0,
|
|
626
|
+
color: copiedKey === rowKey ? "#22c55e" : "#71717a",
|
|
627
|
+
opacity: hover || copiedKey === rowKey ? 1 : 0,
|
|
628
|
+
transition: "opacity 0.12s ease, color 0.12s ease",
|
|
629
|
+
paddingTop: 2,
|
|
630
|
+
display: "flex",
|
|
631
|
+
alignItems: "flex-start"
|
|
632
|
+
},
|
|
633
|
+
title: "Copy value",
|
|
634
|
+
children: copiedKey === rowKey ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontSize: 10 }, children: "\u2713" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ClipboardIcon, {})
|
|
635
|
+
}
|
|
636
|
+
)
|
|
637
|
+
]
|
|
638
|
+
}
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
function BasicDevToolbar({ enabled = true, debug }) {
|
|
642
|
+
const {
|
|
643
|
+
isReady,
|
|
644
|
+
isSignedIn,
|
|
645
|
+
user,
|
|
646
|
+
did,
|
|
647
|
+
scope,
|
|
648
|
+
missingScopes,
|
|
649
|
+
dbMode,
|
|
650
|
+
dbStatus,
|
|
651
|
+
devInfo,
|
|
652
|
+
refreshSchemaStatus
|
|
653
|
+
} = useBasic();
|
|
654
|
+
const [open, setOpen] = (0, import_react2.useState)(false);
|
|
655
|
+
const [refreshing, setRefreshing] = (0, import_react2.useState)(false);
|
|
656
|
+
const [copied, setCopied] = (0, import_react2.useState)(false);
|
|
657
|
+
const [rowCopied, setRowCopied] = (0, import_react2.useState)(null);
|
|
658
|
+
const show = enabled && typeof window !== "undefined" && isDevelopment(debug);
|
|
659
|
+
const authTone = toneForAuth(isReady, isSignedIn);
|
|
660
|
+
const dbTone = toneForDb(dbMode, dbStatus);
|
|
661
|
+
const schemaTone = toneForSchema(devInfo);
|
|
662
|
+
const syncTone = dbMode === "remote" ? "muted" : dbTone === "ok" || dbStatus === "SYNCING" /* SYNCING */ ? "ok" : dbTone === "warn" ? "warn" : dbTone === "bad" ? "bad" : "muted";
|
|
663
|
+
const handleRefreshSchema = (0, import_react2.useCallback)(async () => {
|
|
664
|
+
setRefreshing(true);
|
|
665
|
+
try {
|
|
666
|
+
await refreshSchemaStatus();
|
|
667
|
+
} finally {
|
|
668
|
+
setRefreshing(false);
|
|
669
|
+
}
|
|
670
|
+
}, [refreshSchemaStatus]);
|
|
671
|
+
const missingList = missingScopes();
|
|
672
|
+
const debugPayload = (0, import_react2.useMemo)(() => {
|
|
673
|
+
return {
|
|
674
|
+
sdkVersion: version,
|
|
675
|
+
isReady,
|
|
676
|
+
isSignedIn,
|
|
677
|
+
did: did ?? null,
|
|
678
|
+
user: user ? {
|
|
679
|
+
sub: user.sub,
|
|
680
|
+
email: user.email,
|
|
681
|
+
name: user.name,
|
|
682
|
+
picture: user.picture
|
|
683
|
+
} : null,
|
|
684
|
+
scope,
|
|
685
|
+
missingScopes: missingList,
|
|
686
|
+
dbMode,
|
|
687
|
+
dbStatus,
|
|
688
|
+
indexedDbName: dbMode === "sync" ? INDEXED_DB_NAME : null,
|
|
689
|
+
schema: devInfo
|
|
690
|
+
};
|
|
691
|
+
}, [isReady, isSignedIn, did, user, scope, dbMode, dbStatus, devInfo, missingList]);
|
|
692
|
+
const handleCopy = (0, import_react2.useCallback)(async () => {
|
|
693
|
+
try {
|
|
694
|
+
await navigator.clipboard.writeText(JSON.stringify(debugPayload, null, 2));
|
|
695
|
+
setCopied(true);
|
|
696
|
+
setTimeout(() => setCopied(false), 2e3);
|
|
697
|
+
} catch {
|
|
698
|
+
}
|
|
699
|
+
}, [debugPayload]);
|
|
700
|
+
const onRowCopied = (0, import_react2.useCallback)((key) => {
|
|
701
|
+
setRowCopied(key);
|
|
702
|
+
setTimeout(() => setRowCopied((k) => k === key ? null : k), 1500);
|
|
703
|
+
}, []);
|
|
704
|
+
if (!show) return null;
|
|
705
|
+
const shell = {
|
|
706
|
+
position: "fixed",
|
|
707
|
+
bottom: 12,
|
|
708
|
+
left: "50%",
|
|
709
|
+
transform: "translateX(-50%)",
|
|
710
|
+
zIndex: 99999,
|
|
711
|
+
fontFamily: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
|
|
712
|
+
fontSize: 11,
|
|
713
|
+
color: "#e4e4e7",
|
|
714
|
+
pointerEvents: "auto"
|
|
715
|
+
};
|
|
716
|
+
const bar = {
|
|
717
|
+
display: "flex",
|
|
718
|
+
alignItems: "center",
|
|
719
|
+
gap: 8,
|
|
720
|
+
padding: "8px 12px",
|
|
721
|
+
borderRadius: 999,
|
|
722
|
+
background: "rgba(24, 24, 27, 0.92)",
|
|
723
|
+
border: "1px solid rgba(63, 63, 70, 0.9)",
|
|
724
|
+
boxShadow: "0 8px 32px rgba(0,0,0,0.35)",
|
|
725
|
+
cursor: "pointer",
|
|
726
|
+
userSelect: "none"
|
|
727
|
+
};
|
|
728
|
+
const dot = (tone) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
729
|
+
"span",
|
|
730
|
+
{
|
|
731
|
+
style: {
|
|
732
|
+
display: "block",
|
|
733
|
+
boxSizing: "border-box",
|
|
734
|
+
width: 6,
|
|
735
|
+
height: 6,
|
|
736
|
+
minWidth: 6,
|
|
737
|
+
minHeight: 6,
|
|
738
|
+
maxWidth: 6,
|
|
739
|
+
maxHeight: 6,
|
|
740
|
+
borderRadius: "50%",
|
|
741
|
+
background: chipColor(tone),
|
|
742
|
+
flexShrink: 0
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
);
|
|
746
|
+
const dotSlot = (title, tone) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
747
|
+
"span",
|
|
748
|
+
{
|
|
749
|
+
title,
|
|
750
|
+
style: {
|
|
751
|
+
display: "inline-flex",
|
|
752
|
+
alignItems: "center",
|
|
753
|
+
justifyContent: "center",
|
|
754
|
+
width: 6,
|
|
755
|
+
height: 6,
|
|
756
|
+
flexShrink: 0,
|
|
757
|
+
lineHeight: 0
|
|
758
|
+
},
|
|
759
|
+
children: dot(tone)
|
|
760
|
+
}
|
|
761
|
+
);
|
|
762
|
+
const panel = {
|
|
763
|
+
marginBottom: 8,
|
|
764
|
+
maxHeight: "50vh",
|
|
765
|
+
overflow: "auto",
|
|
766
|
+
padding: PANEL_PAD_X,
|
|
767
|
+
borderRadius: 10,
|
|
768
|
+
background: "rgba(24, 24, 27, 0.96)",
|
|
769
|
+
border: "1px solid rgba(63, 63, 70, 0.9)",
|
|
770
|
+
boxShadow: "0 8px 32px rgba(0,0,0,0.35)",
|
|
771
|
+
minWidth: 300,
|
|
772
|
+
maxWidth: "min(560px, calc(100vw - 24px))"
|
|
773
|
+
};
|
|
774
|
+
const syncStatusText = dbStatusLabel(dbStatus);
|
|
775
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: shell, children: [
|
|
776
|
+
open && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: panel, children: [
|
|
777
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginBottom: 12 }, children: [
|
|
778
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: { fontWeight: 600, fontSize: 12 }, children: "Basic SDK" }),
|
|
779
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { color: "#71717a", fontSize: 10, marginTop: 2 }, children: [
|
|
780
|
+
"v",
|
|
781
|
+
version
|
|
782
|
+
] })
|
|
783
|
+
] }),
|
|
784
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionHeader, { children: "Auth" }),
|
|
785
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
786
|
+
CopyableRow,
|
|
787
|
+
{
|
|
788
|
+
rowKey: "ready",
|
|
789
|
+
label: "Ready",
|
|
790
|
+
copyText: String(isReady),
|
|
791
|
+
copiedKey: rowCopied,
|
|
792
|
+
onCopied: onRowCopied,
|
|
793
|
+
children: String(isReady)
|
|
794
|
+
}
|
|
795
|
+
),
|
|
796
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
797
|
+
CopyableRow,
|
|
798
|
+
{
|
|
799
|
+
rowKey: "signedIn",
|
|
800
|
+
label: "Signed in",
|
|
801
|
+
copyText: String(isSignedIn),
|
|
802
|
+
copiedKey: rowCopied,
|
|
803
|
+
onCopied: onRowCopied,
|
|
804
|
+
children: String(isSignedIn)
|
|
805
|
+
}
|
|
806
|
+
),
|
|
807
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
808
|
+
CopyableRow,
|
|
809
|
+
{
|
|
810
|
+
rowKey: "did",
|
|
811
|
+
label: "DID",
|
|
812
|
+
copyText: did || "",
|
|
813
|
+
copiedKey: rowCopied,
|
|
814
|
+
onCopied: onRowCopied,
|
|
815
|
+
children: displayDid(did)
|
|
816
|
+
}
|
|
817
|
+
),
|
|
818
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
819
|
+
CopyableRow,
|
|
820
|
+
{
|
|
821
|
+
rowKey: "user",
|
|
822
|
+
label: "User",
|
|
823
|
+
copyText: user ? displayUserLine(user) : "",
|
|
824
|
+
copiedKey: rowCopied,
|
|
825
|
+
onCopied: onRowCopied,
|
|
826
|
+
children: user ? displayUserLine(user) : "\u2014"
|
|
827
|
+
}
|
|
828
|
+
),
|
|
829
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
830
|
+
CopyableRow,
|
|
831
|
+
{
|
|
832
|
+
rowKey: "scopes",
|
|
833
|
+
label: "Scopes",
|
|
834
|
+
copyText: scope || "",
|
|
835
|
+
copiedKey: rowCopied,
|
|
836
|
+
onCopied: onRowCopied,
|
|
837
|
+
children: scope || "\u2014"
|
|
838
|
+
}
|
|
839
|
+
),
|
|
840
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
841
|
+
CopyableRow,
|
|
842
|
+
{
|
|
843
|
+
rowKey: "missingScopes",
|
|
844
|
+
label: "Missing scopes",
|
|
845
|
+
copyText: missingList.length ? missingList.join(", ") : "",
|
|
846
|
+
copiedKey: rowCopied,
|
|
847
|
+
onCopied: onRowCopied,
|
|
848
|
+
children: missingList.length ? missingList.join(", ") : "\u2014"
|
|
849
|
+
}
|
|
850
|
+
),
|
|
851
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionRule, {}),
|
|
852
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionHeader, { children: "Database" }),
|
|
853
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
854
|
+
CopyableRow,
|
|
855
|
+
{
|
|
856
|
+
rowKey: "dbMode",
|
|
857
|
+
label: "Mode",
|
|
858
|
+
copyText: dbMode,
|
|
859
|
+
copiedKey: rowCopied,
|
|
860
|
+
onCopied: onRowCopied,
|
|
861
|
+
children: dbMode
|
|
862
|
+
}
|
|
863
|
+
),
|
|
864
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
865
|
+
CopyableRow,
|
|
866
|
+
{
|
|
867
|
+
rowKey: "indexedDb",
|
|
868
|
+
label: "IndexedDB",
|
|
869
|
+
copyText: dbMode === "sync" ? INDEXED_DB_NAME : "",
|
|
870
|
+
copiedKey: rowCopied,
|
|
871
|
+
onCopied: onRowCopied,
|
|
872
|
+
children: dbMode === "sync" ? INDEXED_DB_NAME : "\u2014"
|
|
873
|
+
}
|
|
874
|
+
),
|
|
875
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
876
|
+
CopyableRow,
|
|
877
|
+
{
|
|
878
|
+
rowKey: "syncStatus",
|
|
879
|
+
label: "Sync / status",
|
|
880
|
+
copyText: syncStatusText,
|
|
881
|
+
copiedKey: rowCopied,
|
|
882
|
+
onCopied: onRowCopied,
|
|
883
|
+
children: syncStatusText
|
|
884
|
+
}
|
|
885
|
+
),
|
|
886
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionRule, {}),
|
|
887
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionHeader, { children: "Schema" }),
|
|
888
|
+
devInfo ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
889
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
890
|
+
CopyableRow,
|
|
891
|
+
{
|
|
892
|
+
rowKey: "schemaProject",
|
|
893
|
+
label: "Project",
|
|
894
|
+
copyText: devInfo.projectId ?? "",
|
|
895
|
+
copiedKey: rowCopied,
|
|
896
|
+
onCopied: onRowCopied,
|
|
897
|
+
children: devInfo.projectId ?? "\u2014"
|
|
898
|
+
}
|
|
899
|
+
),
|
|
900
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
901
|
+
CopyableRow,
|
|
902
|
+
{
|
|
903
|
+
rowKey: "schemaLocalVer",
|
|
904
|
+
label: "Local version",
|
|
905
|
+
copyText: devInfo.localVersion !== void 0 && devInfo.localVersion !== null ? String(devInfo.localVersion) : "",
|
|
906
|
+
copiedKey: rowCopied,
|
|
907
|
+
onCopied: onRowCopied,
|
|
908
|
+
children: devInfo.localVersion ?? "\u2014"
|
|
909
|
+
}
|
|
910
|
+
),
|
|
911
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
912
|
+
CopyableRow,
|
|
913
|
+
{
|
|
914
|
+
rowKey: "schemaRemote",
|
|
915
|
+
label: "Remote check",
|
|
916
|
+
copyText: devInfo.status,
|
|
917
|
+
copiedKey: rowCopied,
|
|
918
|
+
onCopied: onRowCopied,
|
|
919
|
+
children: devInfo.status
|
|
920
|
+
}
|
|
921
|
+
),
|
|
922
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
923
|
+
CopyableRow,
|
|
924
|
+
{
|
|
925
|
+
rowKey: "schemaValid",
|
|
926
|
+
label: "Valid",
|
|
927
|
+
copyText: String(devInfo.valid),
|
|
928
|
+
copiedKey: rowCopied,
|
|
929
|
+
onCopied: onRowCopied,
|
|
930
|
+
children: String(devInfo.valid)
|
|
931
|
+
}
|
|
932
|
+
),
|
|
933
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
934
|
+
CopyableRow,
|
|
935
|
+
{
|
|
936
|
+
rowKey: "schemaChecked",
|
|
937
|
+
label: "Checked",
|
|
938
|
+
copyText: devInfo.lastCheckedAt ? new Date(devInfo.lastCheckedAt).toISOString() : "",
|
|
939
|
+
copiedKey: rowCopied,
|
|
940
|
+
onCopied: onRowCopied,
|
|
941
|
+
children: devInfo.lastCheckedAt ? new Date(devInfo.lastCheckedAt).toLocaleString() : "\u2014"
|
|
942
|
+
}
|
|
943
|
+
),
|
|
944
|
+
devInfo.error ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
945
|
+
CopyableRow,
|
|
946
|
+
{
|
|
947
|
+
rowKey: "schemaError",
|
|
948
|
+
label: "Error",
|
|
949
|
+
copyText: devInfo.error,
|
|
950
|
+
copiedKey: rowCopied,
|
|
951
|
+
onCopied: onRowCopied,
|
|
952
|
+
children: devInfo.error
|
|
953
|
+
}
|
|
954
|
+
) : null
|
|
955
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
956
|
+
CopyableRow,
|
|
957
|
+
{
|
|
958
|
+
rowKey: "schemaStatus",
|
|
959
|
+
label: "Status",
|
|
960
|
+
copyText: "No schema on provider",
|
|
961
|
+
copiedKey: rowCopied,
|
|
962
|
+
onCopied: onRowCopied,
|
|
963
|
+
children: "No schema on provider"
|
|
964
|
+
}
|
|
965
|
+
),
|
|
966
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", gap: 8, marginTop: 12, flexWrap: "wrap" }, children: [
|
|
967
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
968
|
+
"button",
|
|
969
|
+
{
|
|
970
|
+
type: "button",
|
|
971
|
+
onClick: (e) => {
|
|
972
|
+
e.stopPropagation();
|
|
973
|
+
void handleRefreshSchema();
|
|
974
|
+
},
|
|
975
|
+
disabled: refreshing,
|
|
976
|
+
style: {
|
|
977
|
+
padding: "6px 10px",
|
|
978
|
+
borderRadius: 6,
|
|
979
|
+
border: "1px solid #3f3f46",
|
|
980
|
+
background: "#27272a",
|
|
981
|
+
color: "#e4e4e7",
|
|
982
|
+
cursor: refreshing ? "wait" : "pointer",
|
|
983
|
+
fontSize: 11,
|
|
984
|
+
fontFamily: "inherit"
|
|
985
|
+
},
|
|
986
|
+
children: refreshing ? "Refreshing\u2026" : "Refresh schema"
|
|
987
|
+
}
|
|
988
|
+
),
|
|
989
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
990
|
+
"button",
|
|
991
|
+
{
|
|
992
|
+
type: "button",
|
|
993
|
+
onClick: (e) => {
|
|
994
|
+
e.stopPropagation();
|
|
995
|
+
void handleCopy();
|
|
996
|
+
},
|
|
997
|
+
style: {
|
|
998
|
+
padding: "6px 10px",
|
|
999
|
+
borderRadius: 6,
|
|
1000
|
+
border: "1px solid #3f3f46",
|
|
1001
|
+
background: "#27272a",
|
|
1002
|
+
color: "#e4e4e7",
|
|
1003
|
+
cursor: "pointer",
|
|
1004
|
+
fontSize: 11,
|
|
1005
|
+
fontFamily: "inherit"
|
|
1006
|
+
},
|
|
1007
|
+
children: copied ? "Copied" : "Copy debug info"
|
|
1008
|
+
}
|
|
1009
|
+
)
|
|
1010
|
+
] })
|
|
1011
|
+
] }),
|
|
1012
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1013
|
+
"button",
|
|
1014
|
+
{
|
|
1015
|
+
type: "button",
|
|
1016
|
+
"aria-expanded": open,
|
|
1017
|
+
onClick: () => setOpen((o) => !o),
|
|
1018
|
+
style: {
|
|
1019
|
+
...bar,
|
|
1020
|
+
border: "none",
|
|
1021
|
+
width: "100%",
|
|
1022
|
+
cursor: "pointer"
|
|
1023
|
+
},
|
|
1024
|
+
children: [
|
|
1025
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { fontWeight: 600, letterSpacing: 0.02 }, children: "Basic" }),
|
|
1026
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1027
|
+
"span",
|
|
1028
|
+
{
|
|
1029
|
+
style: {
|
|
1030
|
+
display: "inline-flex",
|
|
1031
|
+
alignItems: "center",
|
|
1032
|
+
gap: 6,
|
|
1033
|
+
marginLeft: 8,
|
|
1034
|
+
height: 6,
|
|
1035
|
+
flexShrink: 0,
|
|
1036
|
+
lineHeight: 0
|
|
1037
|
+
},
|
|
1038
|
+
children: [
|
|
1039
|
+
dotSlot("Auth", authTone),
|
|
1040
|
+
dotSlot("DB", dbTone),
|
|
1041
|
+
dotSlot("Sync", syncTone),
|
|
1042
|
+
dotSlot("Schema", schemaTone)
|
|
1043
|
+
]
|
|
1044
|
+
}
|
|
1045
|
+
),
|
|
1046
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: "#71717a", marginLeft: 4 }, children: open ? "\u25BE" : "\u25B4" })
|
|
1047
|
+
]
|
|
1048
|
+
}
|
|
1049
|
+
)
|
|
1050
|
+
] });
|
|
1051
|
+
}
|
|
1052
|
+
var import_react2, import_jsx_runtime, INDEXED_DB_NAME, PANEL_PAD_X;
|
|
1053
|
+
var init_BasicDevToolbar = __esm({
|
|
1054
|
+
"src/dev/BasicDevToolbar.tsx"() {
|
|
1055
|
+
"use strict";
|
|
1056
|
+
"use client";
|
|
1057
|
+
import_react2 = require("react");
|
|
1058
|
+
init_context();
|
|
1059
|
+
init_package();
|
|
1060
|
+
init_network();
|
|
1061
|
+
import_jsx_runtime = require("react/jsx-runtime");
|
|
1062
|
+
INDEXED_DB_NAME = "basicdb";
|
|
1063
|
+
PANEL_PAD_X = 12;
|
|
1064
|
+
}
|
|
1065
|
+
});
|
|
1066
|
+
|
|
241
1067
|
// src/index.ts
|
|
242
1068
|
var index_exports = {};
|
|
243
1069
|
__export(index_exports, {
|
|
1070
|
+
BasicDevToolbar: () => BasicDevToolbar,
|
|
244
1071
|
BasicProvider: () => BasicProvider,
|
|
245
1072
|
DBStatus: () => DBStatus,
|
|
246
1073
|
NotAuthenticatedError: () => NotAuthenticatedError,
|
|
@@ -257,7 +1084,7 @@ __export(index_exports, {
|
|
|
257
1084
|
module.exports = __toCommonJS(index_exports);
|
|
258
1085
|
|
|
259
1086
|
// src/AuthContext.tsx
|
|
260
|
-
var
|
|
1087
|
+
var import_react3 = require("react");
|
|
261
1088
|
|
|
262
1089
|
// src/sync/index.ts
|
|
263
1090
|
var import_uuid = require("uuid");
|
|
@@ -848,75 +1675,8 @@ async function resolveHandle(handle) {
|
|
|
848
1675
|
return resolved;
|
|
849
1676
|
}
|
|
850
1677
|
|
|
851
|
-
// src/utils/network.ts
|
|
852
|
-
init_config();
|
|
853
|
-
|
|
854
|
-
// package.json
|
|
855
|
-
var version = "0.8.0-beta.1";
|
|
856
|
-
|
|
857
|
-
// src/utils/network.ts
|
|
858
|
-
function isDevelopment(debug) {
|
|
859
|
-
return window.location.hostname === "localhost" || window.location.hostname === "127.0.0.1" || window.location.hostname.includes("localhost") || window.location.hostname.includes("127.0.0.1") || window.location.hostname.includes(".local") || process.env.NODE_ENV === "development" || debug === true;
|
|
860
|
-
}
|
|
861
|
-
async function checkForNewVersion() {
|
|
862
|
-
try {
|
|
863
|
-
const isBeta = version.includes("beta");
|
|
864
|
-
const response = await fetch(`https://registry.npmjs.org/@basictech/react/${isBeta ? "beta" : "latest"}`);
|
|
865
|
-
if (!response.ok) {
|
|
866
|
-
throw new Error("Failed to fetch version from npm");
|
|
867
|
-
}
|
|
868
|
-
const data = await response.json();
|
|
869
|
-
const latestVersion = data.version;
|
|
870
|
-
if (latestVersion !== version) {
|
|
871
|
-
console.warn("[basic] New version available:", latestVersion, `
|
|
872
|
-
run "npm install @basictech/react@${latestVersion}" to update`);
|
|
873
|
-
}
|
|
874
|
-
if (isBeta) {
|
|
875
|
-
log("thank you for being on basictech/react beta :)");
|
|
876
|
-
}
|
|
877
|
-
return {
|
|
878
|
-
hasNewVersion: version !== latestVersion,
|
|
879
|
-
latestVersion,
|
|
880
|
-
currentVersion: version
|
|
881
|
-
};
|
|
882
|
-
} catch (error) {
|
|
883
|
-
log("Error checking for new version:", error);
|
|
884
|
-
return {
|
|
885
|
-
hasNewVersion: false,
|
|
886
|
-
latestVersion: null,
|
|
887
|
-
currentVersion: null
|
|
888
|
-
};
|
|
889
|
-
}
|
|
890
|
-
}
|
|
891
|
-
function cleanOAuthParamsFromUrl() {
|
|
892
|
-
if (window.location.search.includes("code") || window.location.search.includes("state")) {
|
|
893
|
-
const url = new URL(window.location.href);
|
|
894
|
-
url.searchParams.delete("code");
|
|
895
|
-
url.searchParams.delete("state");
|
|
896
|
-
window.history.pushState({}, document.title, url.pathname + url.search);
|
|
897
|
-
log("Cleaned OAuth parameters from URL");
|
|
898
|
-
}
|
|
899
|
-
}
|
|
900
|
-
function getSyncStatus(statusCode) {
|
|
901
|
-
switch (statusCode) {
|
|
902
|
-
case -1:
|
|
903
|
-
return "ERROR";
|
|
904
|
-
case 0:
|
|
905
|
-
return "OFFLINE";
|
|
906
|
-
case 1:
|
|
907
|
-
return "CONNECTING";
|
|
908
|
-
case 2:
|
|
909
|
-
return "ONLINE";
|
|
910
|
-
case 3:
|
|
911
|
-
return "SYNCING";
|
|
912
|
-
case 4:
|
|
913
|
-
return "ERROR_WILL_RETRY";
|
|
914
|
-
default:
|
|
915
|
-
return "UNKNOWN";
|
|
916
|
-
}
|
|
917
|
-
}
|
|
918
|
-
|
|
919
1678
|
// src/core/auth/AuthManager.ts
|
|
1679
|
+
init_network();
|
|
920
1680
|
init_config();
|
|
921
1681
|
function generateCodeVerifier() {
|
|
922
1682
|
const array = new Uint8Array(32);
|
|
@@ -1064,8 +1824,11 @@ var AuthManager = class {
|
|
|
1064
1824
|
const refreshToken = await this.storage.get(STORAGE_KEYS.REFRESH_TOKEN);
|
|
1065
1825
|
if (refreshToken) {
|
|
1066
1826
|
log("Found refresh token in storage, attempting to refresh access token");
|
|
1067
|
-
this.exchangeToken(refreshToken, true).catch((error) => {
|
|
1827
|
+
this.exchangeToken(refreshToken, true).catch(async (error) => {
|
|
1068
1828
|
log("Error fetching refresh token:", error);
|
|
1829
|
+
if (this.isNetworkError(error)) {
|
|
1830
|
+
await this.restoreCachedUser();
|
|
1831
|
+
}
|
|
1069
1832
|
});
|
|
1070
1833
|
} else {
|
|
1071
1834
|
const cachedUserInfo = await this.storage.get(STORAGE_KEYS.USER_INFO);
|
|
@@ -1134,7 +1897,8 @@ var AuthManager = class {
|
|
|
1134
1897
|
log("Token refresh already in progress, waiting...");
|
|
1135
1898
|
try {
|
|
1136
1899
|
const newToken = await this.refreshPromise;
|
|
1137
|
-
|
|
1900
|
+
if (!newToken?.access_token) throw new Error("Token refresh returned empty access token");
|
|
1901
|
+
return newToken.access_token;
|
|
1138
1902
|
} catch (error) {
|
|
1139
1903
|
log("In-flight refresh failed:", error);
|
|
1140
1904
|
if (this.isNetworkError(error)) {
|
|
@@ -1148,7 +1912,8 @@ var AuthManager = class {
|
|
|
1148
1912
|
if (refreshToken) {
|
|
1149
1913
|
try {
|
|
1150
1914
|
const newToken = await this.exchangeToken(refreshToken, true);
|
|
1151
|
-
|
|
1915
|
+
if (!newToken?.access_token) throw new Error("Token refresh returned empty access token");
|
|
1916
|
+
return newToken.access_token;
|
|
1152
1917
|
} catch (error) {
|
|
1153
1918
|
log("Failed to refresh expired token:", error);
|
|
1154
1919
|
if (this.isNetworkError(error)) {
|
|
@@ -1161,7 +1926,8 @@ var AuthManager = class {
|
|
|
1161
1926
|
throw new Error("no refresh token available");
|
|
1162
1927
|
}
|
|
1163
1928
|
}
|
|
1164
|
-
|
|
1929
|
+
if (!this.token.access_token) throw new Error("Token exists but access_token is empty");
|
|
1930
|
+
return this.token.access_token;
|
|
1165
1931
|
}
|
|
1166
1932
|
async getSignInUrl(redirectUri, endpoints) {
|
|
1167
1933
|
log("getting sign in link...");
|
|
@@ -1170,7 +1936,7 @@ var AuthManager = class {
|
|
|
1170
1936
|
}
|
|
1171
1937
|
const pdsEndpoints = endpoints || this.defaultPdsEndpoints();
|
|
1172
1938
|
await this.storage.set(STORAGE_KEYS.PDS_ENDPOINTS, JSON.stringify(pdsEndpoints));
|
|
1173
|
-
const randomState =
|
|
1939
|
+
const randomState = base64UrlEncode(crypto.getRandomValues(new Uint8Array(16)));
|
|
1174
1940
|
await this.storage.set(STORAGE_KEYS.AUTH_STATE, randomState);
|
|
1175
1941
|
const redirectUrl = redirectUri || window.location.href;
|
|
1176
1942
|
if (!redirectUrl || !redirectUrl.startsWith("http://") && !redirectUrl.startsWith("https://")) {
|
|
@@ -1289,7 +2055,10 @@ var AuthManager = class {
|
|
|
1289
2055
|
return requested.filter((s) => !granted.has(s));
|
|
1290
2056
|
}
|
|
1291
2057
|
/**
|
|
1292
|
-
* Register online/offline handlers that retry pending
|
|
2058
|
+
* Register online/offline and visibility handlers that retry pending
|
|
2059
|
+
* refreshes and proactively refresh tokens when the app resumes from
|
|
2060
|
+
* background (critical for PWAs and mobile browsers where timers are
|
|
2061
|
+
* frozen while backgrounded).
|
|
1293
2062
|
* Returns a cleanup function for useEffect teardown.
|
|
1294
2063
|
*/
|
|
1295
2064
|
setupNetworkListeners() {
|
|
@@ -1311,11 +2080,25 @@ var AuthManager = class {
|
|
|
1311
2080
|
log("Network went offline");
|
|
1312
2081
|
this.isOnline = false;
|
|
1313
2082
|
};
|
|
2083
|
+
const handleVisibilityChange = () => {
|
|
2084
|
+
if (document.visibilityState === "visible" && this.isSignedIn) {
|
|
2085
|
+
log("App became visible - checking token freshness");
|
|
2086
|
+
this.getToken().catch((err) => {
|
|
2087
|
+
log("Token refresh on visibility resume failed:", err);
|
|
2088
|
+
});
|
|
2089
|
+
}
|
|
2090
|
+
};
|
|
1314
2091
|
window.addEventListener("online", handleOnline);
|
|
1315
2092
|
window.addEventListener("offline", handleOffline);
|
|
2093
|
+
if (typeof document !== "undefined") {
|
|
2094
|
+
document.addEventListener("visibilitychange", handleVisibilityChange);
|
|
2095
|
+
}
|
|
1316
2096
|
return () => {
|
|
1317
2097
|
window.removeEventListener("online", handleOnline);
|
|
1318
2098
|
window.removeEventListener("offline", handleOffline);
|
|
2099
|
+
if (typeof document !== "undefined") {
|
|
2100
|
+
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
|
2101
|
+
}
|
|
1319
2102
|
};
|
|
1320
2103
|
}
|
|
1321
2104
|
// ------------------------------------------------------------------
|
|
@@ -1378,39 +2161,26 @@ var AuthManager = class {
|
|
|
1378
2161
|
const decoded = (0, import_jwt_decode.jwtDecode)(this.token.access_token);
|
|
1379
2162
|
if (decoded.sub) this.did = decoded.sub;
|
|
1380
2163
|
if (decoded.scope) this.tokenScope = decoded.scope;
|
|
1381
|
-
|
|
1382
|
-
const isExpired = decoded.exp && decoded.exp < Date.now() / 1e3 + expirationBuffer;
|
|
1383
|
-
if (isExpired) {
|
|
1384
|
-
log("token is expired - refreshing ...");
|
|
1385
|
-
const refreshToken = this.token.refresh_token;
|
|
1386
|
-
if (!refreshToken) {
|
|
1387
|
-
log("Error: No refresh token available for expired token");
|
|
1388
|
-
this.isAuthReady = true;
|
|
1389
|
-
this.notify();
|
|
1390
|
-
return;
|
|
1391
|
-
}
|
|
1392
|
-
try {
|
|
1393
|
-
const newToken = await this.exchangeToken(refreshToken, true);
|
|
1394
|
-
await this.fetchUser(newToken?.access_token || "");
|
|
1395
|
-
} catch (error) {
|
|
1396
|
-
log("Failed to refresh token in processNewToken:", error);
|
|
1397
|
-
if (this.isNetworkError(error)) {
|
|
1398
|
-
log("Network issue - continuing with expired token until online");
|
|
1399
|
-
await this.fetchUser(this.token.access_token);
|
|
1400
|
-
} else {
|
|
1401
|
-
this.isAuthReady = true;
|
|
1402
|
-
this.notify();
|
|
1403
|
-
}
|
|
1404
|
-
}
|
|
1405
|
-
} else {
|
|
1406
|
-
await this.fetchUser(this.token.access_token);
|
|
1407
|
-
}
|
|
2164
|
+
await this.fetchUser(this.token.access_token);
|
|
1408
2165
|
} catch (error) {
|
|
1409
2166
|
log("Error processing token:", error);
|
|
1410
2167
|
this.isAuthReady = true;
|
|
1411
2168
|
this.notify();
|
|
1412
2169
|
}
|
|
1413
2170
|
}
|
|
2171
|
+
async restoreCachedUser() {
|
|
2172
|
+
const cached = await this.storage.get(STORAGE_KEYS.USER_INFO);
|
|
2173
|
+
if (cached) {
|
|
2174
|
+
try {
|
|
2175
|
+
this.user = JSON.parse(cached);
|
|
2176
|
+
this.isSignedIn = true;
|
|
2177
|
+
log("Restored cached user info for offline mode");
|
|
2178
|
+
} catch {
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
this.isAuthReady = true;
|
|
2182
|
+
this.notify();
|
|
2183
|
+
}
|
|
1414
2184
|
async fetchUser(accessToken) {
|
|
1415
2185
|
log("fetching user");
|
|
1416
2186
|
try {
|
|
@@ -1444,8 +2214,12 @@ var AuthManager = class {
|
|
|
1444
2214
|
this.notify();
|
|
1445
2215
|
} catch (error) {
|
|
1446
2216
|
log("Failed to fetch user info:", error);
|
|
1447
|
-
this.
|
|
1448
|
-
|
|
2217
|
+
if (this.isNetworkError(error)) {
|
|
2218
|
+
await this.restoreCachedUser();
|
|
2219
|
+
} else {
|
|
2220
|
+
this.isAuthReady = true;
|
|
2221
|
+
this.notify();
|
|
2222
|
+
}
|
|
1449
2223
|
}
|
|
1450
2224
|
}
|
|
1451
2225
|
/**
|
|
@@ -1541,9 +2315,12 @@ var AuthManager = class {
|
|
|
1541
2315
|
this.pendingRefresh = true;
|
|
1542
2316
|
throw new Error("Network issue - refresh will be retried when online");
|
|
1543
2317
|
}
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
2318
|
+
const definitiveErrors = ["invalid_grant", "invalid_client", "unauthorized_client"];
|
|
2319
|
+
if (typeof token.error === "string" && definitiveErrors.includes(token.error)) {
|
|
2320
|
+
await this.clearStoredAuth();
|
|
2321
|
+
this.resetAuthState();
|
|
2322
|
+
this.notify();
|
|
2323
|
+
}
|
|
1547
2324
|
throw new Error(`Token refresh failed: ${token.error}`);
|
|
1548
2325
|
} else {
|
|
1549
2326
|
this.token = token;
|
|
@@ -1564,7 +2341,9 @@ var AuthManager = class {
|
|
|
1564
2341
|
return token;
|
|
1565
2342
|
} catch (error) {
|
|
1566
2343
|
log("Token refresh error:", error);
|
|
1567
|
-
|
|
2344
|
+
const msg = error instanceof Error ? error.message : "";
|
|
2345
|
+
const alreadyHandled = msg.startsWith("Token refresh failed:");
|
|
2346
|
+
if (!alreadyHandled && !this.isNetworkError(error)) {
|
|
1568
2347
|
await this.clearStoredAuth();
|
|
1569
2348
|
this.resetAuthState();
|
|
1570
2349
|
this.notify();
|
|
@@ -1608,6 +2387,7 @@ var AuthManager = class {
|
|
|
1608
2387
|
await this.storage.remove(STORAGE_KEYS.PDS_ENDPOINTS);
|
|
1609
2388
|
}
|
|
1610
2389
|
isNetworkError(error) {
|
|
2390
|
+
if (error instanceof TypeError) return true;
|
|
1611
2391
|
if (error instanceof Error) {
|
|
1612
2392
|
return error.message.includes("offline") || error.message.includes("Network");
|
|
1613
2393
|
}
|
|
@@ -1617,6 +2397,7 @@ var AuthManager = class {
|
|
|
1617
2397
|
|
|
1618
2398
|
// src/AuthContext.tsx
|
|
1619
2399
|
init_config();
|
|
2400
|
+
init_package();
|
|
1620
2401
|
|
|
1621
2402
|
// src/updater/versionUpdater.ts
|
|
1622
2403
|
init_config();
|
|
@@ -1741,6 +2522,9 @@ function getMigrations() {
|
|
|
1741
2522
|
];
|
|
1742
2523
|
}
|
|
1743
2524
|
|
|
2525
|
+
// src/AuthContext.tsx
|
|
2526
|
+
init_network();
|
|
2527
|
+
|
|
1744
2528
|
// src/utils/schema.ts
|
|
1745
2529
|
var import_schema3 = require("@basictech/schema");
|
|
1746
2530
|
init_config();
|
|
@@ -1840,57 +2624,18 @@ async function validateAndCheckSchema(schema) {
|
|
|
1840
2624
|
}
|
|
1841
2625
|
|
|
1842
2626
|
// src/AuthContext.tsx
|
|
1843
|
-
|
|
2627
|
+
init_context();
|
|
2628
|
+
init_context();
|
|
2629
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
2630
|
+
var BasicDevToolbar2 = (0, import_react3.lazy)(
|
|
2631
|
+
() => Promise.resolve().then(() => (init_BasicDevToolbar(), BasicDevToolbar_exports)).then((m) => ({ default: m.BasicDevToolbar }))
|
|
2632
|
+
);
|
|
1844
2633
|
var DEFAULT_AUTH_CONFIG = {
|
|
1845
2634
|
scopes: "profile,email,app:admin",
|
|
1846
2635
|
pds_url: "https://pds.basic.id",
|
|
1847
2636
|
admin_url: "https://api.basic.tech",
|
|
1848
2637
|
ws_url: "wss://pds.basic.id/ws"
|
|
1849
2638
|
};
|
|
1850
|
-
var DBStatus = /* @__PURE__ */ ((DBStatus2) => {
|
|
1851
|
-
DBStatus2["LOADING"] = "LOADING";
|
|
1852
|
-
DBStatus2["OFFLINE"] = "OFFLINE";
|
|
1853
|
-
DBStatus2["CONNECTING"] = "CONNECTING";
|
|
1854
|
-
DBStatus2["ONLINE"] = "ONLINE";
|
|
1855
|
-
DBStatus2["SYNCING"] = "SYNCING";
|
|
1856
|
-
DBStatus2["ERROR"] = "ERROR";
|
|
1857
|
-
DBStatus2["ERROR_WILL_RETRY"] = "ERROR_WILL_RETRY";
|
|
1858
|
-
DBStatus2["ERROR_TOKEN_EXPIRED"] = "ERROR_TOKEN_EXPIRED";
|
|
1859
|
-
return DBStatus2;
|
|
1860
|
-
})(DBStatus || {});
|
|
1861
|
-
var noDb = {
|
|
1862
|
-
collection: () => {
|
|
1863
|
-
throw new Error("no basicdb found - initialization failed. double check your schema.");
|
|
1864
|
-
}
|
|
1865
|
-
};
|
|
1866
|
-
var BasicContext = (0, import_react.createContext)({
|
|
1867
|
-
// Auth state
|
|
1868
|
-
isReady: false,
|
|
1869
|
-
isSignedIn: false,
|
|
1870
|
-
user: null,
|
|
1871
|
-
did: null,
|
|
1872
|
-
scope: null,
|
|
1873
|
-
hasScope: () => false,
|
|
1874
|
-
missingScopes: () => [],
|
|
1875
|
-
// Auth actions
|
|
1876
|
-
signIn: () => Promise.resolve(),
|
|
1877
|
-
signInWithHandle: () => Promise.resolve(),
|
|
1878
|
-
signOut: () => Promise.resolve(),
|
|
1879
|
-
signInWithCode: () => Promise.resolve({ success: false }),
|
|
1880
|
-
// Token management
|
|
1881
|
-
getToken: (_options) => Promise.reject(new Error("no token")),
|
|
1882
|
-
getSignInUrl: () => Promise.resolve(""),
|
|
1883
|
-
// DB access
|
|
1884
|
-
db: noDb,
|
|
1885
|
-
dbStatus: "LOADING" /* LOADING */,
|
|
1886
|
-
dbMode: "sync",
|
|
1887
|
-
// Legacy aliases
|
|
1888
|
-
isAuthReady: false,
|
|
1889
|
-
signin: () => Promise.resolve(),
|
|
1890
|
-
signout: () => Promise.resolve(),
|
|
1891
|
-
signinWithCode: () => Promise.resolve({ success: false }),
|
|
1892
|
-
getSignInLink: () => Promise.resolve("")
|
|
1893
|
-
});
|
|
1894
2639
|
function snapshotAuth(mgr) {
|
|
1895
2640
|
return {
|
|
1896
2641
|
isSignedIn: mgr.isSignedIn,
|
|
@@ -1908,7 +2653,8 @@ function BasicProvider({
|
|
|
1908
2653
|
debug = false,
|
|
1909
2654
|
storage,
|
|
1910
2655
|
auth,
|
|
1911
|
-
dbMode = "sync"
|
|
2656
|
+
dbMode = "sync",
|
|
2657
|
+
devToolbar = false
|
|
1912
2658
|
}) {
|
|
1913
2659
|
const project_id = schema?.project_id || project_id_prop;
|
|
1914
2660
|
if (auth?.server_url && !auth?.pds_url) {
|
|
@@ -1921,9 +2667,11 @@ function BasicProvider({
|
|
|
1921
2667
|
ws_url: auth?.ws_url || DEFAULT_AUTH_CONFIG.ws_url
|
|
1922
2668
|
};
|
|
1923
2669
|
const scopesString = Array.isArray(authConfig.scopes) ? authConfig.scopes.join(" ") : authConfig.scopes;
|
|
1924
|
-
const storageRef = (0,
|
|
2670
|
+
const storageRef = (0, import_react3.useRef)(storage || new LocalStorageAdapter());
|
|
1925
2671
|
const storageAdapter = storageRef.current;
|
|
1926
|
-
const
|
|
2672
|
+
const schemaRef = (0, import_react3.useRef)(schema);
|
|
2673
|
+
schemaRef.current = schema;
|
|
2674
|
+
const [authState, setAuthState] = (0, import_react3.useState)({
|
|
1927
2675
|
isSignedIn: false,
|
|
1928
2676
|
hasToken: false,
|
|
1929
2677
|
isAuthReady: false,
|
|
@@ -1931,7 +2679,7 @@ function BasicProvider({
|
|
|
1931
2679
|
did: null,
|
|
1932
2680
|
tokenScope: null
|
|
1933
2681
|
});
|
|
1934
|
-
const authRef = (0,
|
|
2682
|
+
const authRef = (0, import_react3.useRef)(null);
|
|
1935
2683
|
if (!authRef.current) {
|
|
1936
2684
|
authRef.current = new AuthManager(
|
|
1937
2685
|
{
|
|
@@ -1945,14 +2693,50 @@ function BasicProvider({
|
|
|
1945
2693
|
() => setAuthState(snapshotAuth(authRef.current))
|
|
1946
2694
|
);
|
|
1947
2695
|
}
|
|
1948
|
-
const syncRef = (0,
|
|
1949
|
-
const remoteDbRef = (0,
|
|
1950
|
-
const [shouldConnect, setShouldConnect] = (0,
|
|
1951
|
-
const [dbStatus, setDbStatus] = (0,
|
|
1952
|
-
const [isReady, setIsReady] = (0,
|
|
1953
|
-
const [error, setError] = (0,
|
|
2696
|
+
const syncRef = (0, import_react3.useRef)(null);
|
|
2697
|
+
const remoteDbRef = (0, import_react3.useRef)(null);
|
|
2698
|
+
const [shouldConnect, setShouldConnect] = (0, import_react3.useState)(false);
|
|
2699
|
+
const [dbStatus, setDbStatus] = (0, import_react3.useState)("OFFLINE" /* OFFLINE */);
|
|
2700
|
+
const [isReady, setIsReady] = (0, import_react3.useState)(false);
|
|
2701
|
+
const [error, setError] = (0, import_react3.useState)(null);
|
|
2702
|
+
const [schemaDevInfo, setSchemaDevInfo] = (0, import_react3.useState)(null);
|
|
1954
2703
|
const isDevMode = () => isDevelopment(debug);
|
|
1955
|
-
(0,
|
|
2704
|
+
const refreshSchemaStatus = (0, import_react3.useCallback)(async () => {
|
|
2705
|
+
const s = schemaRef.current;
|
|
2706
|
+
if (!s) {
|
|
2707
|
+
setSchemaDevInfo(
|
|
2708
|
+
project_id ? {
|
|
2709
|
+
projectId: project_id,
|
|
2710
|
+
localVersion: void 0,
|
|
2711
|
+
status: "no_schema",
|
|
2712
|
+
valid: false,
|
|
2713
|
+
lastCheckedAt: Date.now()
|
|
2714
|
+
} : null
|
|
2715
|
+
);
|
|
2716
|
+
return;
|
|
2717
|
+
}
|
|
2718
|
+
const result = await validateAndCheckSchema(s);
|
|
2719
|
+
if (!result.isValid) {
|
|
2720
|
+
const errText = result.errors?.map((e) => e.message || "").join("; ") || "invalid";
|
|
2721
|
+
setSchemaDevInfo({
|
|
2722
|
+
projectId: s.project_id ?? null,
|
|
2723
|
+
localVersion: s.version,
|
|
2724
|
+
status: "invalid",
|
|
2725
|
+
valid: false,
|
|
2726
|
+
lastCheckedAt: Date.now(),
|
|
2727
|
+
error: errText
|
|
2728
|
+
});
|
|
2729
|
+
return;
|
|
2730
|
+
}
|
|
2731
|
+
setSchemaDevInfo({
|
|
2732
|
+
projectId: s.project_id ?? null,
|
|
2733
|
+
localVersion: s.version,
|
|
2734
|
+
status: result.schemaStatus.status ?? "unknown",
|
|
2735
|
+
valid: result.schemaStatus.valid,
|
|
2736
|
+
lastCheckedAt: Date.now()
|
|
2737
|
+
});
|
|
2738
|
+
}, [project_id]);
|
|
2739
|
+
(0, import_react3.useEffect)(() => {
|
|
1956
2740
|
const runVersionUpdater = async () => {
|
|
1957
2741
|
try {
|
|
1958
2742
|
const versionUpdater = createVersionUpdater(storageAdapter, version, getMigrations());
|
|
@@ -1970,7 +2754,7 @@ function BasicProvider({
|
|
|
1970
2754
|
authRef.current.initialize();
|
|
1971
2755
|
return authRef.current.setupNetworkListeners();
|
|
1972
2756
|
}, []);
|
|
1973
|
-
(0,
|
|
2757
|
+
(0, import_react3.useEffect)(() => {
|
|
1974
2758
|
async function initSyncDb(options) {
|
|
1975
2759
|
if (!syncRef.current) {
|
|
1976
2760
|
log("Initializing Basic Sync DB");
|
|
@@ -2013,6 +2797,10 @@ function BasicProvider({
|
|
|
2013
2797
|
debug,
|
|
2014
2798
|
onAuthError: (error2) => {
|
|
2015
2799
|
log("RemoteDB auth error:", error2);
|
|
2800
|
+
if (error2.errorType === "forbidden") {
|
|
2801
|
+
log("403 Forbidden - user lacks required scope, not signing out");
|
|
2802
|
+
return;
|
|
2803
|
+
}
|
|
2016
2804
|
handleSignOut();
|
|
2017
2805
|
}
|
|
2018
2806
|
});
|
|
@@ -2025,11 +2813,19 @@ function BasicProvider({
|
|
|
2025
2813
|
if (!result.isValid) {
|
|
2026
2814
|
let errorMessage = "";
|
|
2027
2815
|
if (result.errors) {
|
|
2028
|
-
result.errors.forEach((
|
|
2029
|
-
errorMessage += `${index + 1}: ${
|
|
2816
|
+
result.errors.forEach((err, index) => {
|
|
2817
|
+
errorMessage += `${index + 1}: ${err.message} - at ${err.instancePath}
|
|
2030
2818
|
`;
|
|
2031
2819
|
});
|
|
2032
2820
|
}
|
|
2821
|
+
setSchemaDevInfo({
|
|
2822
|
+
projectId: schema?.project_id ?? null,
|
|
2823
|
+
localVersion: schema?.version,
|
|
2824
|
+
status: "invalid",
|
|
2825
|
+
valid: false,
|
|
2826
|
+
lastCheckedAt: Date.now(),
|
|
2827
|
+
error: errorMessage.trim() || void 0
|
|
2828
|
+
});
|
|
2033
2829
|
setError({
|
|
2034
2830
|
code: "schema_invalid",
|
|
2035
2831
|
title: "Basic Schema is invalid!",
|
|
@@ -2038,6 +2834,13 @@ function BasicProvider({
|
|
|
2038
2834
|
setIsReady(true);
|
|
2039
2835
|
return null;
|
|
2040
2836
|
}
|
|
2837
|
+
setSchemaDevInfo({
|
|
2838
|
+
projectId: schema?.project_id ?? null,
|
|
2839
|
+
localVersion: schema?.version,
|
|
2840
|
+
status: result.schemaStatus.status ?? "unknown",
|
|
2841
|
+
valid: result.schemaStatus.valid,
|
|
2842
|
+
lastCheckedAt: Date.now()
|
|
2843
|
+
});
|
|
2041
2844
|
if (dbMode === "remote") {
|
|
2042
2845
|
initRemoteDb();
|
|
2043
2846
|
} else {
|
|
@@ -2057,6 +2860,15 @@ function BasicProvider({
|
|
|
2057
2860
|
if (schema) {
|
|
2058
2861
|
checkSchema();
|
|
2059
2862
|
} else {
|
|
2863
|
+
setSchemaDevInfo(
|
|
2864
|
+
project_id ? {
|
|
2865
|
+
projectId: project_id,
|
|
2866
|
+
localVersion: void 0,
|
|
2867
|
+
status: "no_schema",
|
|
2868
|
+
valid: false,
|
|
2869
|
+
lastCheckedAt: Date.now()
|
|
2870
|
+
} : null
|
|
2871
|
+
);
|
|
2060
2872
|
if (dbMode === "remote" && project_id) {
|
|
2061
2873
|
initRemoteDb();
|
|
2062
2874
|
} else {
|
|
@@ -2064,7 +2876,7 @@ function BasicProvider({
|
|
|
2064
2876
|
}
|
|
2065
2877
|
}
|
|
2066
2878
|
}, []);
|
|
2067
|
-
(0,
|
|
2879
|
+
(0, import_react3.useEffect)(() => {
|
|
2068
2880
|
if (authState.hasToken && syncRef.current && authState.isSignedIn && shouldConnect) {
|
|
2069
2881
|
log("connecting to db...");
|
|
2070
2882
|
syncRef.current?.connect({
|
|
@@ -2123,69 +2935,72 @@ function BasicProvider({
|
|
|
2123
2935
|
return syncRef.current || noDb;
|
|
2124
2936
|
};
|
|
2125
2937
|
const contextValue = {
|
|
2126
|
-
// Auth state
|
|
2127
2938
|
isReady: authState.isAuthReady,
|
|
2128
2939
|
isSignedIn: authState.isSignedIn,
|
|
2129
2940
|
user: authState.user,
|
|
2130
2941
|
did: authState.did,
|
|
2131
2942
|
scope: authState.tokenScope,
|
|
2132
|
-
hasScope: (
|
|
2943
|
+
hasScope: (s) => authRef.current.hasScope(s),
|
|
2133
2944
|
missingScopes: () => authRef.current.missingScopes(),
|
|
2134
|
-
// Auth actions
|
|
2135
2945
|
signIn: handleSignIn,
|
|
2136
2946
|
signInWithHandle: handleSignInWithHandle,
|
|
2137
2947
|
signOut: handleSignOut,
|
|
2138
2948
|
signInWithCode: (code, state) => authRef.current.signInWithCode(code, state),
|
|
2139
|
-
// Token management
|
|
2140
2949
|
getToken: (opts) => authRef.current.getToken(opts),
|
|
2141
2950
|
getSignInUrl: (redirectUri) => authRef.current.getSignInUrl(redirectUri),
|
|
2142
|
-
// DB access
|
|
2143
2951
|
db: getCurrentDb(),
|
|
2144
2952
|
dbStatus,
|
|
2145
2953
|
dbMode,
|
|
2146
|
-
|
|
2954
|
+
devInfo: schemaDevInfo,
|
|
2955
|
+
refreshSchemaStatus,
|
|
2147
2956
|
isAuthReady: authState.isAuthReady,
|
|
2148
2957
|
signin: handleSignIn,
|
|
2149
2958
|
signout: handleSignOut,
|
|
2150
2959
|
signinWithCode: (code, state) => authRef.current.signInWithCode(code, state),
|
|
2151
2960
|
getSignInLink: (redirectUri) => authRef.current.getSignInUrl(redirectUri)
|
|
2152
2961
|
};
|
|
2153
|
-
return /* @__PURE__ */ (0,
|
|
2154
|
-
error && isDevMode() && /* @__PURE__ */ (0,
|
|
2962
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(BasicContext.Provider, { value: contextValue, children: [
|
|
2963
|
+
error && isDevMode() && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(ErrorDisplay, { error }),
|
|
2964
|
+
devToolbar && isDevMode() && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_react3.Suspense, { fallback: null, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(BasicDevToolbar2, { debug }) }),
|
|
2155
2965
|
isReady && children
|
|
2156
2966
|
] });
|
|
2157
2967
|
}
|
|
2158
2968
|
function ErrorDisplay({ error }) {
|
|
2159
|
-
return /* @__PURE__ */ (0,
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
}
|
|
2181
|
-
|
|
2182
|
-
|
|
2969
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
2970
|
+
"div",
|
|
2971
|
+
{
|
|
2972
|
+
style: {
|
|
2973
|
+
position: "absolute",
|
|
2974
|
+
top: 20,
|
|
2975
|
+
left: 20,
|
|
2976
|
+
color: "black",
|
|
2977
|
+
backgroundColor: "#f8d7da",
|
|
2978
|
+
border: "1px solid #f5c6cb",
|
|
2979
|
+
borderRadius: "4px",
|
|
2980
|
+
padding: "20px",
|
|
2981
|
+
maxWidth: "400px",
|
|
2982
|
+
margin: "20px auto",
|
|
2983
|
+
boxShadow: "0 2px 4px rgba(0, 0, 0, 0.1)",
|
|
2984
|
+
fontFamily: "monospace"
|
|
2985
|
+
},
|
|
2986
|
+
children: [
|
|
2987
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("h3", { style: { fontSize: "0.8rem", opacity: 0.8 }, children: [
|
|
2988
|
+
"code: ",
|
|
2989
|
+
error.code
|
|
2990
|
+
] }),
|
|
2991
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h1", { style: { fontSize: "1.2rem", lineHeight: 1.5 }, children: error.title }),
|
|
2992
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("p", { children: error.message })
|
|
2993
|
+
]
|
|
2994
|
+
}
|
|
2995
|
+
);
|
|
2183
2996
|
}
|
|
2184
2997
|
|
|
2185
2998
|
// src/index.ts
|
|
2186
2999
|
var import_dexie_react_hooks = require("dexie-react-hooks");
|
|
3000
|
+
init_BasicDevToolbar();
|
|
2187
3001
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2188
3002
|
0 && (module.exports = {
|
|
3003
|
+
BasicDevToolbar,
|
|
2189
3004
|
BasicProvider,
|
|
2190
3005
|
DBStatus,
|
|
2191
3006
|
NotAuthenticatedError,
|