@basictech/react 0.7.0-beta.2 → 0.7.0-beta.4
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/AUTH_IMPLEMENTATION_GUIDE.md +2009 -0
- package/changelog.md +12 -0
- package/dist/index.d.mts +19 -113
- package/dist/index.d.ts +19 -113
- package/dist/index.js +62 -387
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +62 -386
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/AuthContext.tsx +82 -18
- package/src/index.ts +4 -4
- package/src/utils/storage.ts +1 -0
package/dist/index.js
CHANGED
|
@@ -20,7 +20,6 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
|
-
BasicDBSDK: () => BasicDBSDK,
|
|
24
23
|
BasicProvider: () => BasicProvider,
|
|
25
24
|
useBasic: () => useBasic,
|
|
26
25
|
useQuery: () => import_dexie_react_hooks.useLiveQuery
|
|
@@ -279,363 +278,8 @@ var BasicSync = class extends import_dexie2.Dexie {
|
|
|
279
278
|
}
|
|
280
279
|
};
|
|
281
280
|
|
|
282
|
-
// src/db_ts.ts
|
|
283
|
-
var DBError = class extends Error {
|
|
284
|
-
constructor(message, status, response, originalError) {
|
|
285
|
-
super(message);
|
|
286
|
-
this.status = status;
|
|
287
|
-
this.response = response;
|
|
288
|
-
this.originalError = originalError;
|
|
289
|
-
this.name = "DBError";
|
|
290
|
-
}
|
|
291
|
-
};
|
|
292
|
-
var QueryBuilder = class {
|
|
293
|
-
constructor(tableClient, tableSchema) {
|
|
294
|
-
this.tableClient = tableClient;
|
|
295
|
-
this.tableSchema = tableSchema;
|
|
296
|
-
}
|
|
297
|
-
params = {};
|
|
298
|
-
// Reserved fields that are always allowed
|
|
299
|
-
reservedFields = ["created_at", "updated_at", "id"];
|
|
300
|
-
// Validate field existence in schema
|
|
301
|
-
validateField(field) {
|
|
302
|
-
if (this.tableSchema && !this.reservedFields.includes(field)) {
|
|
303
|
-
if (!this.tableSchema.fields || !(field in this.tableSchema.fields)) {
|
|
304
|
-
throw new Error(`Invalid field: "${field}". Field does not exist in table schema.`);
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
// Validate operator based on field type
|
|
309
|
-
validateOperator(field, operator, value) {
|
|
310
|
-
if (!this.tableSchema || this.reservedFields.includes(field)) {
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
const fieldInfo = this.tableSchema.fields[field];
|
|
314
|
-
if (!fieldInfo)
|
|
315
|
-
return;
|
|
316
|
-
switch (operator) {
|
|
317
|
-
case "gt":
|
|
318
|
-
case "gte":
|
|
319
|
-
case "lt":
|
|
320
|
-
case "lte":
|
|
321
|
-
if (fieldInfo.type !== "number" && fieldInfo.type !== "string") {
|
|
322
|
-
throw new Error(`Operator "${operator}" can only be used with number or string fields. Field "${field}" is type "${fieldInfo.type}".`);
|
|
323
|
-
}
|
|
324
|
-
break;
|
|
325
|
-
case "like":
|
|
326
|
-
case "ilike":
|
|
327
|
-
if (fieldInfo.type !== "string") {
|
|
328
|
-
throw new Error(`Operator "${operator}" can only be used with string fields. Field "${field}" is type "${fieldInfo.type}".`);
|
|
329
|
-
}
|
|
330
|
-
if (typeof value !== "string") {
|
|
331
|
-
throw new Error(`Operator "${operator}" requires a string value. Received: ${typeof value}`);
|
|
332
|
-
}
|
|
333
|
-
break;
|
|
334
|
-
case "in":
|
|
335
|
-
if (!Array.isArray(value)) {
|
|
336
|
-
throw new Error(`Operator "in" requires an array value. Received: ${typeof value}`);
|
|
337
|
-
}
|
|
338
|
-
break;
|
|
339
|
-
case "is":
|
|
340
|
-
if (value !== null && typeof value !== "boolean") {
|
|
341
|
-
throw new Error(`Operator "is" requires null or boolean. Received: ${typeof value}`);
|
|
342
|
-
}
|
|
343
|
-
break;
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
// Add ordering to query with schema validation
|
|
347
|
-
order(field, direction = "asc") {
|
|
348
|
-
this.validateField(field);
|
|
349
|
-
this.params.order = `${field}.${direction}`;
|
|
350
|
-
return this;
|
|
351
|
-
}
|
|
352
|
-
// Add filtering to query
|
|
353
|
-
filter(conditions) {
|
|
354
|
-
if (!this.params.filters) {
|
|
355
|
-
this.params.filters = {};
|
|
356
|
-
}
|
|
357
|
-
for (const [field, condition] of Object.entries(conditions)) {
|
|
358
|
-
this.validateField(field);
|
|
359
|
-
if (condition === null || typeof condition !== "object") {
|
|
360
|
-
this.params.filters[field] = condition;
|
|
361
|
-
} else {
|
|
362
|
-
this.params.filters[field] = condition;
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
return this;
|
|
366
|
-
}
|
|
367
|
-
// Add limit to query
|
|
368
|
-
limit(count) {
|
|
369
|
-
this.params.limit = count;
|
|
370
|
-
return this;
|
|
371
|
-
}
|
|
372
|
-
// Add offset to query for pagination
|
|
373
|
-
offset(count) {
|
|
374
|
-
this.params.offset = count;
|
|
375
|
-
return this;
|
|
376
|
-
}
|
|
377
|
-
// Auto-execute when awaited
|
|
378
|
-
then(onfulfilled, onrejected) {
|
|
379
|
-
return this.tableClient.executeQuery(this.params).then(onfulfilled, onrejected);
|
|
380
|
-
}
|
|
381
|
-
// Auto-execute when awaited with catch
|
|
382
|
-
catch(onrejected) {
|
|
383
|
-
return this.tableClient.executeQuery(this.params).catch(onrejected);
|
|
384
|
-
}
|
|
385
|
-
// Auto-execute when awaited with finally
|
|
386
|
-
finally(onfinally) {
|
|
387
|
-
return this.tableClient.executeQuery(this.params).finally(onfinally);
|
|
388
|
-
}
|
|
389
|
-
};
|
|
390
|
-
var TableClient = class {
|
|
391
|
-
constructor(baseUrl, projectId, token, table, getToken, schema) {
|
|
392
|
-
this.baseUrl = baseUrl;
|
|
393
|
-
this.projectId = projectId;
|
|
394
|
-
this.token = token;
|
|
395
|
-
this.table = table;
|
|
396
|
-
this.getToken = getToken;
|
|
397
|
-
this.schema = schema;
|
|
398
|
-
if (schema && schema.tables && schema.tables[table]) {
|
|
399
|
-
this.tableSchema = schema.tables[table];
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
tableSchema;
|
|
403
|
-
async headers() {
|
|
404
|
-
const token = await this.getToken();
|
|
405
|
-
return {
|
|
406
|
-
Authorization: `Bearer ${token}`,
|
|
407
|
-
"Content-Type": "application/json"
|
|
408
|
-
};
|
|
409
|
-
}
|
|
410
|
-
async handleRequest(request) {
|
|
411
|
-
try {
|
|
412
|
-
const res = await request;
|
|
413
|
-
if (!res.ok) {
|
|
414
|
-
let errorMessage = `Request failed with status ${res.status}`;
|
|
415
|
-
let errorData;
|
|
416
|
-
try {
|
|
417
|
-
const json2 = await res.json();
|
|
418
|
-
errorData = json2;
|
|
419
|
-
if (json2.error || json2.message) {
|
|
420
|
-
const errorDetails = typeof json2.error === "object" ? JSON.stringify(json2.error) : json2.error;
|
|
421
|
-
const messageDetails = typeof json2.message === "object" ? JSON.stringify(json2.message) : json2.message;
|
|
422
|
-
errorMessage = `${res.status} ${res.statusText}: ${messageDetails || errorDetails || "Unknown error"}`;
|
|
423
|
-
}
|
|
424
|
-
} catch (e) {
|
|
425
|
-
console.log("Failed to parse error response:", e);
|
|
426
|
-
errorMessage = `${res.status} ${res.statusText}`;
|
|
427
|
-
}
|
|
428
|
-
throw new DBError(
|
|
429
|
-
errorMessage,
|
|
430
|
-
res.status,
|
|
431
|
-
errorData
|
|
432
|
-
);
|
|
433
|
-
}
|
|
434
|
-
const json = await res.json();
|
|
435
|
-
return json.data;
|
|
436
|
-
} catch (error) {
|
|
437
|
-
console.log("Caught error:", error);
|
|
438
|
-
if (error instanceof Error) {
|
|
439
|
-
console.log("Error type:", error.constructor.name);
|
|
440
|
-
console.log("Error stack:", error.stack);
|
|
441
|
-
}
|
|
442
|
-
if (error instanceof DBError) {
|
|
443
|
-
throw error;
|
|
444
|
-
}
|
|
445
|
-
if (error instanceof TypeError && error.message === "Network request failed") {
|
|
446
|
-
throw new DBError(
|
|
447
|
-
"Network request failed. Please check your internet connection and try again.",
|
|
448
|
-
void 0,
|
|
449
|
-
void 0,
|
|
450
|
-
error
|
|
451
|
-
);
|
|
452
|
-
}
|
|
453
|
-
throw new DBError(
|
|
454
|
-
`Unexpected error: ${error instanceof Error ? error.message : "Unknown error"}`,
|
|
455
|
-
void 0,
|
|
456
|
-
void 0,
|
|
457
|
-
error instanceof Error ? error : void 0
|
|
458
|
-
);
|
|
459
|
-
}
|
|
460
|
-
}
|
|
461
|
-
// Build query string from query options
|
|
462
|
-
buildQueryParams(query) {
|
|
463
|
-
if (!query)
|
|
464
|
-
return "";
|
|
465
|
-
const params = [];
|
|
466
|
-
if (query.id) {
|
|
467
|
-
params.push(`id=${query.id}`);
|
|
468
|
-
}
|
|
469
|
-
if (query.filters) {
|
|
470
|
-
for (const [field, condition] of Object.entries(query.filters)) {
|
|
471
|
-
this.addFilterParam(params, field, condition);
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
|
-
if (query.order) {
|
|
475
|
-
params.push(`order=${query.order}`);
|
|
476
|
-
}
|
|
477
|
-
if (query.limit !== void 0 && query.limit >= 0) {
|
|
478
|
-
params.push(`limit=${query.limit}`);
|
|
479
|
-
}
|
|
480
|
-
if (query.offset !== void 0 && query.offset >= 0) {
|
|
481
|
-
params.push(`offset=${query.offset}`);
|
|
482
|
-
}
|
|
483
|
-
return params.length > 0 ? `?${params.join("&")}` : "";
|
|
484
|
-
}
|
|
485
|
-
// Helper method to build filter parameters
|
|
486
|
-
addFilterParam(params, field, condition, negate = false) {
|
|
487
|
-
if (condition === null || typeof condition !== "object") {
|
|
488
|
-
if (condition === null) {
|
|
489
|
-
params.push(`${field}=${negate ? "not." : ""}is.null`);
|
|
490
|
-
} else if (typeof condition === "boolean") {
|
|
491
|
-
params.push(`${field}=${negate ? "not." : ""}is.${condition}`);
|
|
492
|
-
} else if (typeof condition === "number") {
|
|
493
|
-
params.push(`${field}=${negate ? "not." : ""}eq.${condition}`);
|
|
494
|
-
} else {
|
|
495
|
-
params.push(`${field}=${negate ? "not." : ""}eq.${encodeURIComponent(String(condition))}`);
|
|
496
|
-
}
|
|
497
|
-
return;
|
|
498
|
-
}
|
|
499
|
-
const operatorObj = condition;
|
|
500
|
-
if (operatorObj.not) {
|
|
501
|
-
this.addFilterParam(params, field, operatorObj.not, true);
|
|
502
|
-
return;
|
|
503
|
-
}
|
|
504
|
-
for (const [op, value] of Object.entries(operatorObj)) {
|
|
505
|
-
if (op === "not")
|
|
506
|
-
continue;
|
|
507
|
-
const operator = op;
|
|
508
|
-
if (value === null) {
|
|
509
|
-
params.push(`${field}=${negate ? "not." : ""}is.null`);
|
|
510
|
-
} else if (operator === "in" && Array.isArray(value)) {
|
|
511
|
-
params.push(`${field}=${negate ? "not." : ""}in.${value.join(",")}`);
|
|
512
|
-
} else if (operator === "is") {
|
|
513
|
-
if (typeof value === "boolean") {
|
|
514
|
-
params.push(`${field}=${negate ? "not." : ""}is.${value}`);
|
|
515
|
-
} else {
|
|
516
|
-
params.push(`${field}=${negate ? "not." : ""}is.null`);
|
|
517
|
-
}
|
|
518
|
-
} else {
|
|
519
|
-
const paramValue = typeof value === "string" ? encodeURIComponent(value) : String(value);
|
|
520
|
-
params.push(`${field}=${negate ? "not." : ""}${operator}.${paramValue}`);
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
// Internal method to execute a query with options
|
|
525
|
-
async executeQuery(options) {
|
|
526
|
-
const params = this.buildQueryParams(options);
|
|
527
|
-
const headers = await this.headers();
|
|
528
|
-
return this.handleRequest(
|
|
529
|
-
fetch(`${this.baseUrl}/account/${this.projectId}/db/${this.table}${params}`, {
|
|
530
|
-
headers
|
|
531
|
-
})
|
|
532
|
-
);
|
|
533
|
-
}
|
|
534
|
-
// Public method to start building a query
|
|
535
|
-
getAll() {
|
|
536
|
-
return new QueryBuilder(this, this.tableSchema);
|
|
537
|
-
}
|
|
538
|
-
// Get a specific item by ID
|
|
539
|
-
async get(id) {
|
|
540
|
-
const headers = await this.headers();
|
|
541
|
-
return this.handleRequest(
|
|
542
|
-
fetch(`${this.baseUrl}/account/${this.projectId}/db/${this.table}/${id}`, {
|
|
543
|
-
headers
|
|
544
|
-
})
|
|
545
|
-
);
|
|
546
|
-
}
|
|
547
|
-
async create(value) {
|
|
548
|
-
const headers = await this.headers();
|
|
549
|
-
return this.handleRequest(
|
|
550
|
-
fetch(`${this.baseUrl}/account/${this.projectId}/db/${this.table}`, {
|
|
551
|
-
method: "POST",
|
|
552
|
-
headers,
|
|
553
|
-
body: JSON.stringify({ value })
|
|
554
|
-
})
|
|
555
|
-
);
|
|
556
|
-
}
|
|
557
|
-
async update(id, value) {
|
|
558
|
-
const headers = await this.headers();
|
|
559
|
-
return this.handleRequest(
|
|
560
|
-
fetch(`${this.baseUrl}/account/${this.projectId}/db/${this.table}/${id}`, {
|
|
561
|
-
method: "PATCH",
|
|
562
|
-
headers,
|
|
563
|
-
body: JSON.stringify({ value })
|
|
564
|
-
})
|
|
565
|
-
);
|
|
566
|
-
}
|
|
567
|
-
async replace(id, value) {
|
|
568
|
-
const headers = await this.headers();
|
|
569
|
-
return this.handleRequest(
|
|
570
|
-
fetch(`${this.baseUrl}/account/${this.projectId}/db/${this.table}/${id}`, {
|
|
571
|
-
method: "PUT",
|
|
572
|
-
headers,
|
|
573
|
-
body: JSON.stringify({ value })
|
|
574
|
-
})
|
|
575
|
-
);
|
|
576
|
-
}
|
|
577
|
-
async delete(id) {
|
|
578
|
-
const token = await this.getToken();
|
|
579
|
-
const headers = {
|
|
580
|
-
Authorization: `Bearer ${token}`
|
|
581
|
-
};
|
|
582
|
-
return this.handleRequest(
|
|
583
|
-
fetch(`${this.baseUrl}/account/${this.projectId}/db/${this.table}/${id}`, {
|
|
584
|
-
method: "DELETE",
|
|
585
|
-
headers
|
|
586
|
-
})
|
|
587
|
-
);
|
|
588
|
-
}
|
|
589
|
-
};
|
|
590
|
-
var BasicDBSDK = class {
|
|
591
|
-
projectId;
|
|
592
|
-
getToken;
|
|
593
|
-
baseUrl;
|
|
594
|
-
schema;
|
|
595
|
-
tableNames;
|
|
596
|
-
constructor(config) {
|
|
597
|
-
this.projectId = config.project_id;
|
|
598
|
-
if (config.getToken) {
|
|
599
|
-
this.getToken = config.getToken;
|
|
600
|
-
} else if (config.token) {
|
|
601
|
-
this.getToken = async () => config.token;
|
|
602
|
-
} else {
|
|
603
|
-
throw new Error("Either token or getToken must be provided");
|
|
604
|
-
}
|
|
605
|
-
this.baseUrl = config.baseUrl || "https://api.basic.tech";
|
|
606
|
-
this.schema = config.schema;
|
|
607
|
-
this.tableNames = Object.keys(this.schema.tables);
|
|
608
|
-
}
|
|
609
|
-
// Primary method - table access
|
|
610
|
-
table(name) {
|
|
611
|
-
if (!this.tableNames.includes(name)) {
|
|
612
|
-
throw new Error(`Table '${name}' not found in schema. Available tables: ${this.tableNames.join(", ")}`);
|
|
613
|
-
}
|
|
614
|
-
return new TableClient(
|
|
615
|
-
this.baseUrl,
|
|
616
|
-
this.projectId,
|
|
617
|
-
"",
|
|
618
|
-
// Empty placeholder, will be replaced in headers() method
|
|
619
|
-
name,
|
|
620
|
-
this.getToken,
|
|
621
|
-
this.schema
|
|
622
|
-
// Pass the entire schema to the TableClient
|
|
623
|
-
);
|
|
624
|
-
}
|
|
625
|
-
get tables() {
|
|
626
|
-
return {};
|
|
627
|
-
}
|
|
628
|
-
fields(table) {
|
|
629
|
-
const tableSchema = this.schema.tables[table];
|
|
630
|
-
if (!tableSchema) {
|
|
631
|
-
throw new Error(`Table '${table}' not found in schema`);
|
|
632
|
-
}
|
|
633
|
-
return Object.keys(tableSchema.fields);
|
|
634
|
-
}
|
|
635
|
-
};
|
|
636
|
-
|
|
637
281
|
// package.json
|
|
638
|
-
var version = "0.7.0-beta.
|
|
282
|
+
var version = "0.7.0-beta.3";
|
|
639
283
|
|
|
640
284
|
// src/updater/versionUpdater.ts
|
|
641
285
|
var VersionUpdater = class {
|
|
@@ -778,6 +422,7 @@ var STORAGE_KEYS = {
|
|
|
778
422
|
REFRESH_TOKEN: "basic_refresh_token",
|
|
779
423
|
USER_INFO: "basic_user_info",
|
|
780
424
|
AUTH_STATE: "basic_auth_state",
|
|
425
|
+
REDIRECT_URI: "basic_redirect_uri",
|
|
781
426
|
DEBUG: "basic_debug"
|
|
782
427
|
};
|
|
783
428
|
function getCookie(name) {
|
|
@@ -975,6 +620,10 @@ async function validateAndCheckSchema(schema) {
|
|
|
975
620
|
|
|
976
621
|
// src/AuthContext.tsx
|
|
977
622
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
623
|
+
var DEFAULT_AUTH_CONFIG = {
|
|
624
|
+
scopes: "profile email app:admin",
|
|
625
|
+
server_url: "https://api.basic.tech"
|
|
626
|
+
};
|
|
978
627
|
var BasicContext = (0, import_react.createContext)({
|
|
979
628
|
unicorn: "\u{1F984}",
|
|
980
629
|
isAuthReady: false,
|
|
@@ -988,7 +637,6 @@ var BasicContext = (0, import_react.createContext)({
|
|
|
988
637
|
}),
|
|
989
638
|
getSignInLink: () => Promise.resolve(""),
|
|
990
639
|
db: {},
|
|
991
|
-
remoteDb: {},
|
|
992
640
|
dbStatus: "LOADING" /* LOADING */
|
|
993
641
|
});
|
|
994
642
|
function BasicProvider({
|
|
@@ -996,7 +644,8 @@ function BasicProvider({
|
|
|
996
644
|
project_id,
|
|
997
645
|
schema,
|
|
998
646
|
debug = false,
|
|
999
|
-
storage
|
|
647
|
+
storage,
|
|
648
|
+
auth
|
|
1000
649
|
}) {
|
|
1001
650
|
const [isAuthReady, setIsAuthReady] = (0, import_react.useState)(false);
|
|
1002
651
|
const [isSignedIn, setIsSignedIn] = (0, import_react.useState)(false);
|
|
@@ -1009,8 +658,12 @@ function BasicProvider({
|
|
|
1009
658
|
const [isOnline, setIsOnline] = (0, import_react.useState)(navigator.onLine);
|
|
1010
659
|
const [pendingRefresh, setPendingRefresh] = (0, import_react.useState)(false);
|
|
1011
660
|
const syncRef = (0, import_react.useRef)(null);
|
|
1012
|
-
const remoteDbRef = (0, import_react.useRef)(null);
|
|
1013
661
|
const storageAdapter = storage || new LocalStorageAdapter();
|
|
662
|
+
const authConfig = {
|
|
663
|
+
scopes: auth?.scopes || DEFAULT_AUTH_CONFIG.scopes,
|
|
664
|
+
server_url: auth?.server_url || DEFAULT_AUTH_CONFIG.server_url
|
|
665
|
+
};
|
|
666
|
+
const scopesString = Array.isArray(authConfig.scopes) ? authConfig.scopes.join(" ") : authConfig.scopes;
|
|
1014
667
|
const isDevMode = () => isDevelopment(debug);
|
|
1015
668
|
const cleanOAuthParams = () => cleanOAuthParamsFromUrl();
|
|
1016
669
|
(0, import_react.useEffect)(() => {
|
|
@@ -1023,7 +676,7 @@ function BasicProvider({
|
|
|
1023
676
|
if (token) {
|
|
1024
677
|
const refreshToken = token.refresh_token || localStorage.getItem("basic_refresh_token");
|
|
1025
678
|
if (refreshToken) {
|
|
1026
|
-
fetchToken(refreshToken).catch((error2) => {
|
|
679
|
+
fetchToken(refreshToken, true).catch((error2) => {
|
|
1027
680
|
log("Retry refresh failed:", error2);
|
|
1028
681
|
});
|
|
1029
682
|
}
|
|
@@ -1105,17 +758,6 @@ function BasicProvider({
|
|
|
1105
758
|
}
|
|
1106
759
|
connectToDb();
|
|
1107
760
|
}, [isSignedIn, shouldConnect]);
|
|
1108
|
-
(0, import_react.useEffect)(() => {
|
|
1109
|
-
if (project_id && schema && token?.access_token && !remoteDbRef.current) {
|
|
1110
|
-
log("Initializing Remote DB SDK");
|
|
1111
|
-
remoteDbRef.current = new BasicDBSDK({
|
|
1112
|
-
project_id,
|
|
1113
|
-
schema,
|
|
1114
|
-
getToken: () => getToken(),
|
|
1115
|
-
baseUrl: "https://api.basic.tech"
|
|
1116
|
-
});
|
|
1117
|
-
}
|
|
1118
|
-
}, [token, project_id, schema]);
|
|
1119
761
|
(0, import_react.useEffect)(() => {
|
|
1120
762
|
const initializeAuth = async () => {
|
|
1121
763
|
await storageAdapter.set(STORAGE_KEYS.DEBUG, debug ? "true" : "false");
|
|
@@ -1146,14 +788,14 @@ function BasicProvider({
|
|
|
1146
788
|
}
|
|
1147
789
|
await storageAdapter.remove(STORAGE_KEYS.AUTH_STATE);
|
|
1148
790
|
cleanOAuthParams();
|
|
1149
|
-
fetchToken(code).catch((error2) => {
|
|
791
|
+
fetchToken(code, false).catch((error2) => {
|
|
1150
792
|
log("Error fetching token:", error2);
|
|
1151
793
|
});
|
|
1152
794
|
} else {
|
|
1153
795
|
const refreshToken = await storageAdapter.get(STORAGE_KEYS.REFRESH_TOKEN);
|
|
1154
796
|
if (refreshToken) {
|
|
1155
797
|
log("Found refresh token in storage, attempting to refresh access token");
|
|
1156
|
-
fetchToken(refreshToken).catch((error2) => {
|
|
798
|
+
fetchToken(refreshToken, true).catch((error2) => {
|
|
1157
799
|
log("Error fetching refresh token:", error2);
|
|
1158
800
|
});
|
|
1159
801
|
} else {
|
|
@@ -1189,7 +831,7 @@ function BasicProvider({
|
|
|
1189
831
|
(0, import_react.useEffect)(() => {
|
|
1190
832
|
async function fetchUser(acc_token) {
|
|
1191
833
|
console.info("fetching user");
|
|
1192
|
-
const user2 = await fetch(
|
|
834
|
+
const user2 = await fetch(`${authConfig.server_url}/auth/userInfo`, {
|
|
1193
835
|
method: "GET",
|
|
1194
836
|
headers: {
|
|
1195
837
|
"Authorization": `Bearer ${acc_token}`
|
|
@@ -1222,7 +864,7 @@ function BasicProvider({
|
|
|
1222
864
|
if (isExpired) {
|
|
1223
865
|
log("token is expired - refreshing ...");
|
|
1224
866
|
try {
|
|
1225
|
-
const newToken = await fetchToken(token?.refresh_token || "");
|
|
867
|
+
const newToken = await fetchToken(token?.refresh_token || "", true);
|
|
1226
868
|
fetchUser(newToken?.access_token || "");
|
|
1227
869
|
} catch (error2) {
|
|
1228
870
|
log("Failed to refresh token in checkToken:", error2);
|
|
@@ -1253,13 +895,15 @@ function BasicProvider({
|
|
|
1253
895
|
if (!redirectUrl || !redirectUrl.startsWith("http://") && !redirectUrl.startsWith("https://")) {
|
|
1254
896
|
throw new Error("Invalid redirect URI provided");
|
|
1255
897
|
}
|
|
1256
|
-
|
|
898
|
+
await storageAdapter.set(STORAGE_KEYS.REDIRECT_URI, redirectUrl);
|
|
899
|
+
log("Stored redirect_uri for token exchange:", redirectUrl);
|
|
900
|
+
let baseUrl = `${authConfig.server_url}/auth/authorize`;
|
|
1257
901
|
baseUrl += `?client_id=${project_id}`;
|
|
1258
902
|
baseUrl += `&redirect_uri=${encodeURIComponent(redirectUrl)}`;
|
|
1259
903
|
baseUrl += `&response_type=code`;
|
|
1260
|
-
baseUrl += `&scope
|
|
904
|
+
baseUrl += `&scope=${encodeURIComponent(scopesString)}`;
|
|
1261
905
|
baseUrl += `&state=${randomState}`;
|
|
1262
|
-
log("Generated sign-in link successfully");
|
|
906
|
+
log("Generated sign-in link successfully with scopes:", scopesString);
|
|
1263
907
|
return baseUrl;
|
|
1264
908
|
} catch (error2) {
|
|
1265
909
|
log("Error generating sign-in link:", error2);
|
|
@@ -1307,7 +951,7 @@ function BasicProvider({
|
|
|
1307
951
|
}
|
|
1308
952
|
await storageAdapter.remove(STORAGE_KEYS.AUTH_STATE);
|
|
1309
953
|
cleanOAuthParams();
|
|
1310
|
-
const token2 = await fetchToken(code);
|
|
954
|
+
const token2 = await fetchToken(code, false);
|
|
1311
955
|
if (token2) {
|
|
1312
956
|
log("signinWithCode successful");
|
|
1313
957
|
return { success: true };
|
|
@@ -1332,6 +976,7 @@ function BasicProvider({
|
|
|
1332
976
|
await storageAdapter.remove(STORAGE_KEYS.AUTH_STATE);
|
|
1333
977
|
await storageAdapter.remove(STORAGE_KEYS.REFRESH_TOKEN);
|
|
1334
978
|
await storageAdapter.remove(STORAGE_KEYS.USER_INFO);
|
|
979
|
+
await storageAdapter.remove(STORAGE_KEYS.REDIRECT_URI);
|
|
1335
980
|
if (syncRef.current) {
|
|
1336
981
|
(async () => {
|
|
1337
982
|
try {
|
|
@@ -1352,7 +997,7 @@ function BasicProvider({
|
|
|
1352
997
|
if (refreshToken) {
|
|
1353
998
|
log("No token in memory, attempting to refresh from storage");
|
|
1354
999
|
try {
|
|
1355
|
-
const newToken = await fetchToken(refreshToken);
|
|
1000
|
+
const newToken = await fetchToken(refreshToken, true);
|
|
1356
1001
|
if (newToken?.access_token) {
|
|
1357
1002
|
return newToken.access_token;
|
|
1358
1003
|
}
|
|
@@ -1379,7 +1024,7 @@ function BasicProvider({
|
|
|
1379
1024
|
const refreshToken = token?.refresh_token || await storageAdapter.get(STORAGE_KEYS.REFRESH_TOKEN);
|
|
1380
1025
|
if (refreshToken) {
|
|
1381
1026
|
try {
|
|
1382
|
-
const newToken = await fetchToken(refreshToken);
|
|
1027
|
+
const newToken = await fetchToken(refreshToken, true);
|
|
1383
1028
|
return newToken?.access_token || "";
|
|
1384
1029
|
} catch (error2) {
|
|
1385
1030
|
log("Failed to refresh expired token:", error2);
|
|
@@ -1395,19 +1040,45 @@ function BasicProvider({
|
|
|
1395
1040
|
}
|
|
1396
1041
|
return token?.access_token || "";
|
|
1397
1042
|
};
|
|
1398
|
-
const fetchToken = async (
|
|
1043
|
+
const fetchToken = async (codeOrRefreshToken, isRefreshToken = false) => {
|
|
1399
1044
|
try {
|
|
1400
1045
|
if (!isOnline) {
|
|
1401
1046
|
log("Network is offline, marking refresh as pending");
|
|
1402
1047
|
setPendingRefresh(true);
|
|
1403
1048
|
throw new Error("Network offline - refresh will be retried when online");
|
|
1404
1049
|
}
|
|
1405
|
-
|
|
1050
|
+
let requestBody;
|
|
1051
|
+
if (isRefreshToken) {
|
|
1052
|
+
requestBody = {
|
|
1053
|
+
grant_type: "refresh_token",
|
|
1054
|
+
refresh_token: codeOrRefreshToken
|
|
1055
|
+
};
|
|
1056
|
+
if (project_id) {
|
|
1057
|
+
requestBody.client_id = project_id;
|
|
1058
|
+
}
|
|
1059
|
+
} else {
|
|
1060
|
+
requestBody = {
|
|
1061
|
+
grant_type: "authorization_code",
|
|
1062
|
+
code: codeOrRefreshToken
|
|
1063
|
+
};
|
|
1064
|
+
const storedRedirectUri = await storageAdapter.get(STORAGE_KEYS.REDIRECT_URI);
|
|
1065
|
+
if (storedRedirectUri) {
|
|
1066
|
+
requestBody.redirect_uri = storedRedirectUri;
|
|
1067
|
+
log("Including redirect_uri in token exchange:", storedRedirectUri);
|
|
1068
|
+
} else {
|
|
1069
|
+
log("Warning: No redirect_uri found in storage for token exchange");
|
|
1070
|
+
}
|
|
1071
|
+
if (project_id) {
|
|
1072
|
+
requestBody.client_id = project_id;
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
log("Token exchange request body:", { ...requestBody, refresh_token: isRefreshToken ? "[REDACTED]" : void 0, code: !isRefreshToken ? "[REDACTED]" : void 0 });
|
|
1076
|
+
const token2 = await fetch(`${authConfig.server_url}/auth/token`, {
|
|
1406
1077
|
method: "POST",
|
|
1407
1078
|
headers: {
|
|
1408
1079
|
"Content-Type": "application/json"
|
|
1409
1080
|
},
|
|
1410
|
-
body: JSON.stringify(
|
|
1081
|
+
body: JSON.stringify(requestBody)
|
|
1411
1082
|
}).then((response) => response.json()).catch((error2) => {
|
|
1412
1083
|
log("Network error fetching token:", error2);
|
|
1413
1084
|
if (!isOnline) {
|
|
@@ -1424,6 +1095,7 @@ function BasicProvider({
|
|
|
1424
1095
|
}
|
|
1425
1096
|
await storageAdapter.remove(STORAGE_KEYS.REFRESH_TOKEN);
|
|
1426
1097
|
await storageAdapter.remove(STORAGE_KEYS.USER_INFO);
|
|
1098
|
+
await storageAdapter.remove(STORAGE_KEYS.REDIRECT_URI);
|
|
1427
1099
|
clearCookie("basic_token");
|
|
1428
1100
|
clearCookie("basic_access_token");
|
|
1429
1101
|
setUser({});
|
|
@@ -1438,6 +1110,10 @@ function BasicProvider({
|
|
|
1438
1110
|
await storageAdapter.set(STORAGE_KEYS.REFRESH_TOKEN, token2.refresh_token);
|
|
1439
1111
|
log("Updated refresh token in storage");
|
|
1440
1112
|
}
|
|
1113
|
+
if (!isRefreshToken) {
|
|
1114
|
+
await storageAdapter.remove(STORAGE_KEYS.REDIRECT_URI);
|
|
1115
|
+
log("Cleaned up redirect_uri from storage after successful exchange");
|
|
1116
|
+
}
|
|
1441
1117
|
setCookie("basic_access_token", token2.access_token, { httpOnly: false });
|
|
1442
1118
|
log("Updated access token in cookie");
|
|
1443
1119
|
}
|
|
@@ -1447,6 +1123,7 @@ function BasicProvider({
|
|
|
1447
1123
|
if (!error2.message.includes("offline") && !error2.message.includes("Network")) {
|
|
1448
1124
|
await storageAdapter.remove(STORAGE_KEYS.REFRESH_TOKEN);
|
|
1449
1125
|
await storageAdapter.remove(STORAGE_KEYS.USER_INFO);
|
|
1126
|
+
await storageAdapter.remove(STORAGE_KEYS.REDIRECT_URI);
|
|
1450
1127
|
clearCookie("basic_token");
|
|
1451
1128
|
clearCookie("basic_access_token");
|
|
1452
1129
|
setUser({});
|
|
@@ -1473,7 +1150,6 @@ function BasicProvider({
|
|
|
1473
1150
|
getToken,
|
|
1474
1151
|
getSignInLink,
|
|
1475
1152
|
db: syncRef.current ? syncRef.current : noDb,
|
|
1476
|
-
remoteDb: remoteDbRef.current ? remoteDbRef.current : noDb,
|
|
1477
1153
|
dbStatus
|
|
1478
1154
|
}, children: [
|
|
1479
1155
|
error && isDevMode() && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ErrorDisplay, { error }),
|
|
@@ -1511,7 +1187,6 @@ function useBasic() {
|
|
|
1511
1187
|
var import_dexie_react_hooks = require("dexie-react-hooks");
|
|
1512
1188
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1513
1189
|
0 && (module.exports = {
|
|
1514
|
-
BasicDBSDK,
|
|
1515
1190
|
BasicProvider,
|
|
1516
1191
|
useBasic,
|
|
1517
1192
|
useQuery
|