@basictech/react 0.8.0-beta.4 → 0.9.0-beta.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/changelog.md +65 -0
- package/dist/index.d.mts +1258 -285
- package/dist/index.d.ts +1258 -285
- package/dist/index.js +2898 -1425
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2858 -1418
- package/dist/index.mjs.map +1 -1
- package/package.json +27 -8
- package/readme.md +331 -214
- package/.turbo/turbo-build.log +0 -24
- package/AUTH_IMPLEMENTATION_GUIDE.md +0 -2011
- package/src/AuthContext.tsx +0 -591
- package/src/config.ts +0 -9
- package/src/context.tsx +0 -122
- package/src/core/auth/AuthManager.ts +0 -1371
- package/src/core/db/RemoteCollection.ts +0 -308
- package/src/core/db/RemoteDB.ts +0 -40
- package/src/core/db/index.ts +0 -7
- package/src/core/db/types.ts +0 -140
- package/src/dev/BasicDevToolbar.tsx +0 -665
- package/src/index.ts +0 -36
- package/src/sync/index.ts +0 -288
- package/src/sync/syncProtocol.js +0 -291
- package/src/sync/tokenRegistry.ts +0 -20
- package/src/updater/updateMigrations.ts +0 -22
- package/src/updater/versionUpdater.ts +0 -153
- package/src/utils/network.ts +0 -135
- package/src/utils/normalizeClientId.ts +0 -22
- package/src/utils/resolveDid.ts +0 -101
- package/src/utils/schema.ts +0 -119
- package/src/utils/storage.ts +0 -67
- package/tsconfig.json +0 -9
- package/tsup.config.ts +0 -11
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
import { BasicStorage } from '../utils/storage'
|
|
2
|
-
import { log } from '../config'
|
|
3
|
-
|
|
4
|
-
export interface VersionInfo {
|
|
5
|
-
version: string
|
|
6
|
-
lastUpdated: number
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface Migration {
|
|
10
|
-
fromVersion: string
|
|
11
|
-
toVersion: string
|
|
12
|
-
migrate: (storage: BasicStorage) => Promise<void>
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
export class VersionUpdater {
|
|
16
|
-
private storage: BasicStorage
|
|
17
|
-
private currentVersion: string
|
|
18
|
-
private migrations: Migration[]
|
|
19
|
-
private versionKey = 'basic_app_version'
|
|
20
|
-
|
|
21
|
-
constructor(storage: BasicStorage, currentVersion: string, migrations: Migration[] = []) {
|
|
22
|
-
this.storage = storage
|
|
23
|
-
this.currentVersion = currentVersion
|
|
24
|
-
this.migrations = migrations.sort((a, b) => this.compareVersions(a.fromVersion, b.fromVersion))
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Check current stored version and run migrations if needed
|
|
29
|
-
* Only compares major.minor versions, ignoring beta/prerelease parts
|
|
30
|
-
* Example: "0.7.0-beta.1" and "0.7.0" are treated as the same version
|
|
31
|
-
*/
|
|
32
|
-
async checkAndUpdate(): Promise<{ updated: boolean; fromVersion?: string; toVersion: string }> {
|
|
33
|
-
const storedVersion = await this.getStoredVersion()
|
|
34
|
-
|
|
35
|
-
if (!storedVersion) {
|
|
36
|
-
// First time setup
|
|
37
|
-
await this.setStoredVersion(this.currentVersion)
|
|
38
|
-
return { updated: false, toVersion: this.currentVersion }
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (storedVersion === this.currentVersion) {
|
|
42
|
-
return { updated: false, toVersion: this.currentVersion }
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// Need to run migrations
|
|
46
|
-
const migrationsToRun = this.getMigrationsToRun(storedVersion, this.currentVersion)
|
|
47
|
-
|
|
48
|
-
if (migrationsToRun.length === 0) {
|
|
49
|
-
// No migrations needed, just update version
|
|
50
|
-
await this.setStoredVersion(this.currentVersion)
|
|
51
|
-
return { updated: true, fromVersion: storedVersion, toVersion: this.currentVersion }
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Run migrations
|
|
55
|
-
for (const migration of migrationsToRun) {
|
|
56
|
-
try {
|
|
57
|
-
log(`Running migration from ${migration.fromVersion} to ${migration.toVersion}`)
|
|
58
|
-
await migration.migrate(this.storage)
|
|
59
|
-
} catch (error) {
|
|
60
|
-
console.error(`Migration failed from ${migration.fromVersion} to ${migration.toVersion}:`, error)
|
|
61
|
-
throw new Error(`Migration failed: ${error}`)
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// Update to current version
|
|
66
|
-
await this.setStoredVersion(this.currentVersion)
|
|
67
|
-
return { updated: true, fromVersion: storedVersion, toVersion: this.currentVersion }
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
private async getStoredVersion(): Promise<string | null> {
|
|
71
|
-
try {
|
|
72
|
-
const versionData = await this.storage.get(this.versionKey)
|
|
73
|
-
if (!versionData) return null
|
|
74
|
-
|
|
75
|
-
const versionInfo: VersionInfo = JSON.parse(versionData)
|
|
76
|
-
return versionInfo.version
|
|
77
|
-
} catch (error) {
|
|
78
|
-
console.warn('Failed to get stored version:', error)
|
|
79
|
-
return null
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
private async setStoredVersion(version: string): Promise<void> {
|
|
84
|
-
const versionInfo: VersionInfo = {
|
|
85
|
-
version,
|
|
86
|
-
lastUpdated: Date.now()
|
|
87
|
-
}
|
|
88
|
-
await this.storage.set(this.versionKey, JSON.stringify(versionInfo))
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
private getMigrationsToRun(fromVersion: string, toVersion: string): Migration[] {
|
|
92
|
-
return this.migrations.filter(migration => {
|
|
93
|
-
const storedLessThanMigrationTo = this.compareVersions(fromVersion, migration.toVersion) < 0
|
|
94
|
-
const currentGreaterThanOrEqualMigrationTo = this.compareVersions(toVersion, migration.toVersion) >= 0
|
|
95
|
-
const shouldRun = storedLessThanMigrationTo && currentGreaterThanOrEqualMigrationTo
|
|
96
|
-
log(`Migration ${migration.fromVersion} → ${migration.toVersion}: shouldRun=${shouldRun}`)
|
|
97
|
-
return shouldRun
|
|
98
|
-
})
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Simple semantic version comparison (major.minor only, ignoring beta/prerelease)
|
|
103
|
-
* Returns: -1 if a < b, 0 if a === b, 1 if a > b
|
|
104
|
-
*/
|
|
105
|
-
private compareVersions(a: string, b: string): number {
|
|
106
|
-
// Extract major.minor from version strings, ignoring beta/prerelease parts
|
|
107
|
-
const aMajorMinor = this.extractMajorMinor(a)
|
|
108
|
-
const bMajorMinor = this.extractMajorMinor(b)
|
|
109
|
-
|
|
110
|
-
// Compare major version first
|
|
111
|
-
if (aMajorMinor.major !== bMajorMinor.major) {
|
|
112
|
-
return aMajorMinor.major - bMajorMinor.major
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// Then compare minor version
|
|
116
|
-
return aMajorMinor.minor - bMajorMinor.minor
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Extract major.minor from version string, ignoring beta/prerelease
|
|
121
|
-
* Examples: "0.7.0-beta.1" -> {major: 0, minor: 7}
|
|
122
|
-
* "1.2.3" -> {major: 1, minor: 2}
|
|
123
|
-
*/
|
|
124
|
-
private extractMajorMinor(version: string): { major: number, minor: number } {
|
|
125
|
-
// Remove beta/prerelease parts and split by dots
|
|
126
|
-
const cleanVersion = version.split('-')[0]?.split('+')[0] || version
|
|
127
|
-
const parts = cleanVersion.split('.').map(Number)
|
|
128
|
-
|
|
129
|
-
return {
|
|
130
|
-
major: parts[0] || 0,
|
|
131
|
-
minor: parts[1] || 0
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Add a migration to the updater
|
|
137
|
-
*/
|
|
138
|
-
addMigration(migration: Migration): void {
|
|
139
|
-
this.migrations.push(migration)
|
|
140
|
-
this.migrations.sort((a, b) => this.compareVersions(a.fromVersion, b.fromVersion))
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
/**
|
|
145
|
-
* Create a simple version updater instance
|
|
146
|
-
*/
|
|
147
|
-
export function createVersionUpdater(
|
|
148
|
-
storage: BasicStorage,
|
|
149
|
-
currentVersion: string,
|
|
150
|
-
migrations: Migration[] = []
|
|
151
|
-
): VersionUpdater {
|
|
152
|
-
return new VersionUpdater(storage, currentVersion, migrations)
|
|
153
|
-
}
|
package/src/utils/network.ts
DELETED
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
// Network utilities for Basic React package
|
|
2
|
-
import semver from 'semver'
|
|
3
|
-
import { log } from '../config'
|
|
4
|
-
import { version as pkgVersion } from '../../package.json'
|
|
5
|
-
|
|
6
|
-
export function isDevelopment(debug?: boolean): boolean {
|
|
7
|
-
if (debug === true) return true
|
|
8
|
-
if (typeof process !== 'undefined' && process.env.NODE_ENV === 'development') return true
|
|
9
|
-
if (typeof window === 'undefined' || !window.location) return false
|
|
10
|
-
const host = window.location.hostname
|
|
11
|
-
return (
|
|
12
|
-
host === 'localhost' ||
|
|
13
|
-
host === '127.0.0.1' ||
|
|
14
|
-
host.includes('localhost') ||
|
|
15
|
-
host.includes('127.0.0.1') ||
|
|
16
|
-
host.includes('.local')
|
|
17
|
-
)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function normalizeVersion(v: string | null | undefined): string | null {
|
|
21
|
-
if (v == null) return null
|
|
22
|
-
const t = String(v).trim()
|
|
23
|
-
return t.length ? t : null
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function versionsMatch(a: string, b: string): boolean {
|
|
27
|
-
const na = a.trim()
|
|
28
|
-
const nb = b.trim()
|
|
29
|
-
if (na === nb) return true
|
|
30
|
-
const va = semver.valid(na)
|
|
31
|
-
const vb = semver.valid(nb)
|
|
32
|
-
if (va && vb) return semver.eq(va, vb)
|
|
33
|
-
return false
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/** Use npm `beta` dist-tag when the installed version is a semver prerelease whose first id is `beta`. */
|
|
37
|
-
function usesBetaDistTag(version: string): boolean {
|
|
38
|
-
const pre = semver.prerelease(version)
|
|
39
|
-
const id = pre?.[0]
|
|
40
|
-
return typeof id === 'string' && id.toLowerCase() === 'beta'
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
type NpmInstallMeta = {
|
|
44
|
-
'dist-tags'?: { latest?: string; beta?: string }
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export async function checkForNewVersion(): Promise<{
|
|
48
|
-
hasNewVersion: boolean,
|
|
49
|
-
latestVersion: string | null,
|
|
50
|
-
currentVersion: string | null
|
|
51
|
-
}> {
|
|
52
|
-
try {
|
|
53
|
-
const currentVersion = normalizeVersion(pkgVersion)
|
|
54
|
-
if (!currentVersion) {
|
|
55
|
-
return { hasNewVersion: false, latestVersion: null, currentVersion: null }
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const response = await fetch('https://registry.npmjs.org/@basictech/react', {
|
|
59
|
-
headers: { Accept: 'application/vnd.npm.install-v1+json' },
|
|
60
|
-
})
|
|
61
|
-
if (!response.ok) {
|
|
62
|
-
throw new Error('Failed to fetch version from npm');
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const data = (await response.json()) as NpmInstallMeta
|
|
66
|
-
const distTags = data['dist-tags'] ?? {}
|
|
67
|
-
const rawRegistry =
|
|
68
|
-
usesBetaDistTag(currentVersion)
|
|
69
|
-
? distTags.beta ?? distTags.latest
|
|
70
|
-
: distTags.latest
|
|
71
|
-
const latestVersion = normalizeVersion(rawRegistry ?? null)
|
|
72
|
-
if (!latestVersion) {
|
|
73
|
-
throw new Error('Missing dist-tags from npm registry')
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
const same = versionsMatch(currentVersion, latestVersion)
|
|
77
|
-
|
|
78
|
-
if (!same && isDevelopment()) {
|
|
79
|
-
log('[basic] version check mismatch:', {
|
|
80
|
-
currentVersion,
|
|
81
|
-
registryVersion: latestVersion,
|
|
82
|
-
channel: usesBetaDistTag(currentVersion) ? 'beta' : 'latest',
|
|
83
|
-
})
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
if (!same) {
|
|
87
|
-
console.warn('[basic] New version available:', latestVersion, `\nrun "npm install @basictech/react@${latestVersion}" to update`);
|
|
88
|
-
}
|
|
89
|
-
if (usesBetaDistTag(currentVersion)) {
|
|
90
|
-
log('thank you for being on basictech/react beta :)')
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return {
|
|
94
|
-
hasNewVersion: !same,
|
|
95
|
-
latestVersion,
|
|
96
|
-
currentVersion
|
|
97
|
-
};
|
|
98
|
-
} catch (error) {
|
|
99
|
-
log('Error checking for new version:', error);
|
|
100
|
-
return {
|
|
101
|
-
hasNewVersion: false,
|
|
102
|
-
latestVersion: null,
|
|
103
|
-
currentVersion: null
|
|
104
|
-
};
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export function cleanOAuthParamsFromUrl(): void {
|
|
109
|
-
if (window.location.search.includes('code') || window.location.search.includes('state')) {
|
|
110
|
-
const url = new URL(window.location.href)
|
|
111
|
-
url.searchParams.delete('code')
|
|
112
|
-
url.searchParams.delete('state')
|
|
113
|
-
window.history.replaceState({}, document.title, url.pathname + url.search)
|
|
114
|
-
log('Cleaned OAuth parameters from URL')
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export function getSyncStatus(statusCode: number): string {
|
|
119
|
-
switch (statusCode) {
|
|
120
|
-
case -1:
|
|
121
|
-
return "ERROR";
|
|
122
|
-
case 0:
|
|
123
|
-
return "OFFLINE";
|
|
124
|
-
case 1:
|
|
125
|
-
return "CONNECTING";
|
|
126
|
-
case 2:
|
|
127
|
-
return "ONLINE";
|
|
128
|
-
case 3:
|
|
129
|
-
return "SYNCING";
|
|
130
|
-
case 4:
|
|
131
|
-
return "ERROR_WILL_RETRY";
|
|
132
|
-
default:
|
|
133
|
-
return "UNKNOWN";
|
|
134
|
-
}
|
|
135
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Normalize a project_id / client_id to the canonical did:web format
|
|
5
|
-
* before sending it to the PDS.
|
|
6
|
-
*
|
|
7
|
-
* - did:web:... -> passthrough (already canonical)
|
|
8
|
-
* - bare UUID -> did:web:{adminHostname}:projects:{hex}
|
|
9
|
-
* - "self" -> passthrough
|
|
10
|
-
*/
|
|
11
|
-
export function normalizeClientId(projectId: string, adminHostname: string = 'api.basic.tech'): string {
|
|
12
|
-
if (!projectId) return projectId
|
|
13
|
-
if (projectId === 'self') return projectId
|
|
14
|
-
if (projectId.startsWith('did:')) return projectId
|
|
15
|
-
|
|
16
|
-
if (UUID_RE.test(projectId)) {
|
|
17
|
-
const hex = projectId.replace(/-/g, '').toLowerCase()
|
|
18
|
-
return `did:web:${adminHostname}:projects:${hex}`
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
return projectId
|
|
22
|
-
}
|
package/src/utils/resolveDid.ts
DELETED
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
export type ResolvedDid = {
|
|
2
|
-
did: string
|
|
3
|
-
handle?: string
|
|
4
|
-
didDocument: Record<string, unknown>
|
|
5
|
-
pdsUrl: string
|
|
6
|
-
authorization_endpoint: string
|
|
7
|
-
token_endpoint: string
|
|
8
|
-
userinfo_endpoint: string
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Convert a did:web DID to the HTTPS URL where its DID document lives.
|
|
13
|
-
*
|
|
14
|
-
* did:web:pds.basic.id:did:abc123 -> https://pds.basic.id/did/abc123/did.json
|
|
15
|
-
* did:web:example.com -> https://example.com/.well-known/did.json
|
|
16
|
-
*/
|
|
17
|
-
export function resolveDidWebUrl(did: string): string | null {
|
|
18
|
-
if (!did.startsWith('did:web:')) return null
|
|
19
|
-
|
|
20
|
-
const rest = did.slice(8) // strip 'did:web:'
|
|
21
|
-
if (!rest) return null
|
|
22
|
-
const parts = rest.split(':')
|
|
23
|
-
|
|
24
|
-
// Decode the hostname (first part, may contain %3A for port)
|
|
25
|
-
const hostname = parts[0]!.replace(/%3A/gi, ':')
|
|
26
|
-
|
|
27
|
-
if (parts.length === 1) {
|
|
28
|
-
return `https://${hostname}/.well-known/did.json`
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
const pathParts = parts.slice(1).map(p => decodeURIComponent(p))
|
|
32
|
-
return `https://${hostname}/${pathParts.join('/')}/did.json`
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
/**
|
|
36
|
-
* Given a DID document, extract PDS URL and discover OAuth endpoints.
|
|
37
|
-
*/
|
|
38
|
-
async function resolveFromDocument(did: string, didDocument: Record<string, unknown>): Promise<ResolvedDid> {
|
|
39
|
-
const services = didDocument.service as Array<{ id: string; type: string; serviceEndpoint: string }> | undefined
|
|
40
|
-
const pdsService = services?.find(
|
|
41
|
-
(s) => s.id === '#basic_pds' || s.id === `${did}#basic_pds`
|
|
42
|
-
)
|
|
43
|
-
if (!pdsService) {
|
|
44
|
-
throw new Error(`DID document has no #basic_pds service entry`)
|
|
45
|
-
}
|
|
46
|
-
const pdsUrl = pdsService.serviceEndpoint.replace(/\/+$/, '')
|
|
47
|
-
|
|
48
|
-
const oauthRes = await fetch(`${pdsUrl}/auth/.well-known/openid-configuration`)
|
|
49
|
-
if (!oauthRes.ok) {
|
|
50
|
-
throw new Error(`Failed to fetch OpenID configuration from ${pdsUrl}: ${oauthRes.status}`)
|
|
51
|
-
}
|
|
52
|
-
const oauth = await oauthRes.json()
|
|
53
|
-
|
|
54
|
-
return {
|
|
55
|
-
did,
|
|
56
|
-
didDocument,
|
|
57
|
-
pdsUrl,
|
|
58
|
-
authorization_endpoint: oauth.authorization_endpoint,
|
|
59
|
-
token_endpoint: oauth.token_endpoint,
|
|
60
|
-
userinfo_endpoint: oauth.userinfo_endpoint,
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Fetch a DID document by DID, extract the PDS URL, and discover OAuth endpoints.
|
|
66
|
-
*/
|
|
67
|
-
export async function resolveDid(did: string): Promise<ResolvedDid> {
|
|
68
|
-
const url = resolveDidWebUrl(did)
|
|
69
|
-
if (!url) {
|
|
70
|
-
throw new Error(`Unsupported DID method: ${did}`)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const didRes = await fetch(url)
|
|
74
|
-
if (!didRes.ok) {
|
|
75
|
-
throw new Error(`Failed to fetch DID document at ${url}: ${didRes.status}`)
|
|
76
|
-
}
|
|
77
|
-
const didDocument = await didRes.json()
|
|
78
|
-
|
|
79
|
-
return resolveFromDocument(did, didDocument)
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* Resolve a handle (e.g. "alice.basic.id") to a DID and discover PDS + OAuth endpoints.
|
|
84
|
-
*
|
|
85
|
-
* Fetches https://{handle}/.well-known/did.json per the did:web spec.
|
|
86
|
-
*/
|
|
87
|
-
export async function resolveHandle(handle: string): Promise<ResolvedDid> {
|
|
88
|
-
const res = await fetch(`https://${handle}/.well-known/did.json`)
|
|
89
|
-
if (!res.ok) {
|
|
90
|
-
throw new Error(`Handle resolution failed for ${handle}: ${res.status}`)
|
|
91
|
-
}
|
|
92
|
-
const didDocument = await res.json()
|
|
93
|
-
const did = didDocument.id as string
|
|
94
|
-
if (!did) {
|
|
95
|
-
throw new Error(`Handle response has no 'id' field`)
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const resolved = await resolveFromDocument(did, didDocument)
|
|
99
|
-
resolved.handle = handle
|
|
100
|
-
return resolved
|
|
101
|
-
}
|
package/src/utils/schema.ts
DELETED
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
// Schema utilities for Basic React package
|
|
2
|
-
import { validateSchema, compareSchemas } from '@basictech/schema'
|
|
3
|
-
import { log } from '../config'
|
|
4
|
-
|
|
5
|
-
export async function getSchemaStatus(schema: any): Promise<{
|
|
6
|
-
valid: boolean,
|
|
7
|
-
status: string,
|
|
8
|
-
latest: any
|
|
9
|
-
}> {
|
|
10
|
-
const projectId = schema.project_id
|
|
11
|
-
const valid = validateSchema(schema)
|
|
12
|
-
|
|
13
|
-
if (!valid.valid) {
|
|
14
|
-
console.warn('BasicDB Error: your local schema is invalid. Please fix errors and try again - sync is disabled')
|
|
15
|
-
return {
|
|
16
|
-
valid: false,
|
|
17
|
-
status: 'invalid',
|
|
18
|
-
latest: null
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const latestSchema = await fetch(`https://api.basic.tech/project/${projectId}/schema`)
|
|
23
|
-
.then(res => res.json())
|
|
24
|
-
.then(data => data.data[0].schema)
|
|
25
|
-
.catch(err => {
|
|
26
|
-
return {
|
|
27
|
-
valid: false,
|
|
28
|
-
status: 'error',
|
|
29
|
-
latest: null
|
|
30
|
-
}
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
if (!latestSchema.version) {
|
|
34
|
-
return {
|
|
35
|
-
valid: false,
|
|
36
|
-
status: 'error',
|
|
37
|
-
latest: null
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (latestSchema.version > schema.version) {
|
|
42
|
-
// error_code: schema_behind
|
|
43
|
-
console.warn('BasicDB Error: your local schema version is behind the latest. Found version:', schema.version, 'but expected', latestSchema.version, " - sync is disabled")
|
|
44
|
-
return {
|
|
45
|
-
valid: false,
|
|
46
|
-
status: 'behind',
|
|
47
|
-
latest: latestSchema
|
|
48
|
-
}
|
|
49
|
-
} else if (latestSchema.version < schema.version) {
|
|
50
|
-
// error_code: schema_ahead
|
|
51
|
-
console.warn('BasicDB Error: your local schema version is ahead of the latest. Found version:', schema.version, 'but expected', latestSchema.version, " - sync is disabled")
|
|
52
|
-
return {
|
|
53
|
-
valid: false,
|
|
54
|
-
status: 'ahead',
|
|
55
|
-
latest: latestSchema
|
|
56
|
-
}
|
|
57
|
-
} else if (latestSchema.version === schema.version) {
|
|
58
|
-
const changes = compareSchemas(schema, latestSchema)
|
|
59
|
-
if (changes.valid) {
|
|
60
|
-
return {
|
|
61
|
-
valid: true,
|
|
62
|
-
status: 'current',
|
|
63
|
-
latest: latestSchema
|
|
64
|
-
}
|
|
65
|
-
} else {
|
|
66
|
-
// error_code: schema_conflict
|
|
67
|
-
console.warn('BasicDB Error: your local schema is conflicting with the latest. Your version:', schema.version, 'does not match origin version', latestSchema.version, " - sync is disabled")
|
|
68
|
-
return {
|
|
69
|
-
valid: false,
|
|
70
|
-
status: 'conflict',
|
|
71
|
-
latest: latestSchema
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
} else {
|
|
75
|
-
return {
|
|
76
|
-
valid: false,
|
|
77
|
-
status: 'error',
|
|
78
|
-
latest: null
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export async function validateAndCheckSchema(schema: any): Promise<{
|
|
84
|
-
isValid: boolean,
|
|
85
|
-
schemaStatus: { valid: boolean, status?: string, latest?: any },
|
|
86
|
-
errors?: any[]
|
|
87
|
-
}> {
|
|
88
|
-
const valid = validateSchema(schema)
|
|
89
|
-
if (!valid.valid) {
|
|
90
|
-
log('Basic Schema is invalid!', valid.errors)
|
|
91
|
-
console.group('Schema Errors')
|
|
92
|
-
let errorMessage = ''
|
|
93
|
-
valid.errors.forEach((error, index) => {
|
|
94
|
-
log(`${index + 1}:`, error.message, ` - at ${error.instancePath}`)
|
|
95
|
-
errorMessage += `${index + 1}: ${error.message} - at ${error.instancePath}\n`
|
|
96
|
-
})
|
|
97
|
-
console.groupEnd()
|
|
98
|
-
|
|
99
|
-
return {
|
|
100
|
-
isValid: false,
|
|
101
|
-
schemaStatus: { valid: false },
|
|
102
|
-
errors: valid.errors
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
let schemaStatus: { valid: boolean, status?: string, latest?: any } = { valid: false }
|
|
107
|
-
if (schema.version !== 0) {
|
|
108
|
-
schemaStatus = await getSchemaStatus(schema)
|
|
109
|
-
log('schemaStatus', schemaStatus)
|
|
110
|
-
} else {
|
|
111
|
-
schemaStatus = { valid: false, status: 'unpublished' }
|
|
112
|
-
log("schema not published - at version 0")
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
return {
|
|
116
|
-
isValid: true,
|
|
117
|
-
schemaStatus
|
|
118
|
-
}
|
|
119
|
-
}
|
package/src/utils/storage.ts
DELETED
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
// Storage utilities for Basic React package
|
|
2
|
-
export interface BasicStorage {
|
|
3
|
-
get(key: string): Promise<string | null>
|
|
4
|
-
set(key: string, value: string): Promise<void>
|
|
5
|
-
remove(key: string): Promise<void>
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
export class LocalStorageAdapter implements BasicStorage {
|
|
9
|
-
async get(key: string): Promise<string | null> {
|
|
10
|
-
return localStorage.getItem(key)
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
async set(key: string, value: string): Promise<void> {
|
|
14
|
-
localStorage.setItem(key, value)
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
async remove(key: string): Promise<void> {
|
|
18
|
-
localStorage.removeItem(key)
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export const STORAGE_KEYS = {
|
|
23
|
-
REFRESH_TOKEN: 'basic_refresh_token',
|
|
24
|
-
USER_INFO: 'basic_user_info',
|
|
25
|
-
AUTH_STATE: 'basic_auth_state',
|
|
26
|
-
REDIRECT_URI: 'basic_redirect_uri',
|
|
27
|
-
SERVER_URL: 'basic_server_url',
|
|
28
|
-
PDS_ENDPOINTS: 'basic_pds_endpoints',
|
|
29
|
-
LAST_CONNECT_REPORT: 'basic_last_connect_report',
|
|
30
|
-
DEBUG: 'basic_debug',
|
|
31
|
-
CODE_VERIFIER: 'basic_code_verifier'
|
|
32
|
-
} as const
|
|
33
|
-
|
|
34
|
-
export function getCookie(name: string): string {
|
|
35
|
-
let cookieValue = '';
|
|
36
|
-
if (document.cookie && document.cookie !== '') {
|
|
37
|
-
const cookies = document.cookie.split(';');
|
|
38
|
-
for (let i = 0; i < cookies.length; i++) {
|
|
39
|
-
const cookie = cookies[i]?.trim();
|
|
40
|
-
if (cookie && cookie.substring(0, name.length + 1) === (name + '=')) {
|
|
41
|
-
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
|
42
|
-
break;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
return cookieValue;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function setCookie(name: string, value: string, options?: { secure?: boolean, sameSite?: string, httpOnly?: boolean }): void {
|
|
50
|
-
const opts = {
|
|
51
|
-
secure: true,
|
|
52
|
-
sameSite: 'Strict',
|
|
53
|
-
httpOnly: false,
|
|
54
|
-
...options
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
let cookieString = `${name}=${value}`;
|
|
58
|
-
if (opts.secure) cookieString += '; Secure';
|
|
59
|
-
if (opts.sameSite) cookieString += `; SameSite=${opts.sameSite}`;
|
|
60
|
-
if (opts.httpOnly) cookieString += '; HttpOnly';
|
|
61
|
-
|
|
62
|
-
document.cookie = cookieString;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function clearCookie(name: string): void {
|
|
66
|
-
document.cookie = `${name}=; Secure; SameSite=Strict`;
|
|
67
|
-
}
|
package/tsconfig.json
DELETED