@rohal12/spindle 0.35.0 → 0.36.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/pkg/format.js +1 -1
- package/package.json +1 -1
- package/src/markup/tokenizer.ts +19 -66
- package/src/saves/save-manager.ts +128 -44
- package/src/saves/storage.ts +567 -0
- package/src/saves/types.ts +47 -0
- package/src/store.ts +54 -0
- package/src/story-api.ts +85 -2
- package/src/saves/idb.ts +0 -165
package/package.json
CHANGED
package/src/markup/tokenizer.ts
CHANGED
|
@@ -53,68 +53,8 @@ export type Token =
|
|
|
53
53
|
| VariableToken
|
|
54
54
|
| HtmlToken;
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
'article',
|
|
59
|
-
'aside',
|
|
60
|
-
'b',
|
|
61
|
-
'blockquote',
|
|
62
|
-
'br',
|
|
63
|
-
'caption',
|
|
64
|
-
'code',
|
|
65
|
-
'col',
|
|
66
|
-
'colgroup',
|
|
67
|
-
'dd',
|
|
68
|
-
'del',
|
|
69
|
-
'details',
|
|
70
|
-
'dfn',
|
|
71
|
-
'div',
|
|
72
|
-
'dl',
|
|
73
|
-
'dt',
|
|
74
|
-
'em',
|
|
75
|
-
'figcaption',
|
|
76
|
-
'figure',
|
|
77
|
-
'footer',
|
|
78
|
-
'h1',
|
|
79
|
-
'h2',
|
|
80
|
-
'h3',
|
|
81
|
-
'h4',
|
|
82
|
-
'h5',
|
|
83
|
-
'h6',
|
|
84
|
-
'header',
|
|
85
|
-
'hr',
|
|
86
|
-
'i',
|
|
87
|
-
'img',
|
|
88
|
-
'ins',
|
|
89
|
-
'kbd',
|
|
90
|
-
'li',
|
|
91
|
-
'main',
|
|
92
|
-
'mark',
|
|
93
|
-
'nav',
|
|
94
|
-
'ol',
|
|
95
|
-
'p',
|
|
96
|
-
'pre',
|
|
97
|
-
'q',
|
|
98
|
-
's',
|
|
99
|
-
'samp',
|
|
100
|
-
'section',
|
|
101
|
-
'small',
|
|
102
|
-
'span',
|
|
103
|
-
'strong',
|
|
104
|
-
'sub',
|
|
105
|
-
'summary',
|
|
106
|
-
'sup',
|
|
107
|
-
'table',
|
|
108
|
-
'tbody',
|
|
109
|
-
'td',
|
|
110
|
-
'tfoot',
|
|
111
|
-
'th',
|
|
112
|
-
'thead',
|
|
113
|
-
'tr',
|
|
114
|
-
'u',
|
|
115
|
-
'ul',
|
|
116
|
-
'wbr',
|
|
117
|
-
]);
|
|
56
|
+
/** Tag name must start with a letter (covers standard and custom elements). */
|
|
57
|
+
const VALID_TAG_START = /[a-zA-Z]/;
|
|
118
58
|
|
|
119
59
|
const HTML_VOID_TAGS = new Set(['br', 'col', 'hr', 'img', 'wbr']);
|
|
120
60
|
|
|
@@ -661,13 +601,26 @@ export function tokenize(input: string): Token[] {
|
|
|
661
601
|
const isClose = input[j] === '/';
|
|
662
602
|
if (isClose) j++;
|
|
663
603
|
|
|
664
|
-
// Read tag name
|
|
604
|
+
// Read tag name (letters, digits, hyphens for custom elements)
|
|
665
605
|
const tagStart = j;
|
|
666
|
-
while (j < input.length && /[a-zA-Z0-9]/.test(input[j]!)) j++;
|
|
606
|
+
while (j < input.length && /[a-zA-Z0-9-]/.test(input[j]!)) j++;
|
|
667
607
|
const tag = input.slice(tagStart, j).toLowerCase();
|
|
668
608
|
|
|
669
|
-
//
|
|
670
|
-
if (tag &&
|
|
609
|
+
// Valid tag name must start with a letter
|
|
610
|
+
if (tag && VALID_TAG_START.test(tag[0]!)) {
|
|
611
|
+
// SVG is its own world — treat <svg>…</svg> as opaque text
|
|
612
|
+
// so the markdown pipeline handles it with proper namespace.
|
|
613
|
+
if (tag === 'svg' && !isClose) {
|
|
614
|
+
const closeTag = '</svg>';
|
|
615
|
+
const closeIdx = input.indexOf(closeTag, j);
|
|
616
|
+
if (closeIdx !== -1) {
|
|
617
|
+
const end = closeIdx + closeTag.length;
|
|
618
|
+
// Leave the entire <svg>…</svg> block as text
|
|
619
|
+
i = end;
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
671
624
|
if (isClose) {
|
|
672
625
|
// Closing tag: skip whitespace, expect >
|
|
673
626
|
while (j < input.length && /\s/.test(input[j]!)) j++;
|
|
@@ -5,18 +5,10 @@ import type {
|
|
|
5
5
|
SaveInfo,
|
|
6
6
|
PlaythroughRecord,
|
|
7
7
|
SaveExport,
|
|
8
|
+
StorageInfo,
|
|
8
9
|
} from './types';
|
|
9
10
|
import { isSavePayload } from './types';
|
|
10
|
-
import {
|
|
11
|
-
putSave,
|
|
12
|
-
getSave,
|
|
13
|
-
deleteSave as idbDeleteSave,
|
|
14
|
-
getSavesByIfid,
|
|
15
|
-
putPlaythrough,
|
|
16
|
-
getPlaythroughsByIfid,
|
|
17
|
-
getMeta,
|
|
18
|
-
setMeta,
|
|
19
|
-
} from './idb';
|
|
11
|
+
import { getBackend, resetBackend } from './storage';
|
|
20
12
|
import { deepClone, serialize, deserialize } from '../class-registry';
|
|
21
13
|
|
|
22
14
|
type TitleGenerator = (payload: SavePayload) => string;
|
|
@@ -79,7 +71,8 @@ export async function initSaveSystem(): Promise<void> {
|
|
|
79
71
|
// --- Playthroughs ---
|
|
80
72
|
|
|
81
73
|
export async function startNewPlaythrough(ifid: string): Promise<string> {
|
|
82
|
-
const
|
|
74
|
+
const backend = await getBackend();
|
|
75
|
+
const existing = await backend.getPlaythroughsByIfid(ifid);
|
|
83
76
|
const num = existing.length + 1;
|
|
84
77
|
|
|
85
78
|
const id = crypto.randomUUID();
|
|
@@ -90,15 +83,15 @@ export async function startNewPlaythrough(ifid: string): Promise<string> {
|
|
|
90
83
|
label: `Playthrough ${num}`,
|
|
91
84
|
};
|
|
92
85
|
|
|
93
|
-
await putPlaythrough(record);
|
|
94
|
-
await setMeta(`currentPlaythroughId.${ifid}`, id);
|
|
86
|
+
await backend.putPlaythrough(record);
|
|
87
|
+
await backend.setMeta(`currentPlaythroughId.${ifid}`, id);
|
|
95
88
|
return id;
|
|
96
89
|
}
|
|
97
90
|
|
|
98
91
|
export async function getCurrentPlaythroughId(
|
|
99
92
|
ifid: string,
|
|
100
93
|
): Promise<string | undefined> {
|
|
101
|
-
return getMeta<string>(`currentPlaythroughId.${ifid}`);
|
|
94
|
+
return (await getBackend()).getMeta<string>(`currentPlaythroughId.${ifid}`);
|
|
102
95
|
}
|
|
103
96
|
|
|
104
97
|
// --- Save CRUD ---
|
|
@@ -128,7 +121,8 @@ export async function createSave(
|
|
|
128
121
|
variables: serialize(m.variables),
|
|
129
122
|
}));
|
|
130
123
|
const record: SaveRecord = { meta, payload: serializedPayload };
|
|
131
|
-
|
|
124
|
+
record.meta.estimatedBytes = JSON.stringify(serializedPayload).length;
|
|
125
|
+
await (await getBackend()).putSave(record);
|
|
132
126
|
return record;
|
|
133
127
|
}
|
|
134
128
|
|
|
@@ -137,7 +131,8 @@ export async function overwriteSave(
|
|
|
137
131
|
payload: SavePayload,
|
|
138
132
|
custom?: Record<string, unknown>,
|
|
139
133
|
): Promise<SaveRecord | undefined> {
|
|
140
|
-
const
|
|
134
|
+
const backend = await getBackend();
|
|
135
|
+
const existing = await backend.getSave(saveId);
|
|
141
136
|
if (!existing) return undefined;
|
|
142
137
|
|
|
143
138
|
const serializedPayload = deepClone(payload);
|
|
@@ -157,14 +152,15 @@ export async function overwriteSave(
|
|
|
157
152
|
},
|
|
158
153
|
payload: serializedPayload,
|
|
159
154
|
};
|
|
160
|
-
|
|
155
|
+
updated.meta.estimatedBytes = JSON.stringify(serializedPayload).length;
|
|
156
|
+
await backend.putSave(updated);
|
|
161
157
|
return updated;
|
|
162
158
|
}
|
|
163
159
|
|
|
164
160
|
export async function loadSave(
|
|
165
161
|
saveId: string,
|
|
166
162
|
): Promise<SavePayload | undefined> {
|
|
167
|
-
const record = await getSave(saveId);
|
|
163
|
+
const record = await (await getBackend()).getSave(saveId);
|
|
168
164
|
if (!record) return undefined;
|
|
169
165
|
const payload = record.payload;
|
|
170
166
|
payload.variables = deserialize(payload.variables);
|
|
@@ -176,14 +172,15 @@ export async function loadSave(
|
|
|
176
172
|
}
|
|
177
173
|
|
|
178
174
|
export async function deleteSaveById(saveId: string): Promise<void> {
|
|
179
|
-
await
|
|
175
|
+
await (await getBackend()).deleteSave(saveId);
|
|
180
176
|
}
|
|
181
177
|
|
|
182
178
|
export async function renameSave(
|
|
183
179
|
saveId: string,
|
|
184
180
|
newTitle: string,
|
|
185
181
|
): Promise<void> {
|
|
186
|
-
const
|
|
182
|
+
const backend = await getBackend();
|
|
183
|
+
const record = await backend.getSave(saveId);
|
|
187
184
|
if (!record) return;
|
|
188
185
|
const updated: SaveRecord = {
|
|
189
186
|
...record,
|
|
@@ -193,7 +190,7 @@ export async function renameSave(
|
|
|
193
190
|
updatedAt: new Date().toISOString(),
|
|
194
191
|
},
|
|
195
192
|
};
|
|
196
|
-
await putSave(updated);
|
|
193
|
+
await backend.putSave(updated);
|
|
197
194
|
}
|
|
198
195
|
|
|
199
196
|
// --- Grouped Retrieval ---
|
|
@@ -206,9 +203,10 @@ export interface PlaythroughGroup {
|
|
|
206
203
|
export async function getSavesGrouped(
|
|
207
204
|
ifid: string,
|
|
208
205
|
): Promise<PlaythroughGroup[]> {
|
|
206
|
+
const backend = await getBackend();
|
|
209
207
|
const [allSaves, allPlaythroughs] = await Promise.all([
|
|
210
|
-
getSavesByIfid(ifid),
|
|
211
|
-
getPlaythroughsByIfid(ifid),
|
|
208
|
+
backend.getSavesByIfid(ifid),
|
|
209
|
+
backend.getPlaythroughsByIfid(ifid),
|
|
212
210
|
]);
|
|
213
211
|
|
|
214
212
|
const ptMap = new Map<string, PlaythroughRecord>();
|
|
@@ -282,8 +280,9 @@ export async function quickSave(
|
|
|
282
280
|
slot?: string,
|
|
283
281
|
custom?: Record<string, unknown>,
|
|
284
282
|
): Promise<SaveRecord> {
|
|
283
|
+
const backend = await getBackend();
|
|
285
284
|
const metaKey = slotMetaKey(ifid, slot);
|
|
286
|
-
const existingId = await getMeta<string>(metaKey);
|
|
285
|
+
const existingId = await backend.getMeta<string>(metaKey);
|
|
287
286
|
|
|
288
287
|
if (existingId) {
|
|
289
288
|
const updated = await overwriteSave(existingId, payload, custom);
|
|
@@ -296,14 +295,14 @@ export async function quickSave(
|
|
|
296
295
|
...(slot != null ? { slot } : {}),
|
|
297
296
|
...custom,
|
|
298
297
|
});
|
|
299
|
-
await setMeta(metaKey, record.meta.id);
|
|
298
|
+
await backend.setMeta(metaKey, record.meta.id);
|
|
300
299
|
|
|
301
300
|
// Track named slots in the index
|
|
302
301
|
if (slot != null) {
|
|
303
302
|
const indexKey = `${SLOT_INDEX_KEY_PREFIX}${ifid}`;
|
|
304
|
-
const existing = (await getMeta<string[]>(indexKey)) ?? [];
|
|
303
|
+
const existing = (await backend.getMeta<string[]>(indexKey)) ?? [];
|
|
305
304
|
if (!existing.includes(slot)) {
|
|
306
|
-
await setMeta(indexKey, [...existing, slot]);
|
|
305
|
+
await backend.setMeta(indexKey, [...existing, slot]);
|
|
307
306
|
}
|
|
308
307
|
}
|
|
309
308
|
|
|
@@ -314,10 +313,11 @@ export async function hasQuickSave(
|
|
|
314
313
|
ifid: string,
|
|
315
314
|
slot?: string,
|
|
316
315
|
): Promise<boolean> {
|
|
316
|
+
const backend = await getBackend();
|
|
317
317
|
const metaKey = slotMetaKey(ifid, slot);
|
|
318
|
-
const existingId = await getMeta<string>(metaKey);
|
|
318
|
+
const existingId = await backend.getMeta<string>(metaKey);
|
|
319
319
|
if (!existingId) return false;
|
|
320
|
-
const record = await getSave(existingId);
|
|
320
|
+
const record = await backend.getSave(existingId);
|
|
321
321
|
return record !== undefined;
|
|
322
322
|
}
|
|
323
323
|
|
|
@@ -326,13 +326,13 @@ export async function loadQuickSave(
|
|
|
326
326
|
slot?: string,
|
|
327
327
|
): Promise<SavePayload | undefined> {
|
|
328
328
|
const metaKey = slotMetaKey(ifid, slot);
|
|
329
|
-
const existingId = await getMeta<string>(metaKey);
|
|
329
|
+
const existingId = await (await getBackend()).getMeta<string>(metaKey);
|
|
330
330
|
if (!existingId) return undefined;
|
|
331
331
|
return loadSave(existingId);
|
|
332
332
|
}
|
|
333
333
|
|
|
334
334
|
/**
|
|
335
|
-
* Check
|
|
335
|
+
* Check storage for all known saves and return a map of slot keys to true.
|
|
336
336
|
* The default (autosave) slot uses empty string as key.
|
|
337
337
|
*/
|
|
338
338
|
export async function populateKnownSaves(
|
|
@@ -347,7 +347,7 @@ export async function populateKnownSaves(
|
|
|
347
347
|
|
|
348
348
|
// Check named slots from the index
|
|
349
349
|
const indexKey = `${SLOT_INDEX_KEY_PREFIX}${ifid}`;
|
|
350
|
-
const slots = (await getMeta<string[]>(indexKey)) ?? [];
|
|
350
|
+
const slots = (await (await getBackend()).getMeta<string[]>(indexKey)) ?? [];
|
|
351
351
|
for (const slot of slots) {
|
|
352
352
|
if (await hasQuickSave(ifid, slot)) {
|
|
353
353
|
result[slot] = true;
|
|
@@ -365,10 +365,11 @@ export async function getSlotSaveInfo(
|
|
|
365
365
|
ifid: string,
|
|
366
366
|
slot?: string,
|
|
367
367
|
): Promise<SaveInfo | null> {
|
|
368
|
+
const backend = await getBackend();
|
|
368
369
|
const metaKey = slotMetaKey(ifid, slot);
|
|
369
|
-
const existingId = await getMeta<string>(metaKey);
|
|
370
|
+
const existingId = await backend.getMeta<string>(metaKey);
|
|
370
371
|
if (!existingId) return null;
|
|
371
|
-
const record = await getSave(existingId);
|
|
372
|
+
const record = await backend.getSave(existingId);
|
|
372
373
|
if (!record) return null;
|
|
373
374
|
return {
|
|
374
375
|
slot: slot ?? '',
|
|
@@ -392,7 +393,7 @@ export async function listSlotSaves(ifid: string): Promise<SaveInfo[]> {
|
|
|
392
393
|
|
|
393
394
|
// Check named slots from the index
|
|
394
395
|
const indexKey = `${SLOT_INDEX_KEY_PREFIX}${ifid}`;
|
|
395
|
-
const slots = (await getMeta<string[]>(indexKey)) ?? [];
|
|
396
|
+
const slots = (await (await getBackend()).getMeta<string[]>(indexKey)) ?? [];
|
|
396
397
|
for (const slot of slots) {
|
|
397
398
|
const info = await getSlotSaveInfo(ifid, slot);
|
|
398
399
|
if (info) result.push(info);
|
|
@@ -408,19 +409,20 @@ export async function deleteSlotSave(
|
|
|
408
409
|
ifid: string,
|
|
409
410
|
slot?: string,
|
|
410
411
|
): Promise<void> {
|
|
412
|
+
const backend = await getBackend();
|
|
411
413
|
const metaKey = slotMetaKey(ifid, slot);
|
|
412
|
-
const existingId = await getMeta<string>(metaKey);
|
|
414
|
+
const existingId = await backend.getMeta<string>(metaKey);
|
|
413
415
|
if (!existingId) return;
|
|
414
416
|
|
|
415
|
-
await
|
|
416
|
-
await
|
|
417
|
+
await backend.deleteSave(existingId);
|
|
418
|
+
await backend.deleteMeta(metaKey);
|
|
417
419
|
|
|
418
420
|
// Remove from slot index if named
|
|
419
421
|
if (slot != null) {
|
|
420
422
|
const indexKey = `${SLOT_INDEX_KEY_PREFIX}${ifid}`;
|
|
421
|
-
const existing = (await getMeta<string[]>(indexKey)) ?? [];
|
|
423
|
+
const existing = (await backend.getMeta<string[]>(indexKey)) ?? [];
|
|
422
424
|
const updated = existing.filter((s) => s !== slot);
|
|
423
|
-
await setMeta(indexKey, updated);
|
|
425
|
+
await backend.setMeta(indexKey, updated);
|
|
424
426
|
}
|
|
425
427
|
}
|
|
426
428
|
|
|
@@ -474,7 +476,7 @@ export function clearSession(ifid: string): void {
|
|
|
474
476
|
export async function exportSave(
|
|
475
477
|
saveId: string,
|
|
476
478
|
): Promise<SaveExport | undefined> {
|
|
477
|
-
const record = await getSave(saveId);
|
|
479
|
+
const record = await (await getBackend()).getSave(saveId);
|
|
478
480
|
if (!record) return undefined;
|
|
479
481
|
|
|
480
482
|
return {
|
|
@@ -498,13 +500,15 @@ export async function importSave(
|
|
|
498
500
|
);
|
|
499
501
|
}
|
|
500
502
|
|
|
503
|
+
const backend = await getBackend();
|
|
504
|
+
|
|
501
505
|
// Re-assign a new ID to avoid collisions
|
|
502
506
|
const record = deepClone(data.save);
|
|
503
507
|
record.meta.id = crypto.randomUUID();
|
|
504
508
|
record.meta.updatedAt = new Date().toISOString();
|
|
505
509
|
|
|
506
510
|
// Ensure the playthrough exists
|
|
507
|
-
const playthroughs = await getPlaythroughsByIfid(ifid);
|
|
511
|
+
const playthroughs = await backend.getPlaythroughsByIfid(ifid);
|
|
508
512
|
const ptExists = playthroughs.some((p) => p.id === record.meta.playthroughId);
|
|
509
513
|
if (!ptExists) {
|
|
510
514
|
// Create an "Imported" playthrough
|
|
@@ -514,9 +518,89 @@ export async function importSave(
|
|
|
514
518
|
createdAt: record.meta.createdAt,
|
|
515
519
|
label: 'Imported',
|
|
516
520
|
};
|
|
517
|
-
await putPlaythrough(pt);
|
|
521
|
+
await backend.putPlaythrough(pt);
|
|
518
522
|
}
|
|
519
523
|
|
|
520
|
-
await putSave(record);
|
|
524
|
+
await backend.putSave(record);
|
|
521
525
|
return record;
|
|
522
526
|
}
|
|
527
|
+
|
|
528
|
+
// --- Storage Management ---
|
|
529
|
+
|
|
530
|
+
export async function getStorageInfo(ifid: string): Promise<StorageInfo> {
|
|
531
|
+
const backend = await getBackend();
|
|
532
|
+
const saves = await backend.getSavesByIfid(ifid);
|
|
533
|
+
const playthroughs = await backend.getPlaythroughsByIfid(ifid);
|
|
534
|
+
|
|
535
|
+
let totalBytes = 0;
|
|
536
|
+
for (const save of saves) {
|
|
537
|
+
if (save.meta.estimatedBytes != null) {
|
|
538
|
+
totalBytes += save.meta.estimatedBytes;
|
|
539
|
+
} else {
|
|
540
|
+
// Lazy backfill for saves created before estimatedBytes was added
|
|
541
|
+
const bytes = JSON.stringify(save.payload).length;
|
|
542
|
+
save.meta.estimatedBytes = bytes;
|
|
543
|
+
await backend.putSave(save);
|
|
544
|
+
totalBytes += bytes;
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
return {
|
|
549
|
+
saveCount: saves.length,
|
|
550
|
+
playthroughCount: playthroughs.length,
|
|
551
|
+
totalBytes,
|
|
552
|
+
backend: backend.type,
|
|
553
|
+
};
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
export async function clearGameData(ifid: string): Promise<void> {
|
|
557
|
+
const backend = await getBackend();
|
|
558
|
+
await backend.deleteSavesByIfid(ifid);
|
|
559
|
+
await backend.deletePlaythroughsByIfid(ifid);
|
|
560
|
+
await backend.deleteMetaByIfid(ifid);
|
|
561
|
+
clearSession(ifid);
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
export async function clearAllData(): Promise<void> {
|
|
565
|
+
const backend = await getBackend();
|
|
566
|
+
await backend.destroy();
|
|
567
|
+
resetBackend();
|
|
568
|
+
|
|
569
|
+
// Clear all Spindle session keys from sessionStorage
|
|
570
|
+
if (typeof sessionStorage !== 'undefined') {
|
|
571
|
+
const toRemove: string[] = [];
|
|
572
|
+
for (let i = 0; i < sessionStorage.length; i++) {
|
|
573
|
+
const key = sessionStorage.key(i);
|
|
574
|
+
if (key?.startsWith('spindle.session.')) toRemove.push(key);
|
|
575
|
+
}
|
|
576
|
+
for (const key of toRemove) sessionStorage.removeItem(key);
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
export async function deletePlaythroughData(
|
|
581
|
+
ifid: string,
|
|
582
|
+
playthroughId: string,
|
|
583
|
+
): Promise<void> {
|
|
584
|
+
const backend = await getBackend();
|
|
585
|
+
const deletedSaveIds = await backend.deleteSavesByPlaythrough(playthroughId);
|
|
586
|
+
await backend.deletePlaythroughById(playthroughId);
|
|
587
|
+
|
|
588
|
+
// Clean up slot/autosave meta keys pointing to deleted saves
|
|
589
|
+
const deletedSet = new Set(deletedSaveIds);
|
|
590
|
+
const allKeys = await backend.getAllMetaKeys();
|
|
591
|
+
for (const key of allKeys) {
|
|
592
|
+
if (key.startsWith('slot.') || key.startsWith('autosave.')) {
|
|
593
|
+
const value = await backend.getMeta<string>(key);
|
|
594
|
+
if (value && deletedSet.has(value)) {
|
|
595
|
+
await backend.deleteMeta(key);
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
// Clear currentPlaythroughId if it was this one
|
|
601
|
+
const currentPtKey = `currentPlaythroughId.${ifid}`;
|
|
602
|
+
const currentPt = await backend.getMeta<string>(currentPtKey);
|
|
603
|
+
if (currentPt === playthroughId) {
|
|
604
|
+
await backend.deleteMeta(currentPtKey);
|
|
605
|
+
}
|
|
606
|
+
}
|