@fragno-dev/core 0.1.11 → 0.2.2
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/.turbo/turbo-build.log +87 -69
- package/CHANGELOG.md +79 -0
- package/dist/api/api.d.ts +21 -2
- package/dist/api/api.d.ts.map +1 -1
- package/dist/api/api.js +2 -1
- package/dist/api/api.js.map +1 -1
- package/dist/api/bind-services.d.ts +0 -1
- package/dist/api/bind-services.d.ts.map +1 -1
- package/dist/api/bind-services.js.map +1 -1
- package/dist/api/error.d.ts.map +1 -1
- package/dist/api/error.js.map +1 -1
- package/dist/api/fragment-definition-builder.d.ts +32 -40
- package/dist/api/fragment-definition-builder.d.ts.map +1 -1
- package/dist/api/fragment-definition-builder.js +15 -21
- package/dist/api/fragment-definition-builder.js.map +1 -1
- package/dist/api/fragment-instantiator.d.ts +51 -30
- package/dist/api/fragment-instantiator.d.ts.map +1 -1
- package/dist/api/fragment-instantiator.js +201 -52
- package/dist/api/fragment-instantiator.js.map +1 -1
- package/dist/api/request-context-storage.d.ts +4 -0
- package/dist/api/request-context-storage.d.ts.map +1 -1
- package/dist/api/request-context-storage.js +6 -0
- package/dist/api/request-context-storage.js.map +1 -1
- package/dist/api/request-input-context.d.ts +57 -1
- package/dist/api/request-input-context.d.ts.map +1 -1
- package/dist/api/request-input-context.js +67 -0
- package/dist/api/request-input-context.js.map +1 -1
- package/dist/api/request-middleware.d.ts +2 -2
- package/dist/api/request-middleware.d.ts.map +1 -1
- package/dist/api/request-middleware.js.map +1 -1
- package/dist/api/request-output-context.d.ts +1 -1
- package/dist/api/request-output-context.d.ts.map +1 -1
- package/dist/api/request-output-context.js.map +1 -1
- package/dist/api/route-caller.d.ts +30 -0
- package/dist/api/route-caller.d.ts.map +1 -0
- package/dist/api/route-caller.js +63 -0
- package/dist/api/route-caller.js.map +1 -0
- package/dist/api/route-handler-input-options.d.ts.map +1 -1
- package/dist/api/route.d.ts +8 -8
- package/dist/api/route.d.ts.map +1 -1
- package/dist/api/route.js.map +1 -1
- package/dist/api/shared-types.d.ts.map +1 -1
- package/dist/client/client-error.d.ts.map +1 -1
- package/dist/client/client-error.js.map +1 -1
- package/dist/client/client.d.ts +90 -50
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +128 -16
- package/dist/client/client.js.map +1 -1
- package/dist/client/client.svelte.d.ts +6 -5
- package/dist/client/client.svelte.d.ts.map +1 -1
- package/dist/client/client.svelte.js +10 -2
- package/dist/client/client.svelte.js.map +1 -1
- package/dist/client/internal/ndjson-streaming.js.map +1 -1
- package/dist/client/react.d.ts +5 -4
- package/dist/client/react.d.ts.map +1 -1
- package/dist/client/react.js +104 -12
- package/dist/client/react.js.map +1 -1
- package/dist/client/solid.d.ts +7 -5
- package/dist/client/solid.d.ts.map +1 -1
- package/dist/client/solid.js +23 -9
- package/dist/client/solid.js.map +1 -1
- package/dist/client/vanilla.d.ts +16 -4
- package/dist/client/vanilla.d.ts.map +1 -1
- package/dist/client/vanilla.js +21 -1
- package/dist/client/vanilla.js.map +1 -1
- package/dist/client/vue.d.ts +10 -4
- package/dist/client/vue.d.ts.map +1 -1
- package/dist/client/vue.js +24 -1
- package/dist/client/vue.js.map +1 -1
- package/dist/id.d.ts +2 -0
- package/dist/id.js +3 -0
- package/dist/internal/cuid.d.ts +16 -0
- package/dist/internal/cuid.d.ts.map +1 -0
- package/dist/internal/cuid.js +82 -0
- package/dist/internal/cuid.js.map +1 -0
- package/dist/internal/trace-context.d.ts +23 -0
- package/dist/internal/trace-context.d.ts.map +1 -0
- package/dist/internal/trace-context.js +14 -0
- package/dist/internal/trace-context.js.map +1 -0
- package/dist/mod-client.d.ts +7 -20
- package/dist/mod-client.d.ts.map +1 -1
- package/dist/mod-client.js +25 -13
- package/dist/mod-client.js.map +1 -1
- package/dist/mod.d.ts +8 -6
- package/dist/mod.js +3 -1
- package/dist/runtime.d.ts +15 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +33 -0
- package/dist/runtime.js.map +1 -0
- package/dist/test/test.d.ts +6 -6
- package/dist/test/test.d.ts.map +1 -1
- package/dist/test/test.js.map +1 -1
- package/dist/util/ssr.js.map +1 -1
- package/package.json +42 -52
- package/src/api/api.test.ts +3 -1
- package/src/api/api.ts +28 -0
- package/src/api/bind-services.ts +0 -5
- package/src/api/error.ts +1 -0
- package/src/api/fragment-definition-builder.extend.test.ts +2 -1
- package/src/api/fragment-definition-builder.test.ts +2 -1
- package/src/api/fragment-definition-builder.ts +56 -112
- package/src/api/fragment-instantiator.test.ts +311 -166
- package/src/api/fragment-instantiator.ts +470 -131
- package/src/api/fragment-services.test.ts +1 -0
- package/src/api/internal/path-runtime.test.ts +8 -0
- package/src/api/internal/path-type.test.ts +3 -1
- package/src/api/internal/route.test.ts +1 -0
- package/src/api/request-context-storage.ts +7 -0
- package/src/api/request-input-context.test.ts +156 -2
- package/src/api/request-input-context.ts +87 -1
- package/src/api/request-middleware.test.ts +43 -2
- package/src/api/request-middleware.ts +4 -3
- package/src/api/request-output-context.test.ts +3 -1
- package/src/api/request-output-context.ts +2 -1
- package/src/api/route-caller.test.ts +195 -0
- package/src/api/route-caller.ts +167 -0
- package/src/api/route-handler-input-options.ts +2 -1
- package/src/api/route.test.ts +4 -2
- package/src/api/route.ts +9 -3
- package/src/api/shared-types.ts +2 -1
- package/src/client/client-builder.test.ts +4 -2
- package/src/client/client-error.test.ts +2 -1
- package/src/client/client-error.ts +1 -1
- package/src/client/client-types.test.ts +19 -5
- package/src/client/client.ssr.test.ts +6 -4
- package/src/client/client.svelte.test.ts +18 -9
- package/src/client/client.svelte.ts +38 -13
- package/src/client/client.test.ts +244 -10
- package/src/client/client.ts +473 -148
- package/src/client/internal/ndjson-streaming.test.ts +6 -3
- package/src/client/internal/ndjson-streaming.ts +1 -0
- package/src/client/react.test.ts +176 -6
- package/src/client/react.ts +226 -31
- package/src/client/solid.test.ts +29 -5
- package/src/client/solid.ts +60 -22
- package/src/client/vanilla.test.ts +148 -6
- package/src/client/vanilla.ts +63 -9
- package/src/client/vue.test.ts +397 -8
- package/src/client/vue.ts +74 -4
- package/src/id.ts +1 -0
- package/src/internal/cuid.test.ts +164 -0
- package/src/internal/cuid.ts +133 -0
- package/src/internal/trace-context.ts +35 -0
- package/src/mod-client.ts +55 -9
- package/src/mod.ts +9 -3
- package/src/runtime.ts +48 -0
- package/src/test/test.test.ts +4 -2
- package/src/test/test.ts +14 -7
- package/src/util/async.test.ts +1 -0
- package/src/util/content-type.test.ts +1 -0
- package/src/util/nanostores.test.ts +3 -1
- package/src/util/ssr.ts +1 -0
- package/tsconfig.json +1 -1
- package/tsdown.config.ts +2 -0
- package/vitest.config.ts +2 -1
package/src/client/solid.ts
CHANGED
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
1
|
import { atom, type ReadableAtom, type Store, type StoreValue } from "nanostores";
|
|
3
|
-
import { useStore } from "@nanostores/solid";
|
|
4
2
|
import type { Accessor } from "solid-js";
|
|
5
|
-
import { createEffect } from "solid-js";
|
|
3
|
+
import { createEffect, onCleanup } from "solid-js";
|
|
4
|
+
|
|
5
|
+
import { useStore } from "@nanostores/solid";
|
|
6
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
7
|
+
|
|
6
8
|
import type { NonGetHTTPMethod } from "../api/api";
|
|
9
|
+
import type { MaybeExtractPathParamsOrWiden, QueryParamsHint } from "../api/internal/path";
|
|
10
|
+
import { isReadableAtom } from "../util/nanostores";
|
|
11
|
+
import type { InferOr } from "../util/types-util";
|
|
7
12
|
import {
|
|
8
13
|
isGetHook,
|
|
9
14
|
isMutatorHook,
|
|
@@ -11,11 +16,10 @@ import {
|
|
|
11
16
|
type FragnoClientHookData,
|
|
12
17
|
type FragnoClientMutatorData,
|
|
13
18
|
type FragnoStoreData,
|
|
19
|
+
type FragnoStoreFactoryData,
|
|
20
|
+
type FragnoStoreObjectData,
|
|
14
21
|
} from "./client";
|
|
15
22
|
import type { FragnoClientError } from "./client-error";
|
|
16
|
-
import type { InferOr } from "../util/types-util";
|
|
17
|
-
import type { MaybeExtractPathParamsOrWiden, QueryParamsHint } from "../api/internal/path";
|
|
18
|
-
import { isReadableAtom } from "../util/nanostores";
|
|
19
23
|
|
|
20
24
|
export type FragnoSolidHook<
|
|
21
25
|
_TMethod extends "GET",
|
|
@@ -178,38 +182,72 @@ function createSolidMutator<
|
|
|
178
182
|
};
|
|
179
183
|
}
|
|
180
184
|
|
|
181
|
-
|
|
185
|
+
type FragnoSolidStoreValue<T extends object> = T extends Store
|
|
186
|
+
? Accessor<StoreValue<T>>
|
|
187
|
+
: {
|
|
188
|
+
[K in keyof T]: T[K] extends Store ? Accessor<StoreValue<T[K]>> : T[K];
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
type FragnoSolidStaticStore<T extends object> = T extends Store
|
|
182
192
|
? Accessor<StoreValue<T>>
|
|
183
193
|
: () => {
|
|
184
194
|
[K in keyof T]: T[K] extends Store ? Accessor<StoreValue<T[K]>> : T[K];
|
|
185
195
|
};
|
|
186
196
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
197
|
+
export type FragnoSolidStore<TStore extends FragnoStoreData<object, unknown[]>> =
|
|
198
|
+
TStore extends FragnoStoreFactoryData<infer T, infer TArgs>
|
|
199
|
+
? (...args: TArgs) => FragnoSolidStoreValue<T>
|
|
200
|
+
: TStore extends FragnoStoreObjectData<infer T>
|
|
201
|
+
? FragnoSolidStaticStore<T>
|
|
202
|
+
: never;
|
|
203
|
+
|
|
204
|
+
function unwrapSolidStoreValue<T extends object>(value: T): FragnoSolidStoreValue<T> {
|
|
205
|
+
if (isReadableAtom(value)) {
|
|
206
|
+
return useStore(value) as FragnoSolidStoreValue<T>;
|
|
193
207
|
}
|
|
194
208
|
|
|
195
209
|
// For objects containing atoms, wrap each atom property with useStore
|
|
196
210
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
197
211
|
const result: any = {};
|
|
198
212
|
|
|
199
|
-
for (const key in
|
|
200
|
-
if (!Object.prototype.hasOwnProperty.call(
|
|
213
|
+
for (const key in value) {
|
|
214
|
+
if (!Object.prototype.hasOwnProperty.call(value, key)) {
|
|
201
215
|
continue;
|
|
202
216
|
}
|
|
203
217
|
|
|
204
|
-
const
|
|
205
|
-
if (isReadableAtom(
|
|
206
|
-
result[key] = useStore(
|
|
218
|
+
const fieldValue = value[key];
|
|
219
|
+
if (isReadableAtom(fieldValue)) {
|
|
220
|
+
result[key] = useStore(fieldValue);
|
|
207
221
|
} else {
|
|
208
|
-
result[key] =
|
|
222
|
+
result[key] = fieldValue;
|
|
209
223
|
}
|
|
210
224
|
}
|
|
211
225
|
|
|
212
|
-
return
|
|
226
|
+
return result as FragnoSolidStoreValue<T>;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function createSolidStore<const TStore extends FragnoStoreData<object, unknown[]>>(
|
|
230
|
+
hook: TStore,
|
|
231
|
+
): FragnoSolidStore<TStore> {
|
|
232
|
+
if ("obj" in hook) {
|
|
233
|
+
if (isReadableAtom(hook.obj)) {
|
|
234
|
+
return useStore(hook.obj) as FragnoSolidStore<TStore>;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return (() => unwrapSolidStoreValue(hook.obj)) as FragnoSolidStore<TStore>;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
return ((...args: Parameters<typeof hook.factory>) => {
|
|
241
|
+
const value = hook.factory(...args);
|
|
242
|
+
const disposer = (value as { [Symbol.dispose]?: (() => void) | undefined })[Symbol.dispose];
|
|
243
|
+
if (typeof disposer === "function") {
|
|
244
|
+
onCleanup(() => {
|
|
245
|
+
disposer.call(value);
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return unwrapSolidStoreValue(value);
|
|
250
|
+
}) as FragnoSolidStore<TStore>;
|
|
213
251
|
}
|
|
214
252
|
|
|
215
253
|
export function useFragno<T extends Record<string, unknown>>(
|
|
@@ -232,8 +270,8 @@ export function useFragno<T extends Record<string, unknown>>(
|
|
|
232
270
|
infer TQueryParameters
|
|
233
271
|
>
|
|
234
272
|
? FragnoSolidMutator<TMethod, TPath, TInput, TOutput, TError, TQueryParameters>
|
|
235
|
-
: T[K] extends FragnoStoreData<
|
|
236
|
-
? FragnoSolidStore<
|
|
273
|
+
: T[K] extends FragnoStoreData<object, unknown[]>
|
|
274
|
+
? FragnoSolidStore<T[K]>
|
|
237
275
|
: T[K];
|
|
238
276
|
} {
|
|
239
277
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { test, expect, describe, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
-
|
|
1
|
+
import { test, expect, describe, vi, beforeEach, afterEach, expectTypeOf } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { atom, type ReadableAtom } from "nanostores";
|
|
3
4
|
import { z } from "zod";
|
|
4
|
-
|
|
5
|
-
import { useFragno } from "./vanilla";
|
|
6
|
-
import { defineRoute } from "../api/route";
|
|
5
|
+
|
|
7
6
|
import { defineFragment } from "../api/fragment-definition-builder";
|
|
7
|
+
import { defineRoute } from "../api/route";
|
|
8
|
+
import { waitForAsyncIterator } from "../util/async";
|
|
9
|
+
import { createClientBuilder } from "./client";
|
|
8
10
|
import type { FragnoPublicClientConfig } from "./client";
|
|
9
11
|
import { FragnoClientFetchNetworkError } from "./client-error";
|
|
10
|
-
import {
|
|
12
|
+
import { useFragno } from "./vanilla";
|
|
11
13
|
|
|
12
14
|
// Mock fetch globally
|
|
13
15
|
global.fetch = vi.fn();
|
|
@@ -620,6 +622,56 @@ describe("createVanillaMutator", () => {
|
|
|
620
622
|
});
|
|
621
623
|
});
|
|
622
624
|
|
|
625
|
+
describe("useFragno - createStore", () => {
|
|
626
|
+
const clientConfig: FragnoPublicClientConfig = {
|
|
627
|
+
baseUrl: "http://localhost:3000",
|
|
628
|
+
};
|
|
629
|
+
|
|
630
|
+
test("unwraps store wrappers to the raw store object", () => {
|
|
631
|
+
const stringAtom = atom("hello");
|
|
632
|
+
const numberAtom = atom(42);
|
|
633
|
+
const cb = createClientBuilder(defineFragment("test-fragment-store"), clientConfig, []);
|
|
634
|
+
const client = {
|
|
635
|
+
useStore: cb.createStore({
|
|
636
|
+
message: stringAtom,
|
|
637
|
+
count: numberAtom,
|
|
638
|
+
constant: 7,
|
|
639
|
+
}),
|
|
640
|
+
};
|
|
641
|
+
|
|
642
|
+
const { useStore } = useFragno(client);
|
|
643
|
+
|
|
644
|
+
expectTypeOf(useStore).toExtend<{
|
|
645
|
+
message: typeof stringAtom;
|
|
646
|
+
count: typeof numberAtom;
|
|
647
|
+
constant: number;
|
|
648
|
+
}>();
|
|
649
|
+
|
|
650
|
+
if (!("obj" in client.useStore)) {
|
|
651
|
+
throw new Error("Expected object-backed store");
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
expect(useStore).toBe(client.useStore.obj);
|
|
655
|
+
expect(useStore.message.get()).toBe("hello");
|
|
656
|
+
expect(useStore.count.get()).toBe(42);
|
|
657
|
+
expect(useStore.constant).toBe(7);
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
test("unwraps single-store wrappers to the underlying atom", () => {
|
|
661
|
+
const singleAtom = atom("single");
|
|
662
|
+
const cb = createClientBuilder(defineFragment("test-fragment-single-store"), clientConfig, []);
|
|
663
|
+
const client = {
|
|
664
|
+
useSingle: cb.createStore(singleAtom),
|
|
665
|
+
};
|
|
666
|
+
|
|
667
|
+
const { useSingle } = useFragno(client);
|
|
668
|
+
|
|
669
|
+
expectTypeOf(useSingle).toExtend<typeof singleAtom>();
|
|
670
|
+
expect(useSingle).toBe(singleAtom);
|
|
671
|
+
expect(useSingle.get()).toBe("single");
|
|
672
|
+
});
|
|
673
|
+
});
|
|
674
|
+
|
|
623
675
|
describe("useFragno", () => {
|
|
624
676
|
const testFragmentDefinition = defineFragment("test-fragment-useFragno");
|
|
625
677
|
const testRoutes = [
|
|
@@ -771,6 +823,96 @@ describe("useFragno", () => {
|
|
|
771
823
|
});
|
|
772
824
|
});
|
|
773
825
|
|
|
826
|
+
describe("createVanillaStore", () => {
|
|
827
|
+
const clientConfig: FragnoPublicClientConfig = {
|
|
828
|
+
baseUrl: "http://localhost:3000",
|
|
829
|
+
};
|
|
830
|
+
|
|
831
|
+
test("should support createStore factory callbacks", () => {
|
|
832
|
+
const dispose = vi.fn();
|
|
833
|
+
const builder = createClientBuilder(defineFragment("test-fragment-store"), clientConfig, []);
|
|
834
|
+
const clientObj = {
|
|
835
|
+
useSession: builder.createStore(({ path }: { path: { sessionId: string } }) => ({
|
|
836
|
+
sessionId: atom(path.sessionId),
|
|
837
|
+
sendMessage: (text: string) => text,
|
|
838
|
+
[Symbol.dispose]: dispose,
|
|
839
|
+
})),
|
|
840
|
+
};
|
|
841
|
+
|
|
842
|
+
const { useSession } = useFragno(clientObj);
|
|
843
|
+
const session = useSession({ path: { sessionId: "abc" } });
|
|
844
|
+
|
|
845
|
+
expect(session.sessionId.get()).toBe("abc");
|
|
846
|
+
expect(session.sendMessage("hello")).toBe("hello");
|
|
847
|
+
expect(typeof session.destroy).toBe("function");
|
|
848
|
+
|
|
849
|
+
session.destroy?.();
|
|
850
|
+
expect(dispose).toHaveBeenCalledTimes(1);
|
|
851
|
+
});
|
|
852
|
+
|
|
853
|
+
test("should preserve prototype methods when adding destroy to factory store instances", () => {
|
|
854
|
+
const dispose = vi.fn();
|
|
855
|
+
const builder = createClientBuilder(
|
|
856
|
+
defineFragment("test-fragment-store-instance"),
|
|
857
|
+
clientConfig,
|
|
858
|
+
[],
|
|
859
|
+
);
|
|
860
|
+
|
|
861
|
+
class SessionStore {
|
|
862
|
+
sessionId: ReadableAtom<string>;
|
|
863
|
+
|
|
864
|
+
constructor(sessionId: string) {
|
|
865
|
+
this.sessionId = atom(sessionId);
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
sendMessage(text: string) {
|
|
869
|
+
return `${this.sessionId.get()}:${text}`;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
[Symbol.dispose]() {
|
|
873
|
+
dispose();
|
|
874
|
+
}
|
|
875
|
+
}
|
|
876
|
+
|
|
877
|
+
const clientObj = {
|
|
878
|
+
useSession: builder.createStore(({ path }: { path: { sessionId: string } }) => {
|
|
879
|
+
return new SessionStore(path.sessionId);
|
|
880
|
+
}),
|
|
881
|
+
};
|
|
882
|
+
|
|
883
|
+
const { useSession } = useFragno(clientObj);
|
|
884
|
+
const session = useSession({ path: { sessionId: "abc" } });
|
|
885
|
+
|
|
886
|
+
expect(session).toBeInstanceOf(SessionStore);
|
|
887
|
+
expect(session.sendMessage("hello")).toBe("abc:hello");
|
|
888
|
+
expect(typeof session.destroy).toBe("function");
|
|
889
|
+
expect(Object.keys(session)).not.toContain("destroy");
|
|
890
|
+
|
|
891
|
+
session.destroy?.();
|
|
892
|
+
expect(dispose).toHaveBeenCalledTimes(1);
|
|
893
|
+
});
|
|
894
|
+
|
|
895
|
+
test("should type and return zero-argument factory stores as callable", () => {
|
|
896
|
+
const builder = createClientBuilder(
|
|
897
|
+
defineFragment("test-fragment-zero-arg-store"),
|
|
898
|
+
clientConfig,
|
|
899
|
+
[],
|
|
900
|
+
);
|
|
901
|
+
const countAtom = atom(0);
|
|
902
|
+
const clientObj = {
|
|
903
|
+
useCounter: builder.createStore(() => ({
|
|
904
|
+
count: countAtom,
|
|
905
|
+
})),
|
|
906
|
+
};
|
|
907
|
+
|
|
908
|
+
const { useCounter } = useFragno(clientObj);
|
|
909
|
+
|
|
910
|
+
expectTypeOf(useCounter).toExtend<() => { count: typeof countAtom }>();
|
|
911
|
+
expect(typeof useCounter).toBe("function");
|
|
912
|
+
expect(useCounter().count.get()).toBe(0);
|
|
913
|
+
});
|
|
914
|
+
});
|
|
915
|
+
|
|
774
916
|
describe("error handling", () => {
|
|
775
917
|
const testFragmentDefinition = defineFragment("test-fragment-errors");
|
|
776
918
|
const testRoutes = [
|
package/src/client/vanilla.ts
CHANGED
|
@@ -1,20 +1,26 @@
|
|
|
1
|
-
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
2
1
|
import type { ReadableAtom } from "nanostores";
|
|
2
|
+
|
|
3
|
+
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
|
4
|
+
|
|
3
5
|
import type { NonGetHTTPMethod } from "../api/api";
|
|
6
|
+
import type {
|
|
7
|
+
ExtractPathParamsOrWiden,
|
|
8
|
+
HasPathParams,
|
|
9
|
+
MaybeExtractPathParamsOrWiden,
|
|
10
|
+
} from "../api/internal/path";
|
|
11
|
+
import { createAsyncIteratorFromCallback } from "../util/async";
|
|
12
|
+
import type { InferOr } from "../util/types-util";
|
|
4
13
|
import {
|
|
5
14
|
isGetHook,
|
|
6
15
|
isMutatorHook,
|
|
16
|
+
isStore,
|
|
7
17
|
type FragnoClientMutatorData,
|
|
8
18
|
type FragnoClientHookData,
|
|
19
|
+
type FragnoStoreData,
|
|
20
|
+
type FragnoStoreFactoryData,
|
|
21
|
+
type FragnoStoreObjectData,
|
|
9
22
|
} from "./client";
|
|
10
23
|
import type { FragnoClientError } from "./client-error";
|
|
11
|
-
import { createAsyncIteratorFromCallback } from "../util/async";
|
|
12
|
-
import type { InferOr } from "../util/types-util";
|
|
13
|
-
import type {
|
|
14
|
-
ExtractPathParamsOrWiden,
|
|
15
|
-
HasPathParams,
|
|
16
|
-
MaybeExtractPathParamsOrWiden,
|
|
17
|
-
} from "../api/internal/path";
|
|
18
24
|
|
|
19
25
|
export type StoreData<
|
|
20
26
|
TOutputSchema extends StandardSchemaV1 | undefined,
|
|
@@ -213,6 +219,48 @@ function createVanillaMutator<
|
|
|
213
219
|
};
|
|
214
220
|
}
|
|
215
221
|
|
|
222
|
+
export type FragnoVanillaStore<T extends object, TArgs extends unknown[] = []> = TArgs extends []
|
|
223
|
+
? T
|
|
224
|
+
: (...args: TArgs) => T & { destroy?: () => void };
|
|
225
|
+
|
|
226
|
+
export type FragnoVanillaStoreFromData<TStore> = TStore extends {
|
|
227
|
+
factory: (...args: infer TArgs) => infer TObject extends object;
|
|
228
|
+
}
|
|
229
|
+
? (...args: TArgs) => TObject & { destroy?: () => void }
|
|
230
|
+
: TStore extends { obj: infer TObject extends object }
|
|
231
|
+
? TObject
|
|
232
|
+
: never;
|
|
233
|
+
|
|
234
|
+
const getStoreDisposer = (value: object): (() => void) | undefined => {
|
|
235
|
+
const disposer = (value as { [Symbol.dispose]?: (() => void) | undefined })[Symbol.dispose];
|
|
236
|
+
return typeof disposer === "function" ? disposer.bind(value) : undefined;
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
function createVanillaStore<const TStore extends FragnoStoreData<object, unknown[]>>(
|
|
240
|
+
hook: TStore,
|
|
241
|
+
): FragnoVanillaStoreFromData<TStore> {
|
|
242
|
+
if ("obj" in hook) {
|
|
243
|
+
return hook.obj as FragnoVanillaStoreFromData<TStore>;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return ((...args: Parameters<typeof hook.factory>) => {
|
|
247
|
+
const value = hook.factory(...args);
|
|
248
|
+
const disposer = getStoreDisposer(value);
|
|
249
|
+
if (!disposer) {
|
|
250
|
+
return value as ReturnType<typeof hook.factory> & { destroy?: () => void };
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
Object.defineProperty(value, "destroy", {
|
|
254
|
+
value: disposer,
|
|
255
|
+
enumerable: false,
|
|
256
|
+
configurable: true,
|
|
257
|
+
writable: true,
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
return value as ReturnType<typeof hook.factory> & { destroy?: () => void };
|
|
261
|
+
}) as FragnoVanillaStoreFromData<TStore>;
|
|
262
|
+
}
|
|
263
|
+
|
|
216
264
|
export function useFragno<T extends Record<string, unknown>>(
|
|
217
265
|
clientObj: T,
|
|
218
266
|
): {
|
|
@@ -240,7 +288,11 @@ export function useFragno<T extends Record<string, unknown>>(
|
|
|
240
288
|
TErrorCode,
|
|
241
289
|
TQueryParameters
|
|
242
290
|
>
|
|
243
|
-
: T[K]
|
|
291
|
+
: T[K] extends FragnoStoreObjectData<infer TStoreObj>
|
|
292
|
+
? TStoreObj
|
|
293
|
+
: T[K] extends FragnoStoreFactoryData<infer TStoreObj, infer TStoreArgs>
|
|
294
|
+
? (...args: TStoreArgs) => TStoreObj & { destroy?: () => void }
|
|
295
|
+
: T[K];
|
|
244
296
|
} {
|
|
245
297
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
246
298
|
const result = {} as any; // We need one any cast here due to TypeScript's limitations with mapped types
|
|
@@ -255,6 +307,8 @@ export function useFragno<T extends Record<string, unknown>>(
|
|
|
255
307
|
result[key] = createVanillaListeners(hook);
|
|
256
308
|
} else if (isMutatorHook(hook)) {
|
|
257
309
|
result[key] = createVanillaMutator(hook);
|
|
310
|
+
} else if (isStore(hook)) {
|
|
311
|
+
result[key] = createVanillaStore(hook);
|
|
258
312
|
} else {
|
|
259
313
|
// Pass through non-hook values unchanged
|
|
260
314
|
result[key] = hook;
|