brep-io-kernel 1.0.34 → 1.0.35
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-kernel/brep-kernel.js +8357 -8091
- package/package.json +1 -1
- package/src/UI/fileManagerWidget.js +497 -77
- package/src/exporters/threeMF.js +170 -12
- package/src/features/assemblyComponent/AssemblyComponentFeature.js +322 -25
- package/src/githubStorage.js +101 -44
- package/src/services/componentLibrary.js +10 -4
package/src/githubStorage.js
CHANGED
|
@@ -3,6 +3,19 @@ const GH = {
|
|
|
3
3
|
apiVersion: '2022-11-28',
|
|
4
4
|
};
|
|
5
5
|
|
|
6
|
+
const _WRITE_CHAINS = new Map();
|
|
7
|
+
|
|
8
|
+
function _enqueueGithubWrite(key, op) {
|
|
9
|
+
const prev = _WRITE_CHAINS.get(key) || Promise.resolve();
|
|
10
|
+
const next = prev.then(op);
|
|
11
|
+
let safe;
|
|
12
|
+
safe = next.catch(() => {}).finally(() => {
|
|
13
|
+
if (_WRITE_CHAINS.get(key) === safe) _WRITE_CHAINS.delete(key);
|
|
14
|
+
});
|
|
15
|
+
_WRITE_CHAINS.set(key, safe);
|
|
16
|
+
return next;
|
|
17
|
+
}
|
|
18
|
+
|
|
6
19
|
const STORAGE_ROOT = 'brep-storage';
|
|
7
20
|
const SETTINGS_DIR = 'settings';
|
|
8
21
|
const DATA_DIR = '__BREP_DATA__';
|
|
@@ -227,30 +240,51 @@ export async function readGithubFileBase64({ token, repoFull, branch, path }) {
|
|
|
227
240
|
return null;
|
|
228
241
|
}
|
|
229
242
|
|
|
230
|
-
export async function writeGithubFileBase64({ token, repoFull, branch, path, base64, message }) {
|
|
243
|
+
export async function writeGithubFileBase64({ token, repoFull, branch, path, base64, message, retryOn409 = 2 }) {
|
|
231
244
|
const t = String(token || '').trim();
|
|
232
245
|
if (!t) throw new Error('Missing GitHub token');
|
|
233
246
|
const { owner, repo } = parseRepo(repoFull);
|
|
234
|
-
let sha = null;
|
|
235
|
-
try {
|
|
236
|
-
const meta = await readGithubFileMeta({ token: t, repoFull, branch, path });
|
|
237
|
-
sha = meta?.sha || null;
|
|
238
|
-
} catch (e) {
|
|
239
|
-
if (!e || e.status !== 404) throw e;
|
|
240
|
-
}
|
|
241
|
-
const body = {
|
|
242
|
-
message: message || `BREP storage update: ${path}`,
|
|
243
|
-
content: String(base64 || '').replace(/\s+/g, ''),
|
|
244
|
-
};
|
|
245
|
-
if (branch) body.branch = branch;
|
|
246
|
-
if (sha) body.sha = sha;
|
|
247
247
|
const url = `${GH.apiBase}/repos/${owner}/${repo}/contents/${encodePath(path)}`;
|
|
248
|
-
const
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
248
|
+
const content = String(base64 || '').replace(/\s+/g, '');
|
|
249
|
+
const maxRetry = Math.max(0, Number.isFinite(retryOn409) ? retryOn409 : 0);
|
|
250
|
+
const writeKey = `${repoFull}@${branch || ''}:${path}`;
|
|
251
|
+
|
|
252
|
+
return _enqueueGithubWrite(writeKey, async () => {
|
|
253
|
+
let sha = null;
|
|
254
|
+
const refreshSha = async () => {
|
|
255
|
+
try {
|
|
256
|
+
const meta = await readGithubFileMeta({ token: t, repoFull, branch, path });
|
|
257
|
+
sha = meta?.sha || null;
|
|
258
|
+
} catch (e) {
|
|
259
|
+
if (!e || e.status !== 404) throw e;
|
|
260
|
+
sha = null;
|
|
261
|
+
}
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
for (let attempt = 0; attempt <= maxRetry; attempt++) {
|
|
265
|
+
await refreshSha();
|
|
266
|
+
const body = {
|
|
267
|
+
message: message || `BREP storage update: ${path}`,
|
|
268
|
+
content,
|
|
269
|
+
};
|
|
270
|
+
if (branch) body.branch = branch;
|
|
271
|
+
if (sha) body.sha = sha;
|
|
272
|
+
try {
|
|
273
|
+
const res = await ghFetch(url, t, {
|
|
274
|
+
method: 'PUT',
|
|
275
|
+
headers: { 'Content-Type': 'application/json' },
|
|
276
|
+
body: JSON.stringify(body),
|
|
277
|
+
});
|
|
278
|
+
return res;
|
|
279
|
+
} catch (e) {
|
|
280
|
+
if (e && e.status === 409 && attempt < maxRetry) {
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
throw e;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return null;
|
|
252
287
|
});
|
|
253
|
-
return res;
|
|
254
288
|
}
|
|
255
289
|
|
|
256
290
|
export async function deleteGithubFile({ token, repoFull, branch, path, message }) {
|
|
@@ -285,7 +319,10 @@ async function readGithubFileMeta({ token, repoFull, branch, path }) {
|
|
|
285
319
|
const { owner, repo } = parseRepo(repoFull);
|
|
286
320
|
const url = new URL(`${GH.apiBase}/repos/${owner}/${repo}/contents/${encodePath(path)}`);
|
|
287
321
|
if (branch) url.searchParams.set('ref', branch);
|
|
288
|
-
|
|
322
|
+
url.searchParams.set('_ts', Date.now().toString());
|
|
323
|
+
return await ghFetch(url.toString(), t, {
|
|
324
|
+
cache: 'no-store',
|
|
325
|
+
});
|
|
289
326
|
}
|
|
290
327
|
|
|
291
328
|
export class GithubStorage {
|
|
@@ -400,38 +437,58 @@ export class GithubStorage {
|
|
|
400
437
|
const { owner, repo } = this._repo;
|
|
401
438
|
const url = new URL(`${GH.apiBase}/repos/${owner}/${repo}/contents/${encodePath(path)}`);
|
|
402
439
|
url.searchParams.set('ref', this._branch || 'main');
|
|
403
|
-
|
|
440
|
+
url.searchParams.set('_ts', Date.now().toString());
|
|
441
|
+
return await ghFetch(url.toString(), this._token, {
|
|
442
|
+
cache: 'no-store',
|
|
443
|
+
});
|
|
404
444
|
}
|
|
405
445
|
|
|
406
446
|
async _writeKeyToRepo(key, value) {
|
|
407
447
|
if (!this._token || !this._repo) return;
|
|
408
448
|
const { owner, repo } = this._repo;
|
|
409
449
|
const path = keyToPath(key, this._rootDir);
|
|
410
|
-
let sha = this._shaByKey.get(key) || null;
|
|
411
|
-
if (!sha) {
|
|
412
|
-
try {
|
|
413
|
-
const meta = await this._getFileMeta(path);
|
|
414
|
-
sha = meta?.sha || null;
|
|
415
|
-
if (sha) this._shaByKey.set(key, sha);
|
|
416
|
-
} catch (e) {
|
|
417
|
-
if (!e || e.status !== 404) throw e;
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
const body = {
|
|
421
|
-
message: `BREP storage update: ${key}`,
|
|
422
|
-
content: encodeBase64(value),
|
|
423
|
-
branch: this._branch || 'main',
|
|
424
|
-
};
|
|
425
|
-
if (sha) body.sha = sha;
|
|
426
450
|
const url = `${GH.apiBase}/repos/${owner}/${repo}/contents/${encodePath(path)}`;
|
|
427
|
-
const
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
451
|
+
const content = encodeBase64(value);
|
|
452
|
+
const writeKey = `${this._repoFull || ''}@${this._branch || ''}:${path}`;
|
|
453
|
+
return _enqueueGithubWrite(writeKey, async () => {
|
|
454
|
+
let sha = null;
|
|
455
|
+
const refreshSha = async () => {
|
|
456
|
+
try {
|
|
457
|
+
const meta = await this._getFileMeta(path);
|
|
458
|
+
sha = meta?.sha || null;
|
|
459
|
+
if (sha) this._shaByKey.set(key, sha);
|
|
460
|
+
} catch (e) {
|
|
461
|
+
if (!e || e.status !== 404) throw e;
|
|
462
|
+
sha = null;
|
|
463
|
+
}
|
|
464
|
+
};
|
|
465
|
+
|
|
466
|
+
for (let attempt = 0; attempt <= 1; attempt++) {
|
|
467
|
+
await refreshSha();
|
|
468
|
+
const body = {
|
|
469
|
+
message: `BREP storage update: ${key}`,
|
|
470
|
+
content,
|
|
471
|
+
branch: this._branch || 'main',
|
|
472
|
+
};
|
|
473
|
+
if (sha) body.sha = sha;
|
|
474
|
+
try {
|
|
475
|
+
const res = await ghFetch(url, this._token, {
|
|
476
|
+
method: 'PUT',
|
|
477
|
+
headers: { 'Content-Type': 'application/json' },
|
|
478
|
+
body: JSON.stringify(body),
|
|
479
|
+
});
|
|
480
|
+
if (res && res.content && res.content.sha) {
|
|
481
|
+
this._shaByKey.set(key, res.content.sha);
|
|
482
|
+
}
|
|
483
|
+
return;
|
|
484
|
+
} catch (e) {
|
|
485
|
+
if (e && e.status === 409 && attempt < 1) {
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
throw e;
|
|
489
|
+
}
|
|
490
|
+
}
|
|
431
491
|
});
|
|
432
|
-
if (res && res.content && res.content.sha) {
|
|
433
|
-
this._shaByKey.set(key, res.content.sha);
|
|
434
|
-
}
|
|
435
492
|
}
|
|
436
493
|
|
|
437
494
|
async _removeKeyFromRepo(key) {
|
|
@@ -96,7 +96,7 @@ async function listGithubComponentRecords() {
|
|
|
96
96
|
return out;
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
async function getGithubComponentRecord(name) {
|
|
99
|
+
async function getGithubComponentRecord(name, options = {}) {
|
|
100
100
|
const cfg = getGithubStorageConfig();
|
|
101
101
|
if (!cfg?.token || !cfg?.repoFull) return null;
|
|
102
102
|
const { dataPath, metaPath } = getGithubModelPaths(name);
|
|
@@ -110,7 +110,11 @@ async function getGithubComponentRecord(name) {
|
|
|
110
110
|
branch: cfg.branch,
|
|
111
111
|
path: dataPath,
|
|
112
112
|
});
|
|
113
|
-
} catch {
|
|
113
|
+
} catch (err) {
|
|
114
|
+
if (err && err.status === 404) return null;
|
|
115
|
+
if (options?.throwOnError) throw err;
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
114
118
|
if (!data3mf) return null;
|
|
115
119
|
try {
|
|
116
120
|
const metaB64 = await readGithubFileBase64({
|
|
@@ -147,6 +151,7 @@ async function setGithubComponentRecord(name, dataObj) {
|
|
|
147
151
|
path: dataPath,
|
|
148
152
|
base64: data3mf,
|
|
149
153
|
message: `BREP model update: ${name}`,
|
|
154
|
+
retryOn409: 3,
|
|
150
155
|
});
|
|
151
156
|
const meta = {
|
|
152
157
|
savedAt: dataObj?.savedAt || new Date().toISOString(),
|
|
@@ -160,6 +165,7 @@ async function setGithubComponentRecord(name, dataObj) {
|
|
|
160
165
|
path: metaPath,
|
|
161
166
|
base64: metaB64,
|
|
162
167
|
message: `BREP model meta: ${name}`,
|
|
168
|
+
retryOn409: 3,
|
|
163
169
|
});
|
|
164
170
|
}
|
|
165
171
|
|
|
@@ -233,9 +239,9 @@ export async function listComponentRecords() {
|
|
|
233
239
|
return items;
|
|
234
240
|
}
|
|
235
241
|
|
|
236
|
-
export async function getComponentRecord(name) {
|
|
242
|
+
export async function getComponentRecord(name, options = {}) {
|
|
237
243
|
if (LS?.isGithub?.()) {
|
|
238
|
-
return await getGithubComponentRecord(name);
|
|
244
|
+
return await getGithubComponentRecord(name, options);
|
|
239
245
|
}
|
|
240
246
|
if (!name) return null;
|
|
241
247
|
const key = MODEL_STORAGE_PREFIX + encodeURIComponent(String(name));
|