@shaxpir/duiduidui-models 1.8.1 → 1.9.0
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/dist/models/Condition.d.ts +15 -1
- package/dist/models/Condition.js +31 -0
- package/dist/models/Device.d.ts +13 -0
- package/dist/models/Device.js +70 -1
- package/dist/util/Database.d.ts +124 -0
- package/dist/util/Database.js +135 -0
- package/dist/util/index.d.ts +1 -0
- package/dist/util/index.js +1 -0
- package/package.json +1 -1
|
@@ -37,7 +37,16 @@ export interface DifficultyCondition extends BaseCondition {
|
|
|
37
37
|
min?: number;
|
|
38
38
|
max?: number;
|
|
39
39
|
}
|
|
40
|
-
export
|
|
40
|
+
export interface HasTermCondition extends BaseCondition {
|
|
41
|
+
type: 'has_term';
|
|
42
|
+
value: boolean;
|
|
43
|
+
}
|
|
44
|
+
export interface ComponentCondition extends BaseCondition {
|
|
45
|
+
type: 'component';
|
|
46
|
+
components: string[];
|
|
47
|
+
mode: 'any' | 'all';
|
|
48
|
+
}
|
|
49
|
+
export type AnyCondition = TagCondition | GradeCondition | ThetaCondition | TemporalCondition | CountCondition | StarredCondition | DifficultyCondition | HasTermCondition | ComponentCondition;
|
|
41
50
|
export interface ConditionFilters {
|
|
42
51
|
all?: AnyCondition[];
|
|
43
52
|
any?: AnyCondition[];
|
|
@@ -72,6 +81,11 @@ export declare const Condition: {
|
|
|
72
81
|
intermediate: () => DifficultyCondition;
|
|
73
82
|
advanced: () => DifficultyCondition;
|
|
74
83
|
expert: () => DifficultyCondition;
|
|
84
|
+
hasTerm: () => HasTermCondition;
|
|
85
|
+
noTerm: () => HasTermCondition;
|
|
86
|
+
hasComponent: (component: string) => ComponentCondition;
|
|
87
|
+
hasAnyComponent: (components: string[]) => ComponentCondition;
|
|
88
|
+
hasAllComponents: (components: string[]) => ComponentCondition;
|
|
75
89
|
/**
|
|
76
90
|
* Check if starred condition is required (in 'all' section)
|
|
77
91
|
*/
|
package/dist/models/Condition.js
CHANGED
|
@@ -38,6 +38,13 @@ exports.Condition = {
|
|
|
38
38
|
intermediate: () => ({ type: 'difficulty', min: 500, max: 1500 }), // Medium
|
|
39
39
|
advanced: () => ({ type: 'difficulty', min: 1500, max: 3000 }), // Hard
|
|
40
40
|
expert: () => ({ type: 'difficulty', min: 3000 }), // Very hard
|
|
41
|
+
// HasTerm conditions
|
|
42
|
+
hasTerm: () => ({ type: 'has_term', value: true }),
|
|
43
|
+
noTerm: () => ({ type: 'has_term', value: false }),
|
|
44
|
+
// Component conditions
|
|
45
|
+
hasComponent: (component) => ({ type: 'component', components: [component], mode: 'any' }),
|
|
46
|
+
hasAnyComponent: (components) => ({ type: 'component', components, mode: 'any' }),
|
|
47
|
+
hasAllComponents: (components) => ({ type: 'component', components, mode: 'all' }),
|
|
41
48
|
// Helper methods to check if conditions are present in filters
|
|
42
49
|
/**
|
|
43
50
|
* Check if starred condition is required (in 'all' section)
|
|
@@ -129,6 +136,30 @@ exports.Condition = {
|
|
|
129
136
|
if (allGrades.length > 1) {
|
|
130
137
|
errors.push(`Cannot require multiple grades: ${allGrades.join(', ')} - an item can only have one grade`);
|
|
131
138
|
}
|
|
139
|
+
// Check for contradictory has_term conditions
|
|
140
|
+
const requiresTerm = filters.all?.some(c => c.type === 'has_term' && c.value === true);
|
|
141
|
+
const excludesTerm = filters.all?.some(c => c.type === 'has_term' && c.value === false);
|
|
142
|
+
if (requiresTerm && excludesTerm) {
|
|
143
|
+
errors.push('Cannot require both has_term and no_term');
|
|
144
|
+
}
|
|
145
|
+
// Check for implicit term requirements conflicting with no_term
|
|
146
|
+
const termDependentTypes = ['grade', 'theta', 'starred', 'temporal', 'count'];
|
|
147
|
+
const hasTermDependentInAll = filters.all?.some(c => termDependentTypes.includes(c.type));
|
|
148
|
+
const hasTermDependentInAny = filters.any?.some(c => termDependentTypes.includes(c.type));
|
|
149
|
+
if (excludesTerm && hasTermDependentInAll) {
|
|
150
|
+
const conflictingTypes = filters.all
|
|
151
|
+
?.filter(c => termDependentTypes.includes(c.type))
|
|
152
|
+
.map(c => c.type)
|
|
153
|
+
.join(', ');
|
|
154
|
+
errors.push(`Conditions [${conflictingTypes}] require a term record, but has_term is false`);
|
|
155
|
+
}
|
|
156
|
+
if (excludesTerm && hasTermDependentInAny) {
|
|
157
|
+
const conflictingTypes = filters.any
|
|
158
|
+
?.filter(c => termDependentTypes.includes(c.type))
|
|
159
|
+
.map(c => c.type)
|
|
160
|
+
.join(', ');
|
|
161
|
+
errors.push(`Conditions [${conflictingTypes}] in 'any' section require a term record, but has_term is false`);
|
|
162
|
+
}
|
|
132
163
|
return errors;
|
|
133
164
|
},
|
|
134
165
|
// Backward compatibility aliases (deprecated - use requiresStarred/allowsStarred/excludesStarred instead)
|
package/dist/models/Device.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { CompactDateTime } from "@shaxpir/shaxpir-common";
|
|
|
3
3
|
import { ShareSync } from '../repo';
|
|
4
4
|
import { Content, ContentBody, ContentId, ContentMeta } from "./Content";
|
|
5
5
|
import { ConditionFilters } from './Condition';
|
|
6
|
+
import { BuiltInDbState, DatabaseVersion } from '../util/Database';
|
|
6
7
|
export interface LastSync {
|
|
7
8
|
at_utc_time: CompactDateTime | null;
|
|
8
9
|
}
|
|
@@ -34,6 +35,7 @@ export interface DevicePayload {
|
|
|
34
35
|
conditions?: ConditionFilters;
|
|
35
36
|
download_queue?: DownloadQueueItem[];
|
|
36
37
|
upload_queue?: UploadQueueItem[];
|
|
38
|
+
builtin_db?: BuiltInDbState;
|
|
37
39
|
}
|
|
38
40
|
export interface DeviceBody extends ContentBody {
|
|
39
41
|
meta: ContentMeta;
|
|
@@ -49,6 +51,17 @@ export declare class Device extends Content {
|
|
|
49
51
|
setLastSyncAtUtcTime(value: CompactDateTime): void;
|
|
50
52
|
get chineseFont(): string;
|
|
51
53
|
setChineseFont(value: string): void;
|
|
54
|
+
getBuiltInDbState(): BuiltInDbState | undefined;
|
|
55
|
+
getBuiltInDbVersion(): DatabaseVersion;
|
|
56
|
+
getBuiltInDbPendingVersion(): DatabaseVersion;
|
|
57
|
+
getBuiltInDbDownloadStatus(): string;
|
|
58
|
+
getBuiltInDbLastCheck(): CompactDateTime;
|
|
59
|
+
getBuiltInDbLastError(): string;
|
|
60
|
+
setBuiltInDbVersion(version: DatabaseVersion | null): void;
|
|
61
|
+
setBuiltInDbPendingVersion(pending_version: DatabaseVersion | null): void;
|
|
62
|
+
setBuiltInDbDownloadStatus(download_status: 'none' | 'downloading' | 'ready' | 'failed'): void;
|
|
63
|
+
setBuiltInDbLastCheck(last_check: CompactDateTime | null): void;
|
|
64
|
+
setBuiltInDbLastError(last_error: string | null): void;
|
|
52
65
|
get downloadQueue(): DownloadQueueItem[];
|
|
53
66
|
get downloadQueueLength(): number;
|
|
54
67
|
addToDownloadQueue(item: AudioDownloadQueueItem | ImageDownloadQueueItem): void;
|
package/dist/models/Device.js
CHANGED
|
@@ -22,7 +22,14 @@ class Device extends Content_1.Content {
|
|
|
22
22
|
payload: {
|
|
23
23
|
last_sync: { at_utc_time: null },
|
|
24
24
|
download_queue: [],
|
|
25
|
-
upload_queue: []
|
|
25
|
+
upload_queue: [],
|
|
26
|
+
builtin_db: {
|
|
27
|
+
version: null,
|
|
28
|
+
pending_version: null,
|
|
29
|
+
download_status: 'none',
|
|
30
|
+
last_check: null,
|
|
31
|
+
last_error: null
|
|
32
|
+
}
|
|
26
33
|
}
|
|
27
34
|
});
|
|
28
35
|
}
|
|
@@ -59,6 +66,68 @@ class Device extends Content_1.Content {
|
|
|
59
66
|
batch.commit();
|
|
60
67
|
}
|
|
61
68
|
}
|
|
69
|
+
// Built-in Database State Methods
|
|
70
|
+
getBuiltInDbState() {
|
|
71
|
+
this.checkDisposed("Device.getBuiltInDbState");
|
|
72
|
+
return shaxpir_common_1.Struct.clone(this.payload.builtin_db);
|
|
73
|
+
}
|
|
74
|
+
// Convenience getters for built-in DB state
|
|
75
|
+
getBuiltInDbVersion() {
|
|
76
|
+
return this.payload.builtin_db.version;
|
|
77
|
+
}
|
|
78
|
+
getBuiltInDbPendingVersion() {
|
|
79
|
+
return this.payload.builtin_db.pending_version;
|
|
80
|
+
}
|
|
81
|
+
getBuiltInDbDownloadStatus() {
|
|
82
|
+
return this.payload.builtin_db.download_status;
|
|
83
|
+
}
|
|
84
|
+
getBuiltInDbLastCheck() {
|
|
85
|
+
return this.payload.builtin_db.last_check;
|
|
86
|
+
}
|
|
87
|
+
getBuiltInDbLastError() {
|
|
88
|
+
return this.payload.builtin_db.last_error;
|
|
89
|
+
}
|
|
90
|
+
// Convenience setters for built-in DB state
|
|
91
|
+
setBuiltInDbVersion(version) {
|
|
92
|
+
this.checkDisposed("Device.setBuiltInDbVersion");
|
|
93
|
+
if (this.getBuiltInDbVersion() !== version) {
|
|
94
|
+
const batch = new Operation_1.BatchOperation(this);
|
|
95
|
+
batch.setPathValue(['payload', 'builtin_db', 'version'], version);
|
|
96
|
+
batch.commit();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
setBuiltInDbPendingVersion(pending_version) {
|
|
100
|
+
this.checkDisposed("Device.setBuiltInDbPendingVersion");
|
|
101
|
+
if (this.getBuiltInDbPendingVersion() !== pending_version) {
|
|
102
|
+
const batch = new Operation_1.BatchOperation(this);
|
|
103
|
+
batch.setPathValue(['payload', 'builtin_db', 'pending_version'], pending_version);
|
|
104
|
+
batch.commit();
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
setBuiltInDbDownloadStatus(download_status) {
|
|
108
|
+
this.checkDisposed("Device.setBuiltInDbDownloadStatus");
|
|
109
|
+
if (this.getBuiltInDbDownloadStatus() !== download_status) {
|
|
110
|
+
const batch = new Operation_1.BatchOperation(this);
|
|
111
|
+
batch.setPathValue(['payload', 'builtin_db', 'download_status'], download_status);
|
|
112
|
+
batch.commit();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
setBuiltInDbLastCheck(last_check) {
|
|
116
|
+
this.checkDisposed("Device.setBuiltInDbLastCheck");
|
|
117
|
+
if (this.getBuiltInDbLastCheck() !== last_check) {
|
|
118
|
+
const batch = new Operation_1.BatchOperation(this);
|
|
119
|
+
batch.setPathValue(['payload', 'builtin_db', 'last_check'], last_check);
|
|
120
|
+
batch.commit();
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
setBuiltInDbLastError(last_error) {
|
|
124
|
+
this.checkDisposed("Device.setBuiltInDbLastError");
|
|
125
|
+
if (this.getBuiltInDbLastError() !== last_error) {
|
|
126
|
+
const batch = new Operation_1.BatchOperation(this);
|
|
127
|
+
batch.setPathValue(['payload', 'builtin_db', 'last_error'], last_error);
|
|
128
|
+
batch.commit();
|
|
129
|
+
}
|
|
130
|
+
}
|
|
62
131
|
// Download Queue Methods
|
|
63
132
|
get downloadQueue() {
|
|
64
133
|
this.checkDisposed("Device.downloadQueue");
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { CompactDateTime } from '@shaxpir/shaxpir-common';
|
|
2
|
+
declare enum DatabaseVersionBrand {
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Branded type for database version strings (YYYYMMDDx format)
|
|
6
|
+
* Ensures only validated version strings are used
|
|
7
|
+
*/
|
|
8
|
+
export type DatabaseVersion = string & DatabaseVersionBrand;
|
|
9
|
+
/**
|
|
10
|
+
* Describes a version of the built-in database available for download
|
|
11
|
+
*/
|
|
12
|
+
export interface BuiltInDbMetadata {
|
|
13
|
+
version: DatabaseVersion;
|
|
14
|
+
url: string;
|
|
15
|
+
uncompressed_size: number;
|
|
16
|
+
compressed_size: number;
|
|
17
|
+
checksum: string;
|
|
18
|
+
published_at: string;
|
|
19
|
+
min_app_version: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Tracks the state of the built-in database on a device
|
|
23
|
+
* Stored in Device model's payload.builtin_db field
|
|
24
|
+
*/
|
|
25
|
+
export interface BuiltInDbState {
|
|
26
|
+
version: DatabaseVersion | null;
|
|
27
|
+
pending_version: DatabaseVersion | null;
|
|
28
|
+
download_status: DownloadStatus;
|
|
29
|
+
last_check: CompactDateTime | null;
|
|
30
|
+
last_error: string | null;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* General-purpose download status enum
|
|
34
|
+
* Used for built-in DB, images, or any other downloadable content
|
|
35
|
+
*/
|
|
36
|
+
export type DownloadStatus = 'none' | 'downloading' | 'ready' | 'failed';
|
|
37
|
+
/**
|
|
38
|
+
* Checksum utilities for validating downloaded files
|
|
39
|
+
*/
|
|
40
|
+
export declare class Checksum {
|
|
41
|
+
/**
|
|
42
|
+
* Parse a checksum string into algorithm and hash
|
|
43
|
+
* Format: "algorithm:hash" (e.g., "sha256:abc123...")
|
|
44
|
+
*/
|
|
45
|
+
static parse(checksum: string): {
|
|
46
|
+
algorithm: string;
|
|
47
|
+
hash: string;
|
|
48
|
+
} | null;
|
|
49
|
+
/**
|
|
50
|
+
* Validate checksum format
|
|
51
|
+
*/
|
|
52
|
+
static isValid(checksum: string): boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Format a checksum (ensures consistent format)
|
|
55
|
+
*/
|
|
56
|
+
static format(algorithm: string, hash: string): string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Database version utilities
|
|
60
|
+
*/
|
|
61
|
+
export declare class DatabaseVersionModel {
|
|
62
|
+
/**
|
|
63
|
+
* Validate and create a branded DatabaseVersion from a string
|
|
64
|
+
* Throws if the version format is invalid
|
|
65
|
+
*/
|
|
66
|
+
static from(version: string): DatabaseVersion;
|
|
67
|
+
/**
|
|
68
|
+
* Validate YYYYMMDDx format (where x is a lowercase letter a-z)
|
|
69
|
+
* Examples: "20250118a", "20250118b", "20250125a"
|
|
70
|
+
*/
|
|
71
|
+
static isValid(version: string): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Compare two version strings
|
|
74
|
+
* Returns: -1 if v1 < v2, 0 if equal, 1 if v1 > v2
|
|
75
|
+
*/
|
|
76
|
+
static compare(v1: DatabaseVersion, v2: DatabaseVersion): number;
|
|
77
|
+
/**
|
|
78
|
+
* Check if v1 is newer than v2
|
|
79
|
+
*/
|
|
80
|
+
static isNewer(v1: DatabaseVersion, v2: DatabaseVersion): boolean;
|
|
81
|
+
/**
|
|
82
|
+
* Generate next version in sequence for the same day
|
|
83
|
+
* Example: "20250118a" -> "20250118b"
|
|
84
|
+
* Returns null if already at 'z'
|
|
85
|
+
*/
|
|
86
|
+
static nextInDay(version: DatabaseVersion): DatabaseVersion | null;
|
|
87
|
+
/**
|
|
88
|
+
* Extract CompactDate from version string (ignores letter suffix)
|
|
89
|
+
* Example: "20250118a" -> "20250118"
|
|
90
|
+
*/
|
|
91
|
+
static toCompactDate(version: DatabaseVersion): string;
|
|
92
|
+
/**
|
|
93
|
+
* Get the letter suffix from a version string
|
|
94
|
+
* Example: "20250118a" -> "a"
|
|
95
|
+
*/
|
|
96
|
+
static getLetterSuffix(version: DatabaseVersion): string;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Constants for built-in database management
|
|
100
|
+
*/
|
|
101
|
+
export declare const BUILTIN_DB_CONSTANTS: {
|
|
102
|
+
readonly FILE_PREFIX: "duiduidui-";
|
|
103
|
+
readonly FILE_EXTENSION: ".sqlite";
|
|
104
|
+
readonly COMPRESSED_EXTENSION: ".sqlite.gz";
|
|
105
|
+
readonly VERSION_MANIFEST_FILENAME: "manifest.json";
|
|
106
|
+
readonly VERSION_FORMAT: "YYYYMMDDx";
|
|
107
|
+
readonly VERSION_REGEX: RegExp;
|
|
108
|
+
readonly DEFAULT_HASH_ALGORITHM: "sha256";
|
|
109
|
+
readonly SUPPORTED_HASH_ALGORITHMS: readonly ["sha256", "sha512"];
|
|
110
|
+
readonly MANIFEST_CACHE_TTL: 300;
|
|
111
|
+
readonly FILE_CACHE_TTL: 31536000;
|
|
112
|
+
readonly UPDATE_CHECK_INTERVAL_HOURS: 24;
|
|
113
|
+
readonly MAX_RETRIES: 5;
|
|
114
|
+
readonly RETRY_DELAYS_MS: readonly [1000, 2000, 4000, 8000, 16000];
|
|
115
|
+
readonly MIN_FREE_SPACE_MULTIPLIER: 2;
|
|
116
|
+
};
|
|
117
|
+
/**
|
|
118
|
+
* Helper to generate filename for a database version
|
|
119
|
+
* Examples:
|
|
120
|
+
* getBuiltInDbFilename("20250118a", true) -> "duiduidui-20250118a.sqlite.gz"
|
|
121
|
+
* getBuiltInDbFilename("20250118a", false) -> "duiduidui-20250118a.sqlite"
|
|
122
|
+
*/
|
|
123
|
+
export declare function getBuiltInDbFilename(version: DatabaseVersion, compressed?: boolean): string;
|
|
124
|
+
export {};
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Database version and download management types
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.BUILTIN_DB_CONSTANTS = exports.DatabaseVersionModel = exports.Checksum = void 0;
|
|
5
|
+
exports.getBuiltInDbFilename = getBuiltInDbFilename;
|
|
6
|
+
var DatabaseVersionBrand;
|
|
7
|
+
(function (DatabaseVersionBrand) {
|
|
8
|
+
})(DatabaseVersionBrand || (DatabaseVersionBrand = {}));
|
|
9
|
+
/**
|
|
10
|
+
* Checksum utilities for validating downloaded files
|
|
11
|
+
*/
|
|
12
|
+
class Checksum {
|
|
13
|
+
/**
|
|
14
|
+
* Parse a checksum string into algorithm and hash
|
|
15
|
+
* Format: "algorithm:hash" (e.g., "sha256:abc123...")
|
|
16
|
+
*/
|
|
17
|
+
static parse(checksum) {
|
|
18
|
+
const match = checksum.match(/^(\w+):([a-f0-9]+)$/);
|
|
19
|
+
if (!match)
|
|
20
|
+
return null;
|
|
21
|
+
return { algorithm: match[1], hash: match[2] };
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Validate checksum format
|
|
25
|
+
*/
|
|
26
|
+
static isValid(checksum) {
|
|
27
|
+
return this.parse(checksum) !== null;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Format a checksum (ensures consistent format)
|
|
31
|
+
*/
|
|
32
|
+
static format(algorithm, hash) {
|
|
33
|
+
return `${algorithm}:${hash}`;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
exports.Checksum = Checksum;
|
|
37
|
+
/**
|
|
38
|
+
* Database version utilities
|
|
39
|
+
*/
|
|
40
|
+
class DatabaseVersionModel {
|
|
41
|
+
/**
|
|
42
|
+
* Validate and create a branded DatabaseVersion from a string
|
|
43
|
+
* Throws if the version format is invalid
|
|
44
|
+
*/
|
|
45
|
+
static from(version) {
|
|
46
|
+
if (!this.isValid(version)) {
|
|
47
|
+
throw new Error(`Invalid database version format: ${version}. Expected YYYYMMDDx (e.g., "20250118a")`);
|
|
48
|
+
}
|
|
49
|
+
return version;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Validate YYYYMMDDx format (where x is a lowercase letter a-z)
|
|
53
|
+
* Examples: "20250118a", "20250118b", "20250125a"
|
|
54
|
+
*/
|
|
55
|
+
static isValid(version) {
|
|
56
|
+
return /^\d{8}[a-z]$/.test(version);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Compare two version strings
|
|
60
|
+
* Returns: -1 if v1 < v2, 0 if equal, 1 if v1 > v2
|
|
61
|
+
*/
|
|
62
|
+
static compare(v1, v2) {
|
|
63
|
+
return v1.localeCompare(v2);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Check if v1 is newer than v2
|
|
67
|
+
*/
|
|
68
|
+
static isNewer(v1, v2) {
|
|
69
|
+
return this.compare(v1, v2) > 0;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Generate next version in sequence for the same day
|
|
73
|
+
* Example: "20250118a" -> "20250118b"
|
|
74
|
+
* Returns null if already at 'z'
|
|
75
|
+
*/
|
|
76
|
+
static nextInDay(version) {
|
|
77
|
+
const versionStr = version;
|
|
78
|
+
const letter = versionStr.charAt(8);
|
|
79
|
+
if (letter === 'z')
|
|
80
|
+
return null;
|
|
81
|
+
const nextLetter = String.fromCharCode(letter.charCodeAt(0) + 1);
|
|
82
|
+
return (versionStr.slice(0, 8) + nextLetter);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Extract CompactDate from version string (ignores letter suffix)
|
|
86
|
+
* Example: "20250118a" -> "20250118"
|
|
87
|
+
*/
|
|
88
|
+
static toCompactDate(version) {
|
|
89
|
+
return version.slice(0, 8);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Get the letter suffix from a version string
|
|
93
|
+
* Example: "20250118a" -> "a"
|
|
94
|
+
*/
|
|
95
|
+
static getLetterSuffix(version) {
|
|
96
|
+
return version.charAt(8);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
exports.DatabaseVersionModel = DatabaseVersionModel;
|
|
100
|
+
/**
|
|
101
|
+
* Constants for built-in database management
|
|
102
|
+
*/
|
|
103
|
+
exports.BUILTIN_DB_CONSTANTS = {
|
|
104
|
+
// File naming
|
|
105
|
+
FILE_PREFIX: 'duiduidui-',
|
|
106
|
+
FILE_EXTENSION: '.sqlite',
|
|
107
|
+
COMPRESSED_EXTENSION: '.sqlite.gz',
|
|
108
|
+
VERSION_MANIFEST_FILENAME: 'manifest.json',
|
|
109
|
+
// Version format
|
|
110
|
+
VERSION_FORMAT: 'YYYYMMDDx', // x is a lowercase letter a-z
|
|
111
|
+
VERSION_REGEX: /^\d{8}[a-z]$/,
|
|
112
|
+
// Checksums
|
|
113
|
+
DEFAULT_HASH_ALGORITHM: 'sha256',
|
|
114
|
+
SUPPORTED_HASH_ALGORITHMS: ['sha256', 'sha512'],
|
|
115
|
+
// Cache TTLs (seconds)
|
|
116
|
+
MANIFEST_CACHE_TTL: 300, // 5 minutes
|
|
117
|
+
FILE_CACHE_TTL: 31536000, // 1 year
|
|
118
|
+
// Update checking
|
|
119
|
+
UPDATE_CHECK_INTERVAL_HOURS: 24,
|
|
120
|
+
// Download retry
|
|
121
|
+
MAX_RETRIES: 5,
|
|
122
|
+
RETRY_DELAYS_MS: [1000, 2000, 4000, 8000, 16000],
|
|
123
|
+
// Storage
|
|
124
|
+
MIN_FREE_SPACE_MULTIPLIER: 2, // Need 2x compressed size free
|
|
125
|
+
};
|
|
126
|
+
/**
|
|
127
|
+
* Helper to generate filename for a database version
|
|
128
|
+
* Examples:
|
|
129
|
+
* getBuiltInDbFilename("20250118a", true) -> "duiduidui-20250118a.sqlite.gz"
|
|
130
|
+
* getBuiltInDbFilename("20250118a", false) -> "duiduidui-20250118a.sqlite"
|
|
131
|
+
*/
|
|
132
|
+
function getBuiltInDbFilename(version, compressed = true) {
|
|
133
|
+
const base = `${exports.BUILTIN_DB_CONSTANTS.FILE_PREFIX}${version}${exports.BUILTIN_DB_CONSTANTS.FILE_EXTENSION}`;
|
|
134
|
+
return compressed ? `${base}.gz` : base;
|
|
135
|
+
}
|
package/dist/util/index.d.ts
CHANGED
package/dist/util/index.js
CHANGED
|
@@ -16,5 +16,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
16
16
|
};
|
|
17
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
18
|
__exportStar(require("./AvatarUri"), exports);
|
|
19
|
+
__exportStar(require("./Database"), exports);
|
|
19
20
|
__exportStar(require("./Encryption"), exports);
|
|
20
21
|
__exportStar(require("./Logging"), exports);
|