@basictech/react 0.7.0-beta.1 → 0.7.0-beta.2
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 +6 -0
- package/dist/index.d.mts +107 -1
- package/dist/index.d.ts +107 -1
- package/dist/index.js +719 -356
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +716 -344
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/AuthContext.tsx +178 -402
- package/src/index.ts +6 -31
- package/src/sync/index.ts +1 -1
- package/src/updater/updateMigrations.ts +22 -0
- package/src/updater/versionUpdater.ts +160 -0
- package/src/utils/network.ts +82 -0
- package/src/utils/schema.ts +120 -0
- package/src/utils/storage.ts +62 -0
- package/src/schema.ts +0 -159
package/src/index.ts
CHANGED
|
@@ -1,38 +1,13 @@
|
|
|
1
1
|
import { useState } from "react";
|
|
2
2
|
import { useBasic, BasicProvider, BasicStorage, LocalStorageAdapter } from "./AuthContext";
|
|
3
3
|
import { useLiveQuery as useQuery } from "dexie-react-hooks";
|
|
4
|
-
|
|
5
|
-
// const useQuery = (queryable: any) => {
|
|
6
|
-
// const [loading, setLoading] = useState(true)
|
|
7
|
-
// const [error, setError] = useState<Error | null>(null)
|
|
8
|
-
|
|
9
|
-
// const result = useLiveQuery(async () => {
|
|
10
|
-
// try {
|
|
11
|
-
// setLoading(true)
|
|
12
|
-
// setError(null)
|
|
13
|
-
|
|
14
|
-
// // if (typeof queryable === 'function') {
|
|
15
|
-
// // return await queryable()
|
|
16
|
-
// // }
|
|
17
|
-
// return queryable
|
|
18
|
-
|
|
19
|
-
// } catch (err) {
|
|
20
|
-
// setError(err instanceof Error ? err : new Error('Unknown error'))
|
|
21
|
-
// return undefined
|
|
22
|
-
// } finally {
|
|
23
|
-
// setLoading(false)
|
|
24
|
-
// }
|
|
25
|
-
// }, [queryable])
|
|
26
|
-
|
|
27
|
-
// return {
|
|
28
|
-
// data: result,
|
|
29
|
-
// loading,
|
|
30
|
-
// error
|
|
31
|
-
// }
|
|
32
|
-
// }
|
|
33
|
-
|
|
4
|
+
// import { createVersionUpdater, VersionUpdater, Migration } from "./versionUpdater";
|
|
34
5
|
|
|
35
6
|
|
|
36
7
|
export {
|
|
37
|
-
useBasic, BasicProvider, useQuery
|
|
8
|
+
useBasic, BasicProvider, useQuery
|
|
38
9
|
}
|
|
10
|
+
|
|
11
|
+
// export type {
|
|
12
|
+
// VersionUpdater, Migration
|
|
13
|
+
// }
|
package/src/sync/index.ts
CHANGED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { BasicStorage } from '../utils/storage'
|
|
2
|
+
import { Migration } from './versionUpdater'
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
export const addMigrationTimestamp: Migration = {
|
|
6
|
+
fromVersion: '0.6.0',
|
|
7
|
+
toVersion: '0.7.0',
|
|
8
|
+
async migrate(storage: BasicStorage) {
|
|
9
|
+
console.log('Running test migration')
|
|
10
|
+
storage.set('test_migration', 'true')
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Get all available migrations
|
|
17
|
+
*/
|
|
18
|
+
export function getMigrations(): Migration[] {
|
|
19
|
+
return [
|
|
20
|
+
addMigrationTimestamp
|
|
21
|
+
]
|
|
22
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { BasicStorage } from '../utils/storage'
|
|
2
|
+
|
|
3
|
+
export interface VersionInfo {
|
|
4
|
+
version: string
|
|
5
|
+
lastUpdated: number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface Migration {
|
|
9
|
+
fromVersion: string
|
|
10
|
+
toVersion: string
|
|
11
|
+
migrate: (storage: BasicStorage) => Promise<void>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export class VersionUpdater {
|
|
15
|
+
private storage: BasicStorage
|
|
16
|
+
private currentVersion: string
|
|
17
|
+
private migrations: Migration[]
|
|
18
|
+
private versionKey = 'basic_app_version'
|
|
19
|
+
|
|
20
|
+
constructor(storage: BasicStorage, currentVersion: string, migrations: Migration[] = []) {
|
|
21
|
+
this.storage = storage
|
|
22
|
+
this.currentVersion = currentVersion
|
|
23
|
+
this.migrations = migrations.sort((a, b) => this.compareVersions(a.fromVersion, b.fromVersion))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Check current stored version and run migrations if needed
|
|
28
|
+
* Only compares major.minor versions, ignoring beta/prerelease parts
|
|
29
|
+
* Example: "0.7.0-beta.1" and "0.7.0" are treated as the same version
|
|
30
|
+
*/
|
|
31
|
+
async checkAndUpdate(): Promise<{ updated: boolean; fromVersion?: string; toVersion: string }> {
|
|
32
|
+
const storedVersion = await this.getStoredVersion()
|
|
33
|
+
|
|
34
|
+
if (!storedVersion) {
|
|
35
|
+
// First time setup
|
|
36
|
+
await this.setStoredVersion(this.currentVersion)
|
|
37
|
+
return { updated: false, toVersion: this.currentVersion }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (storedVersion === this.currentVersion) {
|
|
41
|
+
return { updated: false, toVersion: this.currentVersion }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Need to run migrations
|
|
45
|
+
const migrationsToRun = this.getMigrationsToRun(storedVersion, this.currentVersion)
|
|
46
|
+
|
|
47
|
+
if (migrationsToRun.length === 0) {
|
|
48
|
+
// No migrations needed, just update version
|
|
49
|
+
await this.setStoredVersion(this.currentVersion)
|
|
50
|
+
return { updated: true, fromVersion: storedVersion, toVersion: this.currentVersion }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Run migrations
|
|
54
|
+
for (const migration of migrationsToRun) {
|
|
55
|
+
try {
|
|
56
|
+
console.log(`Running migration from ${migration.fromVersion} to ${migration.toVersion}`)
|
|
57
|
+
await migration.migrate(this.storage)
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error(`Migration failed from ${migration.fromVersion} to ${migration.toVersion}:`, error)
|
|
60
|
+
throw new Error(`Migration failed: ${error}`)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Update to current version
|
|
65
|
+
await this.setStoredVersion(this.currentVersion)
|
|
66
|
+
return { updated: true, fromVersion: storedVersion, toVersion: this.currentVersion }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private async getStoredVersion(): Promise<string | null> {
|
|
70
|
+
try {
|
|
71
|
+
const versionData = await this.storage.get(this.versionKey)
|
|
72
|
+
if (!versionData) return null
|
|
73
|
+
|
|
74
|
+
const versionInfo: VersionInfo = JSON.parse(versionData)
|
|
75
|
+
return versionInfo.version
|
|
76
|
+
} catch (error) {
|
|
77
|
+
console.warn('Failed to get stored version:', error)
|
|
78
|
+
return null
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
private async setStoredVersion(version: string): Promise<void> {
|
|
83
|
+
const versionInfo: VersionInfo = {
|
|
84
|
+
version,
|
|
85
|
+
lastUpdated: Date.now()
|
|
86
|
+
}
|
|
87
|
+
await this.storage.set(this.versionKey, JSON.stringify(versionInfo))
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
private getMigrationsToRun(fromVersion: string, toVersion: string): Migration[] {
|
|
91
|
+
return this.migrations.filter(migration => {
|
|
92
|
+
// Migration should run if we're crossing the version boundary
|
|
93
|
+
// i.e., stored version is less than migration.toVersion AND current version is >= migration.toVersion
|
|
94
|
+
const storedLessThanMigrationTo = this.compareVersions(fromVersion, migration.toVersion) < 0
|
|
95
|
+
const currentGreaterThanOrEqualMigrationTo = this.compareVersions(toVersion, migration.toVersion) >= 0
|
|
96
|
+
|
|
97
|
+
console.log(`Checking migration ${migration.fromVersion} → ${migration.toVersion}:`)
|
|
98
|
+
console.log(` stored ${fromVersion} < migration.to ${migration.toVersion}: ${storedLessThanMigrationTo}`)
|
|
99
|
+
console.log(` current ${toVersion} >= migration.to ${migration.toVersion}: ${currentGreaterThanOrEqualMigrationTo}`)
|
|
100
|
+
|
|
101
|
+
const shouldRun = storedLessThanMigrationTo && currentGreaterThanOrEqualMigrationTo
|
|
102
|
+
console.log(` Should run: ${shouldRun}`)
|
|
103
|
+
|
|
104
|
+
return shouldRun
|
|
105
|
+
})
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Simple semantic version comparison (major.minor only, ignoring beta/prerelease)
|
|
110
|
+
* Returns: -1 if a < b, 0 if a === b, 1 if a > b
|
|
111
|
+
*/
|
|
112
|
+
private compareVersions(a: string, b: string): number {
|
|
113
|
+
// Extract major.minor from version strings, ignoring beta/prerelease parts
|
|
114
|
+
const aMajorMinor = this.extractMajorMinor(a)
|
|
115
|
+
const bMajorMinor = this.extractMajorMinor(b)
|
|
116
|
+
|
|
117
|
+
// Compare major version first
|
|
118
|
+
if (aMajorMinor.major !== bMajorMinor.major) {
|
|
119
|
+
return aMajorMinor.major - bMajorMinor.major
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Then compare minor version
|
|
123
|
+
return aMajorMinor.minor - bMajorMinor.minor
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Extract major.minor from version string, ignoring beta/prerelease
|
|
128
|
+
* Examples: "0.7.0-beta.1" -> {major: 0, minor: 7}
|
|
129
|
+
* "1.2.3" -> {major: 1, minor: 2}
|
|
130
|
+
*/
|
|
131
|
+
private extractMajorMinor(version: string): { major: number, minor: number } {
|
|
132
|
+
// Remove beta/prerelease parts and split by dots
|
|
133
|
+
const cleanVersion = version.split('-')[0]?.split('+')[0] || version
|
|
134
|
+
const parts = cleanVersion.split('.').map(Number)
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
major: parts[0] || 0,
|
|
138
|
+
minor: parts[1] || 0
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Add a migration to the updater
|
|
144
|
+
*/
|
|
145
|
+
addMigration(migration: Migration): void {
|
|
146
|
+
this.migrations.push(migration)
|
|
147
|
+
this.migrations.sort((a, b) => this.compareVersions(a.fromVersion, b.fromVersion))
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Create a simple version updater instance
|
|
153
|
+
*/
|
|
154
|
+
export function createVersionUpdater(
|
|
155
|
+
storage: BasicStorage,
|
|
156
|
+
currentVersion: string,
|
|
157
|
+
migrations: Migration[] = []
|
|
158
|
+
): VersionUpdater {
|
|
159
|
+
return new VersionUpdater(storage, currentVersion, migrations)
|
|
160
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
// Network utilities for Basic React package
|
|
2
|
+
import { log } from '../config'
|
|
3
|
+
import { version as currentVersion } from '../../package.json'
|
|
4
|
+
|
|
5
|
+
export function isDevelopment(debug?: boolean): boolean {
|
|
6
|
+
return (
|
|
7
|
+
window.location.hostname === 'localhost' ||
|
|
8
|
+
window.location.hostname === '127.0.0.1' ||
|
|
9
|
+
window.location.hostname.includes('localhost') ||
|
|
10
|
+
window.location.hostname.includes('127.0.0.1') ||
|
|
11
|
+
window.location.hostname.includes('.local') ||
|
|
12
|
+
process.env.NODE_ENV === 'development' ||
|
|
13
|
+
debug === true
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export async function checkForNewVersion(): Promise<{
|
|
18
|
+
hasNewVersion: boolean,
|
|
19
|
+
latestVersion: string | null,
|
|
20
|
+
currentVersion: string | null
|
|
21
|
+
}> {
|
|
22
|
+
try {
|
|
23
|
+
const isBeta = currentVersion.includes('beta')
|
|
24
|
+
|
|
25
|
+
const response = await fetch(`https://registry.npmjs.org/@basictech/react/${isBeta ? 'beta' : 'latest'}`);
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
throw new Error('Failed to fetch version from npm');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const data = await response.json();
|
|
31
|
+
const latestVersion = data.version;
|
|
32
|
+
|
|
33
|
+
if (latestVersion !== currentVersion) {
|
|
34
|
+
console.warn('[basic] New version available:', latestVersion, `\nrun "npm install @basictech/react@${latestVersion}" to update`);
|
|
35
|
+
}
|
|
36
|
+
if (isBeta) {
|
|
37
|
+
log('thank you for being on basictech/react beta :)')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return {
|
|
41
|
+
hasNewVersion: currentVersion !== latestVersion,
|
|
42
|
+
latestVersion,
|
|
43
|
+
currentVersion
|
|
44
|
+
};
|
|
45
|
+
} catch (error) {
|
|
46
|
+
log('Error checking for new version:', error);
|
|
47
|
+
return {
|
|
48
|
+
hasNewVersion: false,
|
|
49
|
+
latestVersion: null,
|
|
50
|
+
currentVersion: null
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function cleanOAuthParamsFromUrl(): void {
|
|
56
|
+
if (window.location.search.includes('code') || window.location.search.includes('state')) {
|
|
57
|
+
const url = new URL(window.location.href)
|
|
58
|
+
url.searchParams.delete('code')
|
|
59
|
+
url.searchParams.delete('state')
|
|
60
|
+
window.history.pushState({}, document.title, url.pathname + url.search)
|
|
61
|
+
log('Cleaned OAuth parameters from URL')
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export function getSyncStatus(statusCode: number): string {
|
|
66
|
+
switch (statusCode) {
|
|
67
|
+
case -1:
|
|
68
|
+
return "ERROR";
|
|
69
|
+
case 0:
|
|
70
|
+
return "OFFLINE";
|
|
71
|
+
case 1:
|
|
72
|
+
return "CONNECTING";
|
|
73
|
+
case 2:
|
|
74
|
+
return "ONLINE";
|
|
75
|
+
case 3:
|
|
76
|
+
return "SYNCING";
|
|
77
|
+
case 4:
|
|
78
|
+
return "ERROR_WILL_RETRY";
|
|
79
|
+
default:
|
|
80
|
+
return "UNKNOWN";
|
|
81
|
+
}
|
|
82
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
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
|
+
console.log('latestSchema', latestSchema)
|
|
34
|
+
|
|
35
|
+
if (!latestSchema.version) {
|
|
36
|
+
return {
|
|
37
|
+
valid: false,
|
|
38
|
+
status: 'error',
|
|
39
|
+
latest: null
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (latestSchema.version > schema.version) {
|
|
44
|
+
// error_code: schema_behind
|
|
45
|
+
console.warn('BasicDB Error: your local schema version is behind the latest. Found version:', schema.version, 'but expected', latestSchema.version, " - sync is disabled")
|
|
46
|
+
return {
|
|
47
|
+
valid: false,
|
|
48
|
+
status: 'behind',
|
|
49
|
+
latest: latestSchema
|
|
50
|
+
}
|
|
51
|
+
} else if (latestSchema.version < schema.version) {
|
|
52
|
+
// error_code: schema_ahead
|
|
53
|
+
console.warn('BasicDB Error: your local schema version is ahead of the latest. Found version:', schema.version, 'but expected', latestSchema.version, " - sync is disabled")
|
|
54
|
+
return {
|
|
55
|
+
valid: false,
|
|
56
|
+
status: 'ahead',
|
|
57
|
+
latest: latestSchema
|
|
58
|
+
}
|
|
59
|
+
} else if (latestSchema.version === schema.version) {
|
|
60
|
+
const changes = compareSchemas(schema, latestSchema)
|
|
61
|
+
if (changes.valid) {
|
|
62
|
+
return {
|
|
63
|
+
valid: true,
|
|
64
|
+
status: 'current',
|
|
65
|
+
latest: latestSchema
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
// error_code: schema_conflict
|
|
69
|
+
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")
|
|
70
|
+
return {
|
|
71
|
+
valid: false,
|
|
72
|
+
status: 'conflict',
|
|
73
|
+
latest: latestSchema
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
} else {
|
|
77
|
+
return {
|
|
78
|
+
valid: false,
|
|
79
|
+
status: 'error',
|
|
80
|
+
latest: null
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export async function validateAndCheckSchema(schema: any): Promise<{
|
|
86
|
+
isValid: boolean,
|
|
87
|
+
schemaStatus: { valid: boolean, status?: string, latest?: any },
|
|
88
|
+
errors?: any[]
|
|
89
|
+
}> {
|
|
90
|
+
const valid = validateSchema(schema)
|
|
91
|
+
if (!valid.valid) {
|
|
92
|
+
log('Basic Schema is invalid!', valid.errors)
|
|
93
|
+
console.group('Schema Errors')
|
|
94
|
+
let errorMessage = ''
|
|
95
|
+
valid.errors.forEach((error, index) => {
|
|
96
|
+
log(`${index + 1}:`, error.message, ` - at ${error.instancePath}`)
|
|
97
|
+
errorMessage += `${index + 1}: ${error.message} - at ${error.instancePath}\n`
|
|
98
|
+
})
|
|
99
|
+
console.groupEnd()
|
|
100
|
+
|
|
101
|
+
return {
|
|
102
|
+
isValid: false,
|
|
103
|
+
schemaStatus: { valid: false },
|
|
104
|
+
errors: valid.errors
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
let schemaStatus = { valid: false }
|
|
109
|
+
if (schema.version !== 0) {
|
|
110
|
+
schemaStatus = await getSchemaStatus(schema)
|
|
111
|
+
log('schemaStatus', schemaStatus)
|
|
112
|
+
} else {
|
|
113
|
+
log("schema not published - at version 0")
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
isValid: true,
|
|
118
|
+
schemaStatus
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
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
|
+
DEBUG: 'basic_debug'
|
|
27
|
+
} as const
|
|
28
|
+
|
|
29
|
+
export function getCookie(name: string): string {
|
|
30
|
+
let cookieValue = '';
|
|
31
|
+
if (document.cookie && document.cookie !== '') {
|
|
32
|
+
const cookies = document.cookie.split(';');
|
|
33
|
+
for (let i = 0; i < cookies.length; i++) {
|
|
34
|
+
const cookie = cookies[i]?.trim();
|
|
35
|
+
if (cookie && cookie.substring(0, name.length + 1) === (name + '=')) {
|
|
36
|
+
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return cookieValue;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function setCookie(name: string, value: string, options?: { secure?: boolean, sameSite?: string, httpOnly?: boolean }): void {
|
|
45
|
+
const opts = {
|
|
46
|
+
secure: true,
|
|
47
|
+
sameSite: 'Strict',
|
|
48
|
+
httpOnly: false,
|
|
49
|
+
...options
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
let cookieString = `${name}=${value}`;
|
|
53
|
+
if (opts.secure) cookieString += '; Secure';
|
|
54
|
+
if (opts.sameSite) cookieString += `; SameSite=${opts.sameSite}`;
|
|
55
|
+
if (opts.httpOnly) cookieString += '; HttpOnly';
|
|
56
|
+
|
|
57
|
+
document.cookie = cookieString;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function clearCookie(name: string): void {
|
|
61
|
+
document.cookie = `${name}=; Secure; SameSite=Strict`;
|
|
62
|
+
}
|
package/src/schema.ts
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
// Basic Schema Library
|
|
2
|
-
// utils for validating and interacting with Basic schemas
|
|
3
|
-
import Ajv, { ErrorObject } from 'ajv'
|
|
4
|
-
|
|
5
|
-
const basicJsonSchema = {
|
|
6
|
-
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
7
|
-
"type": "object",
|
|
8
|
-
"properties": {
|
|
9
|
-
"project_id": {
|
|
10
|
-
"type": "string"
|
|
11
|
-
},
|
|
12
|
-
"namespace": {
|
|
13
|
-
"type": "string",
|
|
14
|
-
},
|
|
15
|
-
"version": {
|
|
16
|
-
"type": "integer",
|
|
17
|
-
"minimum": 0
|
|
18
|
-
},
|
|
19
|
-
"tables": {
|
|
20
|
-
"type": "object",
|
|
21
|
-
"patternProperties": {
|
|
22
|
-
"^[a-zA-Z0-9_]+$": {
|
|
23
|
-
"type": "object",
|
|
24
|
-
"properties": {
|
|
25
|
-
"name": {
|
|
26
|
-
"type": "string"
|
|
27
|
-
},
|
|
28
|
-
"type": {
|
|
29
|
-
"type": "string",
|
|
30
|
-
"enum": ["collection"]
|
|
31
|
-
},
|
|
32
|
-
"fields": {
|
|
33
|
-
"type": "object",
|
|
34
|
-
"patternProperties": {
|
|
35
|
-
"^[a-zA-Z0-9_]+$": {
|
|
36
|
-
"type": "object",
|
|
37
|
-
"properties": {
|
|
38
|
-
"type": {
|
|
39
|
-
"type": "string",
|
|
40
|
-
"enum": ["string", "boolean", "number", "json"]
|
|
41
|
-
},
|
|
42
|
-
"indexed": {
|
|
43
|
-
"type": "boolean"
|
|
44
|
-
},
|
|
45
|
-
"required": {
|
|
46
|
-
"type": "boolean"
|
|
47
|
-
}
|
|
48
|
-
},
|
|
49
|
-
"required": ["type"]
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
"additionalProperties": true
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
"required": ["fields"]
|
|
56
|
-
}
|
|
57
|
-
},
|
|
58
|
-
"additionalProperties": true
|
|
59
|
-
}
|
|
60
|
-
},
|
|
61
|
-
"required": ["project_id", "version", "tables"]
|
|
62
|
-
}
|
|
63
|
-
const ajv = new Ajv()
|
|
64
|
-
const validator = ajv.compile(basicJsonSchema)
|
|
65
|
-
|
|
66
|
-
type Schema = typeof basicJsonSchema
|
|
67
|
-
|
|
68
|
-
function generateEmptySchema() {
|
|
69
|
-
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Validate a schema
|
|
75
|
-
* only checks if the schema is formatted correctly, not if can be published
|
|
76
|
-
* @param schema - The schema to validate
|
|
77
|
-
* @returns {valid: boolean, errors: any[]} - The validation result
|
|
78
|
-
*/
|
|
79
|
-
function validateSchema(schema: Schema) : {valid: boolean, errors: ErrorObject[]} {
|
|
80
|
-
const v = validator(schema)
|
|
81
|
-
return {
|
|
82
|
-
valid: v,
|
|
83
|
-
errors: validator.errors || []
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
// type ErrorObject = {
|
|
88
|
-
// keyword: string;
|
|
89
|
-
// instancePath: string;
|
|
90
|
-
// schemaPath: string;
|
|
91
|
-
// params: Record<string, any>;
|
|
92
|
-
// propertyName?: string;
|
|
93
|
-
// message?: string;
|
|
94
|
-
// schema?: any;
|
|
95
|
-
// parentSchema?: any;
|
|
96
|
-
// data?: any;
|
|
97
|
-
// }
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
function validateData(schema: any, table: string, data: Record<string, any>, checkRequired: boolean = true) {
|
|
101
|
-
const valid = validateSchema(schema)
|
|
102
|
-
if (!valid.valid) {
|
|
103
|
-
return { valid: false, errors: valid.errors, message: "Schema is invalid" }
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const tableSchema = schema.tables[table]
|
|
107
|
-
|
|
108
|
-
if (!tableSchema) {
|
|
109
|
-
return { valid: false, errors: [{ message: `Table ${table} not found in schema` }], message: "Table not found" }
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
for (const [fieldName, fieldValue] of Object.entries(data)) {
|
|
113
|
-
const fieldSchema = tableSchema.fields[fieldName]
|
|
114
|
-
|
|
115
|
-
if (!fieldSchema) {
|
|
116
|
-
return {
|
|
117
|
-
valid: false,
|
|
118
|
-
errors: [{ message: `Field ${fieldName} not found in schema` }],
|
|
119
|
-
message: "Invalid field"
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const schemaType = fieldSchema.type
|
|
124
|
-
const valueType = typeof fieldValue
|
|
125
|
-
|
|
126
|
-
if (
|
|
127
|
-
(schemaType === 'string' && valueType !== 'string') ||
|
|
128
|
-
(schemaType === 'number' && valueType !== 'number') ||
|
|
129
|
-
(schemaType === 'boolean' && valueType !== 'boolean') ||
|
|
130
|
-
(schemaType === 'json' && valueType !== 'object')
|
|
131
|
-
) {
|
|
132
|
-
return {
|
|
133
|
-
valid: false,
|
|
134
|
-
errors: [{
|
|
135
|
-
message: `Field ${fieldName} should be type ${schemaType}, got ${valueType}`
|
|
136
|
-
}],
|
|
137
|
-
message: "invalid type"
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
if (checkRequired) {
|
|
143
|
-
for (const [fieldName, fieldSchema] of Object.entries(tableSchema.fields)) {
|
|
144
|
-
if ((fieldSchema as { required?: boolean }).required && !data[fieldName]) {
|
|
145
|
-
return { valid: false, errors: [{ message: `Field ${fieldName} is required` }], message: "Required field missing" }
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return { valid: true, errors: [] }
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
export {
|
|
155
|
-
validateSchema,
|
|
156
|
-
validateData,
|
|
157
|
-
generateEmptySchema
|
|
158
|
-
}
|
|
159
|
-
|