@rohal12/spindle 0.50.1 → 0.51.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/pkg/format.js +1 -1
- package/dist/pkg/headless.js +25 -12
- package/package.json +1 -1
- package/src/store.ts +77 -51
- package/src/story-api.ts +9 -9
- package/types/index.d.ts +16 -6
package/dist/pkg/headless.js
CHANGED
|
@@ -3489,6 +3489,16 @@ function restorePRNGFromMoment(moment) {
|
|
|
3489
3489
|
if (moment?.prng) restorePRNG(moment.prng.seed, moment.prng.pull);
|
|
3490
3490
|
else if (moment) resetPRNG();
|
|
3491
3491
|
}
|
|
3492
|
+
/**
|
|
3493
|
+
* Return `p` marked as handled: a caller that ignores the result of a
|
|
3494
|
+
* fire-and-forget operation (whose failure is already logged) gets no
|
|
3495
|
+
* unhandled-rejection report, while a caller that awaits it still sees the
|
|
3496
|
+
* error.
|
|
3497
|
+
*/
|
|
3498
|
+
function handled(p) {
|
|
3499
|
+
p.catch(() => {});
|
|
3500
|
+
return p;
|
|
3501
|
+
}
|
|
3492
3502
|
var useStoryStore = create()(immer((set, get) => ({
|
|
3493
3503
|
storyData: null,
|
|
3494
3504
|
currentPassage: "",
|
|
@@ -3754,8 +3764,8 @@ var useStoryStore = create()(immer((set, get) => ({
|
|
|
3754
3764
|
},
|
|
3755
3765
|
save: (slot, custom) => {
|
|
3756
3766
|
const { storyData, playthroughId } = get();
|
|
3757
|
-
if (!storyData) return;
|
|
3758
|
-
saveWithHooks(slot, custom, () => get().getSavePayload(), async (payload) => {
|
|
3767
|
+
if (!storyData) return Promise.resolve();
|
|
3768
|
+
return handled(saveWithHooks(slot, custom, () => get().getSavePayload(), async (payload) => {
|
|
3759
3769
|
set((state) => {
|
|
3760
3770
|
state.saveError = null;
|
|
3761
3771
|
});
|
|
@@ -3771,15 +3781,16 @@ var useStoryStore = create()(immer((set, get) => ({
|
|
|
3771
3781
|
set((state) => {
|
|
3772
3782
|
state.saveError = err instanceof Error ? err.message : "Failed to save";
|
|
3773
3783
|
});
|
|
3774
|
-
|
|
3784
|
+
throw err;
|
|
3785
|
+
}));
|
|
3775
3786
|
},
|
|
3776
3787
|
load: (slot) => {
|
|
3777
3788
|
const { storyData } = get();
|
|
3778
|
-
if (!storyData) return;
|
|
3789
|
+
if (!storyData) return Promise.resolve();
|
|
3779
3790
|
set((state) => {
|
|
3780
3791
|
state.loadError = null;
|
|
3781
3792
|
});
|
|
3782
|
-
loadQuickSave(storyData.ifid, slot).then((payload) => {
|
|
3793
|
+
return handled(loadQuickSave(storyData.ifid, slot).then((payload) => {
|
|
3783
3794
|
if (!payload) return;
|
|
3784
3795
|
get().loadFromPayload(payload, slot);
|
|
3785
3796
|
}).catch((err) => {
|
|
@@ -3787,7 +3798,8 @@ var useStoryStore = create()(immer((set, get) => ({
|
|
|
3787
3798
|
set((state) => {
|
|
3788
3799
|
state.loadError = err instanceof Error ? err.message : "Failed to load";
|
|
3789
3800
|
});
|
|
3790
|
-
|
|
3801
|
+
throw err;
|
|
3802
|
+
}));
|
|
3791
3803
|
},
|
|
3792
3804
|
hasSave: (slot) => {
|
|
3793
3805
|
const { storyData, knownSaves } = get();
|
|
@@ -3806,8 +3818,8 @@ var useStoryStore = create()(immer((set, get) => ({
|
|
|
3806
3818
|
},
|
|
3807
3819
|
deleteSave: (slot) => {
|
|
3808
3820
|
const { storyData } = get();
|
|
3809
|
-
if (!storyData) return;
|
|
3810
|
-
deleteSlotSave(storyData.ifid, slot).then(() => {
|
|
3821
|
+
if (!storyData) return Promise.resolve();
|
|
3822
|
+
return handled(deleteSlotSave(storyData.ifid, slot).then(() => {
|
|
3811
3823
|
set((state) => {
|
|
3812
3824
|
const key = slot ?? "";
|
|
3813
3825
|
const { [key]: _, ...rest } = state.knownSaves;
|
|
@@ -3815,7 +3827,8 @@ var useStoryStore = create()(immer((set, get) => ({
|
|
|
3815
3827
|
});
|
|
3816
3828
|
}).catch((err) => {
|
|
3817
3829
|
console.error("spindle: failed to delete save", err);
|
|
3818
|
-
|
|
3830
|
+
throw err;
|
|
3831
|
+
}));
|
|
3819
3832
|
},
|
|
3820
3833
|
exportSave: async (slot) => {
|
|
3821
3834
|
const { storyData } = get();
|
|
@@ -4739,10 +4752,10 @@ function createStoryAPI() {
|
|
|
4739
4752
|
useStoryStore.getState().restart();
|
|
4740
4753
|
},
|
|
4741
4754
|
save(slot, custom) {
|
|
4742
|
-
useStoryStore.getState().save(slot, custom);
|
|
4755
|
+
return useStoryStore.getState().save(slot, custom);
|
|
4743
4756
|
},
|
|
4744
4757
|
load(slot) {
|
|
4745
|
-
useStoryStore.getState().load(slot);
|
|
4758
|
+
return useStoryStore.getState().load(slot);
|
|
4746
4759
|
},
|
|
4747
4760
|
hasSave(slot) {
|
|
4748
4761
|
return useStoryStore.getState().hasSave(slot);
|
|
@@ -4754,7 +4767,7 @@ function createStoryAPI() {
|
|
|
4754
4767
|
return useStoryStore.getState().listSaves();
|
|
4755
4768
|
},
|
|
4756
4769
|
deleteSave(slot) {
|
|
4757
|
-
useStoryStore.getState().deleteSave(slot);
|
|
4770
|
+
return useStoryStore.getState().deleteSave(slot);
|
|
4758
4771
|
},
|
|
4759
4772
|
exportSave(slot) {
|
|
4760
4773
|
return useStoryStore.getState().exportSave(slot);
|
package/package.json
CHANGED
package/src/store.ts
CHANGED
|
@@ -370,12 +370,12 @@ export interface StoryState {
|
|
|
370
370
|
updateVariables: (recipe: (draft: VariableNamespaces) => void) => void;
|
|
371
371
|
trackRender: (passageName: string) => void;
|
|
372
372
|
restart: () => void;
|
|
373
|
-
save: (slot?: string, custom?: Record<string, unknown>) => void
|
|
374
|
-
load: (slot?: string) => void
|
|
373
|
+
save: (slot?: string, custom?: Record<string, unknown>) => Promise<void>;
|
|
374
|
+
load: (slot?: string) => Promise<void>;
|
|
375
375
|
hasSave: (slot?: string) => boolean;
|
|
376
376
|
getSaveInfo: (slot?: string) => Promise<SaveInfo | null>;
|
|
377
377
|
listSaves: () => Promise<SaveInfo[]>;
|
|
378
|
-
deleteSave: (slot?: string) => void
|
|
378
|
+
deleteSave: (slot?: string) => Promise<void>;
|
|
379
379
|
exportSave: (slot?: string) => Promise<SaveExport | null>;
|
|
380
380
|
importSave: (data: unknown, slot?: string) => Promise<SaveInfo>;
|
|
381
381
|
clearGameData: () => void;
|
|
@@ -395,6 +395,17 @@ export interface StoryState {
|
|
|
395
395
|
clearDeferredRender: () => void;
|
|
396
396
|
}
|
|
397
397
|
|
|
398
|
+
/**
|
|
399
|
+
* Return `p` marked as handled: a caller that ignores the result of a
|
|
400
|
+
* fire-and-forget operation (whose failure is already logged) gets no
|
|
401
|
+
* unhandled-rejection report, while a caller that awaits it still sees the
|
|
402
|
+
* error.
|
|
403
|
+
*/
|
|
404
|
+
function handled<T>(p: Promise<T>): Promise<T> {
|
|
405
|
+
p.catch(() => {});
|
|
406
|
+
return p;
|
|
407
|
+
}
|
|
408
|
+
|
|
398
409
|
export const useStoryStore = create<StoryState>()(
|
|
399
410
|
immer((set, get) => ({
|
|
400
411
|
storyData: null,
|
|
@@ -781,52 +792,64 @@ export const useStoryStore = create<StoryState>()(
|
|
|
781
792
|
|
|
782
793
|
save: (slot?: string, custom?: Record<string, unknown>) => {
|
|
783
794
|
const { storyData, playthroughId } = get();
|
|
784
|
-
if (!storyData) return;
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
state
|
|
793
|
-
|
|
794
|
-
|
|
795
|
+
if (!storyData) return Promise.resolve();
|
|
796
|
+
|
|
797
|
+
return handled(
|
|
798
|
+
saveWithHooks(
|
|
799
|
+
slot,
|
|
800
|
+
custom,
|
|
801
|
+
() => get().getSavePayload(),
|
|
802
|
+
async (payload) => {
|
|
803
|
+
set((state) => {
|
|
804
|
+
state.saveError = null;
|
|
805
|
+
});
|
|
806
|
+
await quickSave(
|
|
807
|
+
storyData.ifid,
|
|
808
|
+
playthroughId,
|
|
809
|
+
payload,
|
|
810
|
+
slot,
|
|
811
|
+
custom,
|
|
812
|
+
);
|
|
813
|
+
set((state) => {
|
|
814
|
+
state.knownSaves = {
|
|
815
|
+
...state.knownSaves,
|
|
816
|
+
[slot ?? '']: true,
|
|
817
|
+
};
|
|
818
|
+
});
|
|
819
|
+
},
|
|
820
|
+
).catch((err) => {
|
|
821
|
+
console.error('spindle: failed to save', err);
|
|
795
822
|
set((state) => {
|
|
796
|
-
state.
|
|
797
|
-
|
|
798
|
-
[slot ?? '']: true,
|
|
799
|
-
};
|
|
823
|
+
state.saveError =
|
|
824
|
+
err instanceof Error ? err.message : 'Failed to save';
|
|
800
825
|
});
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
set((state) => {
|
|
805
|
-
state.saveError =
|
|
806
|
-
err instanceof Error ? err.message : 'Failed to save';
|
|
807
|
-
});
|
|
808
|
-
});
|
|
826
|
+
throw err;
|
|
827
|
+
}),
|
|
828
|
+
);
|
|
809
829
|
},
|
|
810
830
|
|
|
811
831
|
load: (slot?: string) => {
|
|
812
832
|
const { storyData } = get();
|
|
813
|
-
if (!storyData) return;
|
|
833
|
+
if (!storyData) return Promise.resolve();
|
|
814
834
|
|
|
815
835
|
set((state) => {
|
|
816
836
|
state.loadError = null;
|
|
817
837
|
});
|
|
818
|
-
|
|
819
|
-
.
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
state
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
838
|
+
return handled(
|
|
839
|
+
loadQuickSave(storyData.ifid, slot)
|
|
840
|
+
.then((payload) => {
|
|
841
|
+
if (!payload) return;
|
|
842
|
+
get().loadFromPayload(payload, slot);
|
|
843
|
+
})
|
|
844
|
+
.catch((err) => {
|
|
845
|
+
console.error('spindle: failed to load save', err);
|
|
846
|
+
set((state) => {
|
|
847
|
+
state.loadError =
|
|
848
|
+
err instanceof Error ? err.message : 'Failed to load';
|
|
849
|
+
});
|
|
850
|
+
throw err;
|
|
851
|
+
}),
|
|
852
|
+
);
|
|
830
853
|
},
|
|
831
854
|
|
|
832
855
|
hasSave: (slot?: string) => {
|
|
@@ -849,19 +872,22 @@ export const useStoryStore = create<StoryState>()(
|
|
|
849
872
|
|
|
850
873
|
deleteSave: (slot?: string) => {
|
|
851
874
|
const { storyData } = get();
|
|
852
|
-
if (!storyData) return;
|
|
875
|
+
if (!storyData) return Promise.resolve();
|
|
853
876
|
|
|
854
|
-
|
|
855
|
-
.
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
877
|
+
return handled(
|
|
878
|
+
deleteSlotSave(storyData.ifid, slot)
|
|
879
|
+
.then(() => {
|
|
880
|
+
set((state) => {
|
|
881
|
+
const key = slot ?? '';
|
|
882
|
+
const { [key]: _, ...rest } = state.knownSaves;
|
|
883
|
+
state.knownSaves = rest as Record<string, true>;
|
|
884
|
+
});
|
|
885
|
+
})
|
|
886
|
+
.catch((err) => {
|
|
887
|
+
console.error('spindle: failed to delete save', err);
|
|
888
|
+
throw err;
|
|
889
|
+
}),
|
|
890
|
+
);
|
|
865
891
|
},
|
|
866
892
|
|
|
867
893
|
exportSave: async (slot?: string): Promise<SaveExport | null> => {
|
package/src/story-api.ts
CHANGED
|
@@ -155,12 +155,12 @@ export interface StoryAPI {
|
|
|
155
155
|
back(): void;
|
|
156
156
|
forward(): void;
|
|
157
157
|
restart(): void;
|
|
158
|
-
save(slot?: string, custom?: Record<string, unknown>): void
|
|
159
|
-
load(slot?: string): void
|
|
158
|
+
save(slot?: string, custom?: Record<string, unknown>): Promise<void>;
|
|
159
|
+
load(slot?: string): Promise<void>;
|
|
160
160
|
hasSave(slot?: string): boolean;
|
|
161
161
|
getSaveInfo(slot?: string): Promise<SaveInfo | null>;
|
|
162
162
|
listSaves(): Promise<SaveInfo[]>;
|
|
163
|
-
deleteSave(slot?: string): void
|
|
163
|
+
deleteSave(slot?: string): Promise<void>;
|
|
164
164
|
exportSave(slot?: string): Promise<SaveExport | null>;
|
|
165
165
|
importSave(data: unknown, slot?: string): Promise<SaveInfo>;
|
|
166
166
|
visited(name?: string): number;
|
|
@@ -343,12 +343,12 @@ function createStoryAPI(): StoryAPI {
|
|
|
343
343
|
useStoryStore.getState().restart();
|
|
344
344
|
},
|
|
345
345
|
|
|
346
|
-
save(slot?: string, custom?: Record<string, unknown>): void {
|
|
347
|
-
useStoryStore.getState().save(slot, custom);
|
|
346
|
+
save(slot?: string, custom?: Record<string, unknown>): Promise<void> {
|
|
347
|
+
return useStoryStore.getState().save(slot, custom);
|
|
348
348
|
},
|
|
349
349
|
|
|
350
|
-
load(slot?: string): void {
|
|
351
|
-
useStoryStore.getState().load(slot);
|
|
350
|
+
load(slot?: string): Promise<void> {
|
|
351
|
+
return useStoryStore.getState().load(slot);
|
|
352
352
|
},
|
|
353
353
|
|
|
354
354
|
hasSave(slot?: string): boolean {
|
|
@@ -363,8 +363,8 @@ function createStoryAPI(): StoryAPI {
|
|
|
363
363
|
return useStoryStore.getState().listSaves();
|
|
364
364
|
},
|
|
365
365
|
|
|
366
|
-
deleteSave(slot?: string): void {
|
|
367
|
-
useStoryStore.getState().deleteSave(slot);
|
|
366
|
+
deleteSave(slot?: string): Promise<void> {
|
|
367
|
+
return useStoryStore.getState().deleteSave(slot);
|
|
368
368
|
},
|
|
369
369
|
|
|
370
370
|
exportSave(slot?: string): Promise<SaveExport | null> {
|
package/types/index.d.ts
CHANGED
|
@@ -541,11 +541,18 @@ export interface StoryAPI {
|
|
|
541
541
|
/** Restart the story from the beginning. */
|
|
542
542
|
restart(): void;
|
|
543
543
|
|
|
544
|
-
/**
|
|
545
|
-
|
|
544
|
+
/**
|
|
545
|
+
* Save the current state. Pass `slot` for a named save, `custom` for metadata.
|
|
546
|
+
* Resolves once the save is persisted (after `aftersave` handlers ran and
|
|
547
|
+
* `hasSave(slot)` is true); rejects if persisting fails.
|
|
548
|
+
*/
|
|
549
|
+
save(slot?: string, custom?: Record<string, unknown>): Promise<void>;
|
|
546
550
|
|
|
547
|
-
/**
|
|
548
|
-
|
|
551
|
+
/**
|
|
552
|
+
* Load a saved state (quick load). Resolves once the loaded state is applied
|
|
553
|
+
* (immediately if the slot is empty); rejects if loading fails.
|
|
554
|
+
*/
|
|
555
|
+
load(slot?: string): Promise<void>;
|
|
549
556
|
|
|
550
557
|
/** Check whether a save exists. */
|
|
551
558
|
hasSave(slot?: string): boolean;
|
|
@@ -556,8 +563,11 @@ export interface StoryAPI {
|
|
|
556
563
|
/** List metadata for all known save slots. */
|
|
557
564
|
listSaves(): Promise<SaveInfo[]>;
|
|
558
565
|
|
|
559
|
-
/**
|
|
560
|
-
|
|
566
|
+
/**
|
|
567
|
+
* Delete a save by slot name. Resolves once the save is removed and
|
|
568
|
+
* `hasSave(slot)` is false; rejects if deleting fails.
|
|
569
|
+
*/
|
|
570
|
+
deleteSave(slot?: string): Promise<void>;
|
|
561
571
|
|
|
562
572
|
/**
|
|
563
573
|
* Export the save in a slot as a portable object (plain JSON).
|