ducjs 3.0.5 → 3.1.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/dist/ducjs_wasm.js +1 -1
- package/dist/ducjs_wasm_bg.wasm +0 -0
- package/dist/lazy-files.d.ts +9 -7
- package/dist/lazy-files.js +62 -35
- package/dist/restore/restoreDataState.js +76 -28
- package/dist/types/index.d.ts +23 -17
- package/package.json +1 -1
package/dist/ducjs_wasm.js
CHANGED
package/dist/ducjs_wasm_bg.wasm
CHANGED
|
Binary file
|
package/dist/lazy-files.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { DucExternalFile, DucExternalFiles, ExternalFileRevision } from "./types";
|
|
2
2
|
export type LazyFileMetadata = {
|
|
3
3
|
id: string;
|
|
4
4
|
mimeType: string;
|
|
@@ -26,17 +26,19 @@ export declare class LazyExternalFileStore {
|
|
|
26
26
|
getMetadata(fileId: string): LazyFileMetadata | undefined;
|
|
27
27
|
/** Get metadata for all files. */
|
|
28
28
|
getAllMetadata(): LazyFileMetadata[];
|
|
29
|
-
/** Fetch the full file
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
|
|
29
|
+
/** Fetch the full file (including data blobs for all revisions) for a specific file. */
|
|
30
|
+
getFile(fileId: string): DucExternalFile | null;
|
|
31
|
+
/** Get the active revision data for a specific file. */
|
|
32
|
+
getFileData(fileId: string): ExternalFileRevision | null;
|
|
33
|
+
/** Fetch active revision data and return a copy of the data buffer (safe for transfer). */
|
|
34
|
+
getFileDataCopy(fileId: string): ExternalFileRevision | null;
|
|
33
35
|
/** Add a file at runtime (not persisted in .duc until next serialize). */
|
|
34
|
-
addRuntimeFile(fileId: string,
|
|
36
|
+
addRuntimeFile(fileId: string, file: DucExternalFile): void;
|
|
35
37
|
/** Remove a runtime file. */
|
|
36
38
|
removeRuntimeFile(fileId: string): boolean;
|
|
37
39
|
/** Export all files eagerly as a DucExternalFiles record. */
|
|
38
40
|
toExternalFiles(): DucExternalFiles;
|
|
39
|
-
/** Merge files from another source
|
|
41
|
+
/** Merge files from another source. Adds missing files and merges new revisions into existing ones. */
|
|
40
42
|
mergeFiles(files: DucExternalFiles): void;
|
|
41
43
|
/** Release the underlying buffer to free memory. */
|
|
42
44
|
release(): void;
|
package/dist/lazy-files.js
CHANGED
|
@@ -26,11 +26,14 @@ export class LazyExternalFileStore {
|
|
|
26
26
|
getMetadata(fileId) {
|
|
27
27
|
const rt = this.runtimeFiles.get(fileId);
|
|
28
28
|
if (rt) {
|
|
29
|
+
const active = rt.revisions[rt.activeRevisionId];
|
|
30
|
+
if (!active)
|
|
31
|
+
return undefined;
|
|
29
32
|
return {
|
|
30
33
|
id: rt.id,
|
|
31
|
-
mimeType:
|
|
32
|
-
created:
|
|
33
|
-
lastRetrieved:
|
|
34
|
+
mimeType: active.mimeType,
|
|
35
|
+
created: active.created,
|
|
36
|
+
lastRetrieved: active.lastRetrieved,
|
|
34
37
|
version: rt.version,
|
|
35
38
|
};
|
|
36
39
|
}
|
|
@@ -39,24 +42,28 @@ export class LazyExternalFileStore {
|
|
|
39
42
|
/** Get metadata for all files. */
|
|
40
43
|
getAllMetadata() {
|
|
41
44
|
const result = [];
|
|
42
|
-
|
|
45
|
+
const persisted = this.getMetadataMap();
|
|
46
|
+
for (const meta of persisted.values()) {
|
|
43
47
|
result.push(meta);
|
|
44
48
|
}
|
|
45
|
-
for (const [id,
|
|
46
|
-
if (!
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
49
|
+
for (const [id, file] of this.runtimeFiles) {
|
|
50
|
+
if (!persisted.has(id)) {
|
|
51
|
+
const active = file.revisions[file.activeRevisionId];
|
|
52
|
+
if (active) {
|
|
53
|
+
result.push({
|
|
54
|
+
id: file.id,
|
|
55
|
+
mimeType: active.mimeType,
|
|
56
|
+
created: active.created,
|
|
57
|
+
lastRetrieved: active.lastRetrieved,
|
|
58
|
+
version: file.version,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
54
61
|
}
|
|
55
62
|
}
|
|
56
63
|
return result;
|
|
57
64
|
}
|
|
58
|
-
/** Fetch the full file
|
|
59
|
-
|
|
65
|
+
/** Fetch the full file (including data blobs for all revisions) for a specific file. */
|
|
66
|
+
getFile(fileId) {
|
|
60
67
|
const rt = this.runtimeFiles.get(fileId);
|
|
61
68
|
if (rt)
|
|
62
69
|
return rt;
|
|
@@ -65,15 +72,17 @@ export class LazyExternalFileStore {
|
|
|
65
72
|
const result = wasmGetExternalFile(this.buffer, fileId);
|
|
66
73
|
if (!result)
|
|
67
74
|
return null;
|
|
68
|
-
|
|
69
|
-
// Unwrap to get the actual file data.
|
|
70
|
-
const entry = result;
|
|
71
|
-
if (entry.value && typeof entry.value === "object") {
|
|
72
|
-
return entry.value;
|
|
73
|
-
}
|
|
74
|
-
return entry;
|
|
75
|
+
return result;
|
|
75
76
|
}
|
|
76
|
-
/**
|
|
77
|
+
/** Get the active revision data for a specific file. */
|
|
78
|
+
getFileData(fileId) {
|
|
79
|
+
var _a;
|
|
80
|
+
const file = this.getFile(fileId);
|
|
81
|
+
if (!file)
|
|
82
|
+
return null;
|
|
83
|
+
return (_a = file.revisions[file.activeRevisionId]) !== null && _a !== void 0 ? _a : null;
|
|
84
|
+
}
|
|
85
|
+
/** Fetch active revision data and return a copy of the data buffer (safe for transfer). */
|
|
77
86
|
getFileDataCopy(fileId) {
|
|
78
87
|
const data = this.getFileData(fileId);
|
|
79
88
|
if (!data)
|
|
@@ -81,8 +90,8 @@ export class LazyExternalFileStore {
|
|
|
81
90
|
return Object.assign(Object.assign({}, data), { data: new Uint8Array(data.data) });
|
|
82
91
|
}
|
|
83
92
|
/** Add a file at runtime (not persisted in .duc until next serialize). */
|
|
84
|
-
addRuntimeFile(fileId,
|
|
85
|
-
this.runtimeFiles.set(fileId,
|
|
93
|
+
addRuntimeFile(fileId, file) {
|
|
94
|
+
this.runtimeFiles.set(fileId, file);
|
|
86
95
|
}
|
|
87
96
|
/** Remove a runtime file. */
|
|
88
97
|
removeRuntimeFile(fileId) {
|
|
@@ -92,24 +101,42 @@ export class LazyExternalFileStore {
|
|
|
92
101
|
toExternalFiles() {
|
|
93
102
|
const result = {};
|
|
94
103
|
if (this.buffer) {
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
result[id] = data;
|
|
104
|
+
for (const [id] of this.getMetadataMap()) {
|
|
105
|
+
const file = this.getFile(id);
|
|
106
|
+
if (file) {
|
|
107
|
+
result[id] = file;
|
|
100
108
|
}
|
|
101
109
|
}
|
|
102
110
|
}
|
|
103
|
-
for (const [id,
|
|
104
|
-
result[id] =
|
|
111
|
+
for (const [id, file] of this.runtimeFiles) {
|
|
112
|
+
result[id] = file;
|
|
105
113
|
}
|
|
106
114
|
return result;
|
|
107
115
|
}
|
|
108
|
-
/** Merge files from another source
|
|
116
|
+
/** Merge files from another source. Adds missing files and merges new revisions into existing ones. */
|
|
109
117
|
mergeFiles(files) {
|
|
110
|
-
|
|
118
|
+
var _a, _b, _c;
|
|
119
|
+
for (const [id, file] of Object.entries(files)) {
|
|
111
120
|
if (!this.has(id)) {
|
|
112
|
-
this.runtimeFiles.set(id,
|
|
121
|
+
this.runtimeFiles.set(id, file);
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const existing = (_a = this.runtimeFiles.get(id)) !== null && _a !== void 0 ? _a : this.getFile(id);
|
|
125
|
+
if (!existing) {
|
|
126
|
+
this.runtimeFiles.set(id, file);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
// Merge: add any new revisions that don't exist yet, and update metadata
|
|
130
|
+
let merged = false;
|
|
131
|
+
const mergedRevisions = Object.assign({}, existing.revisions);
|
|
132
|
+
for (const [revId, rev] of Object.entries(file.revisions)) {
|
|
133
|
+
if (!mergedRevisions[revId]) {
|
|
134
|
+
mergedRevisions[revId] = rev;
|
|
135
|
+
merged = true;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
if (merged || file.updated > existing.updated) {
|
|
139
|
+
this.runtimeFiles.set(id, Object.assign(Object.assign({}, existing), { activeRevisionId: file.activeRevisionId, updated: Math.max(file.updated, existing.updated), version: Math.max((_b = file.version) !== null && _b !== void 0 ? _b : 0, (_c = existing.version) !== null && _c !== void 0 ? _c : 0), revisions: mergedRevisions }));
|
|
113
140
|
}
|
|
114
141
|
}
|
|
115
142
|
}
|
|
@@ -41,46 +41,94 @@ export const restore = (data, elementsConfig, restoreConfig = {}) => {
|
|
|
41
41
|
};
|
|
42
42
|
};
|
|
43
43
|
export const restoreFiles = (importedFiles) => {
|
|
44
|
-
var _a;
|
|
44
|
+
var _a, _b;
|
|
45
45
|
if (!importedFiles || typeof importedFiles !== "object") {
|
|
46
46
|
return {};
|
|
47
47
|
}
|
|
48
48
|
const restoredFiles = {};
|
|
49
49
|
const files = importedFiles;
|
|
50
50
|
for (const key in files) {
|
|
51
|
-
if (Object.prototype.hasOwnProperty.call(files, key))
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
if (!Object.prototype.hasOwnProperty.call(files, key))
|
|
52
|
+
continue;
|
|
53
|
+
const fileData = files[key];
|
|
54
|
+
if (!fileData || typeof fileData !== "object")
|
|
55
|
+
continue;
|
|
56
|
+
const fd = fileData;
|
|
57
|
+
// New format: DucExternalFile with revisions map
|
|
58
|
+
if (fd.revisions && typeof fd.revisions === "object" && fd.activeRevisionId) {
|
|
59
|
+
const id = isValidExternalFileId(fd.id);
|
|
60
|
+
if (!id)
|
|
54
61
|
continue;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
id,
|
|
62
|
+
const restoredRevisions = {};
|
|
63
|
+
const rawRevisions = fd.revisions;
|
|
64
|
+
for (const revKey in rawRevisions) {
|
|
65
|
+
if (!Object.prototype.hasOwnProperty.call(rawRevisions, revKey))
|
|
66
|
+
continue;
|
|
67
|
+
const rev = rawRevisions[revKey];
|
|
68
|
+
if (!rev || typeof rev !== "object")
|
|
69
|
+
continue;
|
|
70
|
+
const r = rev;
|
|
71
|
+
const revId = isValidString(r.id);
|
|
72
|
+
const mimeType = isValidString(r.mimeType);
|
|
73
|
+
const dataSource = (_a = r.data) !== null && _a !== void 0 ? _a : r.dataURL;
|
|
74
|
+
const data = isValidUint8Array(dataSource);
|
|
75
|
+
if (!revId || !mimeType || !data)
|
|
76
|
+
continue;
|
|
77
|
+
restoredRevisions[revKey] = {
|
|
78
|
+
id: revId,
|
|
79
|
+
sizeBytes: isFiniteNumber(r.sizeBytes) ? r.sizeBytes : data.byteLength,
|
|
80
|
+
checksum: isValidString(r.checksum) || undefined,
|
|
81
|
+
sourceName: isValidString(r.sourceName) || undefined,
|
|
72
82
|
mimeType,
|
|
83
|
+
message: isValidString(r.message) || undefined,
|
|
84
|
+
created: isFiniteNumber(r.created) ? r.created : Date.now(),
|
|
85
|
+
lastRetrieved: isFiniteNumber(r.lastRetrieved) ? r.lastRetrieved : undefined,
|
|
73
86
|
data,
|
|
74
|
-
created,
|
|
75
|
-
lastRetrieved: isFiniteNumber(fileData.lastRetrieved)
|
|
76
|
-
? fileData.lastRetrieved
|
|
77
|
-
: undefined,
|
|
78
|
-
version: isFiniteNumber(fileData.version)
|
|
79
|
-
? fileData.version
|
|
80
|
-
: undefined,
|
|
81
87
|
};
|
|
82
88
|
}
|
|
89
|
+
if (Object.keys(restoredRevisions).length === 0)
|
|
90
|
+
continue;
|
|
91
|
+
restoredFiles[id] = {
|
|
92
|
+
id,
|
|
93
|
+
activeRevisionId: isValidString(fd.activeRevisionId) || Object.keys(restoredRevisions)[0],
|
|
94
|
+
updated: isFiniteNumber(fd.updated) ? fd.updated : Date.now(),
|
|
95
|
+
revisions: restoredRevisions,
|
|
96
|
+
version: isFiniteNumber(fd.version) ? fd.version : undefined,
|
|
97
|
+
};
|
|
98
|
+
continue;
|
|
83
99
|
}
|
|
100
|
+
// Legacy flat format: DucExternalFileData — wrap in a single-revision DucExternalFile.
|
|
101
|
+
let legacyData = fd;
|
|
102
|
+
// Handle the nested { key, value: { ... } } structure from old Rust serde output.
|
|
103
|
+
if (fd.value && typeof fd.value === "object") {
|
|
104
|
+
legacyData = fd.value;
|
|
105
|
+
}
|
|
106
|
+
const id = isValidExternalFileId(legacyData.id);
|
|
107
|
+
const mimeType = isValidString(legacyData.mimeType);
|
|
108
|
+
const dataSource = (_b = legacyData.data) !== null && _b !== void 0 ? _b : legacyData.dataURL;
|
|
109
|
+
const data = isValidUint8Array(dataSource);
|
|
110
|
+
if (!id || !mimeType || !data)
|
|
111
|
+
continue;
|
|
112
|
+
const revId = `${id}_rev1`;
|
|
113
|
+
const created = isFiniteNumber(legacyData.created) ? legacyData.created : Date.now();
|
|
114
|
+
restoredFiles[id] = {
|
|
115
|
+
id,
|
|
116
|
+
activeRevisionId: revId,
|
|
117
|
+
updated: created,
|
|
118
|
+
version: isFiniteNumber(legacyData.version) ? legacyData.version : undefined,
|
|
119
|
+
revisions: {
|
|
120
|
+
[revId]: {
|
|
121
|
+
id: revId,
|
|
122
|
+
sizeBytes: data.byteLength,
|
|
123
|
+
mimeType,
|
|
124
|
+
created,
|
|
125
|
+
lastRetrieved: isFiniteNumber(legacyData.lastRetrieved)
|
|
126
|
+
? legacyData.lastRetrieved
|
|
127
|
+
: undefined,
|
|
128
|
+
data,
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
};
|
|
84
132
|
}
|
|
85
133
|
return restoredFiles;
|
|
86
134
|
};
|
package/dist/types/index.d.ts
CHANGED
|
@@ -79,30 +79,36 @@ export type DucUcs = {
|
|
|
79
79
|
angle: Radian;
|
|
80
80
|
};
|
|
81
81
|
export type Scope = SupportedMeasures;
|
|
82
|
-
export type
|
|
82
|
+
export type ExternalFileRevision = {
|
|
83
|
+
id: string;
|
|
84
|
+
sizeBytes: number;
|
|
85
|
+
/** Content hash for integrity checks and optional deduplication. */
|
|
86
|
+
checksum?: string;
|
|
87
|
+
/** Original upload filename shown to the user. */
|
|
88
|
+
sourceName?: string;
|
|
83
89
|
mimeType: string;
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Epoch timestamp in milliseconds
|
|
88
|
-
*/
|
|
90
|
+
/** Optional note describing what changed in this revision. */
|
|
91
|
+
message?: string;
|
|
92
|
+
/** Epoch timestamp in milliseconds when this revision was created. */
|
|
89
93
|
created: number;
|
|
90
94
|
/**
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
* files from storage.
|
|
94
|
-
*
|
|
95
|
-
* Epoch timestamp in milliseconds.
|
|
95
|
+
* Epoch timestamp in milliseconds when this revision was last loaded onto
|
|
96
|
+
* the scene. Used to determine whether to delete unused files from storage.
|
|
96
97
|
*/
|
|
97
98
|
lastRetrieved?: number;
|
|
98
|
-
/**
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
99
|
+
/** The actual file content bytes. */
|
|
100
|
+
data: Uint8Array;
|
|
101
|
+
};
|
|
102
|
+
export type DucExternalFile = {
|
|
103
|
+
id: ExternalFileId;
|
|
104
|
+
activeRevisionId: string;
|
|
105
|
+
/** Epoch ms when the logical file was last mutated (revision added or active changed). */
|
|
106
|
+
updated: number;
|
|
107
|
+
/** All revisions of this file, keyed by their id. */
|
|
108
|
+
revisions: Record<string, ExternalFileRevision>;
|
|
102
109
|
version?: number;
|
|
103
110
|
};
|
|
104
|
-
export type
|
|
105
|
-
export type DucExternalFiles = Record<DucElement["id"], DucExternalFileData>;
|
|
111
|
+
export type DucExternalFiles = Record<ExternalFileId, DucExternalFile>;
|
|
106
112
|
export type SuggestedBinding = NonDeleted<DucBindableElement> | SuggestedPointBinding;
|
|
107
113
|
export type SuggestedPointBinding = [
|
|
108
114
|
NonDeleted<DucLinearElement>,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ducjs",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"description": "The duc 2D CAD file format is a cornerstone of our advanced design system, conceived to cater to professionals seeking precision and efficiency in their design work.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|