@fragno-dev/core 0.0.6 → 0.0.7
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 +41 -37
- package/.turbo/turbo-test.log +297 -0
- package/CHANGELOG.md +8 -0
- package/dist/api/api.d.ts +1 -1
- package/dist/api/api.js +1 -1
- package/dist/{api-CBDGZiLC.d.ts → api-CAPyac52.d.ts} +3 -3
- package/dist/api-CAPyac52.d.ts.map +1 -0
- package/dist/{api-DgHfYjq2.js → api-DuzjjCT4.js} +2 -2
- package/dist/{api-DgHfYjq2.js.map → api-DuzjjCT4.js.map} +1 -1
- package/dist/client/client.d.ts +2 -2
- package/dist/client/client.js +4 -4
- package/dist/client/client.svelte.d.ts +14 -14
- package/dist/client/client.svelte.d.ts.map +1 -1
- package/dist/client/client.svelte.js +4 -4
- package/dist/client/react.d.ts +12 -12
- package/dist/client/react.d.ts.map +1 -1
- package/dist/client/react.js +6 -8
- package/dist/client/react.js.map +1 -1
- package/dist/client/solid.d.ts +45 -0
- package/dist/client/solid.d.ts.map +1 -0
- package/dist/client/solid.js +110 -0
- package/dist/client/solid.js.map +1 -0
- package/dist/client/vanilla.d.ts +21 -21
- package/dist/client/vanilla.d.ts.map +1 -1
- package/dist/client/vanilla.js +4 -4
- package/dist/client/vue.d.ts +14 -14
- package/dist/client/vue.d.ts.map +1 -1
- package/dist/client/vue.js +4 -4
- package/dist/{client-DWjxKDnE.js → client-C_Oc8hpD.js} +7 -10
- package/dist/{client-DWjxKDnE.js.map → client-C_Oc8hpD.js.map} +1 -1
- package/dist/{client-B6s-lTFe.d.ts → client-DdpPMlXL.d.ts} +73 -50
- package/dist/client-DdpPMlXL.d.ts.map +1 -0
- package/dist/integrations/react-ssr.js +1 -1
- package/dist/mod.d.ts +2 -2
- package/dist/mod.js +16 -6
- package/dist/mod.js.map +1 -1
- package/dist/{route-Bp6eByhz.js → route-Dq62lXqB.js} +6 -6
- package/dist/route-Dq62lXqB.js.map +1 -0
- package/dist/{ssr-tJHqcNSw.js → ssr-BAhbA_3q.js} +2 -2
- package/dist/{ssr-tJHqcNSw.js.map → ssr-BAhbA_3q.js.map} +1 -1
- package/package.json +22 -5
- package/src/api/fragment.ts +31 -13
- package/src/api/request-middleware.test.ts +3 -3
- package/src/api/request-output-context.ts +3 -3
- package/src/api/route.ts +1 -8
- package/src/client/client-error.test.ts +17 -1
- package/src/client/solid.test.ts +840 -0
- package/src/client/solid.ts +261 -0
- package/tsdown.config.ts +1 -0
- package/vitest.config.ts +10 -7
- package/dist/api-CBDGZiLC.d.ts.map +0 -1
- package/dist/client-B6s-lTFe.d.ts.map +0 -1
- package/dist/route-Bp6eByhz.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr-
|
|
1
|
+
{"version":3,"file":"ssr-BAhbA_3q.js","names":["stores: FetcherStore[]","clientInitialData: Map<string, unknown> | undefined","stores"],"sources":["../src/util/ssr.ts"],"sourcesContent":["import { allTasks } from \"nanostores\";\nimport type { FetcherStore } from \"@nanostores/query\";\n\nlet stores: FetcherStore[] = [];\n\nexport const SSR_ENABLED = false;\n\nexport function getStores() {\n return stores;\n}\n\nexport function addStore(store: FetcherStore) {\n stores.push(store);\n}\n\nexport function cleanStores() {\n stores = [];\n}\n\n// Client side\ndeclare global {\n interface Window {\n __FRAGNO_INITIAL_DATA__?: [string, unknown][];\n }\n}\n\nlet clientInitialData: Map<string, unknown> | undefined;\n\nexport function hydrateFromWindow() {\n if (typeof window !== \"undefined\" && window.__FRAGNO_INITIAL_DATA__) {\n clientInitialData = new Map(window.__FRAGNO_INITIAL_DATA__);\n delete window.__FRAGNO_INITIAL_DATA__;\n console.warn(\"hydrateFromWindow\", {\n clientInitialData: Array.from(clientInitialData.entries()),\n });\n }\n}\n\nexport function getInitialData(key: string): unknown | undefined {\n if (clientInitialData?.has(key)) {\n const data = clientInitialData.get(key);\n clientInitialData.delete(key);\n return data;\n }\n return undefined;\n}\n\nfunction listenToStores(): void {\n for (const store of getStores()) {\n // By calling `listen`, we trigger the fetcher function of the store.\n // This will start the data fetching process on the server.\n // We don't need to do anything with the return value of `listen`, as we\n // are only interested in starting the data fetching.\n store.listen(() => {});\n }\n}\n\n// Server side\nexport async function getFinalStoreValues(): Promise<Map<string, unknown>> {\n listenToStores();\n await allTasks();\n\n const stores = getStores();\n const storesInitialValue = new Map<string, unknown>();\n\n for (const store of stores) {\n const value = store.get();\n if (!value || !store.key || value.loading) {\n continue;\n }\n storesInitialValue.set(store.key, value.data);\n }\n\n return storesInitialValue;\n}\n"],"mappings":";;;AAGA,IAAIA,SAAyB,EAAE;AAE/B,MAAa,cAAc;AAE3B,SAAgB,YAAY;AAC1B,QAAO;;AAGT,SAAgB,SAAS,OAAqB;AAC5C,QAAO,KAAK,MAAM;;AAGpB,SAAgB,cAAc;AAC5B,UAAS,EAAE;;AAUb,IAAIC;AAEJ,SAAgB,oBAAoB;AAClC,KAAI,OAAO,WAAW,eAAe,OAAO,yBAAyB;AACnE,sBAAoB,IAAI,IAAI,OAAO,wBAAwB;AAC3D,SAAO,OAAO;AACd,UAAQ,KAAK,qBAAqB,EAChC,mBAAmB,MAAM,KAAK,kBAAkB,SAAS,CAAC,EAC3D,CAAC;;;AAIN,SAAgB,eAAe,KAAkC;AAC/D,KAAI,mBAAmB,IAAI,IAAI,EAAE;EAC/B,MAAM,OAAO,kBAAkB,IAAI,IAAI;AACvC,oBAAkB,OAAO,IAAI;AAC7B,SAAO;;;AAKX,SAAS,iBAAuB;AAC9B,MAAK,MAAM,SAAS,WAAW,CAK7B,OAAM,aAAa,GAAG;;AAK1B,eAAsB,sBAAqD;AACzE,iBAAgB;AAChB,OAAM,UAAU;CAEhB,MAAMC,WAAS,WAAW;CAC1B,MAAM,qCAAqB,IAAI,KAAsB;AAErD,MAAK,MAAM,SAASA,UAAQ;EAC1B,MAAM,QAAQ,MAAM,KAAK;AACzB,MAAI,CAAC,SAAS,CAAC,MAAM,OAAO,MAAM,QAChC;AAEF,qBAAmB,IAAI,MAAM,KAAK,MAAM,KAAK;;AAG/C,QAAO"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fragno-dev/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"exports": {
|
|
5
5
|
".": {
|
|
6
6
|
"bun": "./src/mod.ts",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"default": "./dist/client/client.js"
|
|
22
22
|
},
|
|
23
23
|
"./react": {
|
|
24
|
-
"bun": "./src/client/react.
|
|
24
|
+
"bun": "./src/client/react.ts",
|
|
25
25
|
"development": "./src/client/react.ts",
|
|
26
26
|
"types": "./dist/client/react.d.ts",
|
|
27
27
|
"default": "./dist/client/react.js"
|
|
@@ -44,6 +44,12 @@
|
|
|
44
44
|
"types": "./dist/client/client.svelte.d.ts",
|
|
45
45
|
"default": "./dist/client/client.svelte.js"
|
|
46
46
|
},
|
|
47
|
+
"./solid": {
|
|
48
|
+
"bun": "./src/client/solid.ts",
|
|
49
|
+
"development": "./src/client/solid.ts",
|
|
50
|
+
"types": "./dist/client/solid.d.ts",
|
|
51
|
+
"default": "./dist/client/solid.js"
|
|
52
|
+
},
|
|
47
53
|
"./react-ssr": {
|
|
48
54
|
"bun": "./src/integrations/react-ssr.ts",
|
|
49
55
|
"development": "./src/integrations/react-ssr.ts",
|
|
@@ -58,17 +64,21 @@
|
|
|
58
64
|
"scripts": {
|
|
59
65
|
"build": "tsdown",
|
|
60
66
|
"build:watch": "tsdown --watch",
|
|
61
|
-
"types:check": "tsc --noEmit"
|
|
67
|
+
"types:check": "tsc --noEmit",
|
|
68
|
+
"test": "vitest run",
|
|
69
|
+
"test:watch": "vitest --watch"
|
|
62
70
|
},
|
|
63
71
|
"type": "module",
|
|
64
72
|
"dependencies": {
|
|
65
73
|
"@nanostores/query": "^0.3.4",
|
|
74
|
+
"@nanostores/solid": "^1.1.1",
|
|
66
75
|
"@standard-schema/spec": "^1.0.0",
|
|
67
76
|
"nanostores": "^1.0.1",
|
|
68
77
|
"rou3": "^0.7.3"
|
|
69
78
|
},
|
|
70
79
|
"devDependencies": {
|
|
71
80
|
"@fragno-private/typescript-config": "0.0.1",
|
|
81
|
+
"@fragno-private/vitest-config": "0.0.0",
|
|
72
82
|
"@sveltejs/vite-plugin-svelte": "^6.2.0",
|
|
73
83
|
"@testing-library/react": "^16.3.0",
|
|
74
84
|
"@testing-library/svelte": "^5.2.8",
|
|
@@ -76,17 +86,21 @@
|
|
|
76
86
|
"@types/node": "^22",
|
|
77
87
|
"@types/react": "^19.0.0",
|
|
78
88
|
"@types/react-dom": "^19.0.0",
|
|
79
|
-
"
|
|
89
|
+
"@vitest/coverage-istanbul": "^3.2.4",
|
|
90
|
+
"happy-dom": "^20.0.0",
|
|
80
91
|
"react": "^19.0.0",
|
|
81
92
|
"react-dom": "^19.0.0",
|
|
82
93
|
"svelte": "^5.1.0",
|
|
94
|
+
"solid-js": "^1.9.3",
|
|
95
|
+
"vitest": "^3.2.4",
|
|
83
96
|
"vue": "^3",
|
|
84
97
|
"zod": "^4.0.5"
|
|
85
98
|
},
|
|
86
99
|
"peerDependencies": {
|
|
87
100
|
"react": "^19.0.0",
|
|
88
101
|
"vue": "^3",
|
|
89
|
-
"svelte": "^5"
|
|
102
|
+
"svelte": "^5",
|
|
103
|
+
"solid-js": "^1.0.0"
|
|
90
104
|
},
|
|
91
105
|
"peerDependenciesMeta": {
|
|
92
106
|
"react": {
|
|
@@ -97,6 +111,9 @@
|
|
|
97
111
|
},
|
|
98
112
|
"svelte": {
|
|
99
113
|
"optional": true
|
|
114
|
+
},
|
|
115
|
+
"solid-js": {
|
|
116
|
+
"optional": true
|
|
100
117
|
}
|
|
101
118
|
},
|
|
102
119
|
"repository": {
|
package/src/api/fragment.ts
CHANGED
|
@@ -7,7 +7,6 @@ import { RequestInputContext } from "./request-input-context";
|
|
|
7
7
|
import type { ExtractPathParams } from "./internal/path";
|
|
8
8
|
import { RequestOutputContext } from "./request-output-context";
|
|
9
9
|
import {
|
|
10
|
-
type EmptyObject,
|
|
11
10
|
type AnyFragnoRouteConfig,
|
|
12
11
|
type AnyRouteOrFactory,
|
|
13
12
|
type FlattenRouteFactories,
|
|
@@ -37,6 +36,16 @@ type ReactRouterHandlers = {
|
|
|
37
36
|
action: (args: { request: Request }) => Promise<Response>;
|
|
38
37
|
};
|
|
39
38
|
|
|
39
|
+
type SolidStartHandlers = {
|
|
40
|
+
GET: (args: { request: Request }) => Promise<Response>;
|
|
41
|
+
POST: (args: { request: Request }) => Promise<Response>;
|
|
42
|
+
PUT: (args: { request: Request }) => Promise<Response>;
|
|
43
|
+
DELETE: (args: { request: Request }) => Promise<Response>;
|
|
44
|
+
PATCH: (args: { request: Request }) => Promise<Response>;
|
|
45
|
+
HEAD: (args: { request: Request }) => Promise<Response>;
|
|
46
|
+
OPTIONS: (args: { request: Request }) => Promise<Response>;
|
|
47
|
+
};
|
|
48
|
+
|
|
40
49
|
type StandardHandlers = {
|
|
41
50
|
GET: (req: Request) => Promise<Response>;
|
|
42
51
|
POST: (req: Request) => Promise<Response>;
|
|
@@ -52,13 +61,14 @@ type HandlersByFramework = {
|
|
|
52
61
|
"react-router": ReactRouterHandlers;
|
|
53
62
|
"next-js": StandardHandlers;
|
|
54
63
|
"svelte-kit": StandardHandlers;
|
|
64
|
+
"solid-start": SolidStartHandlers;
|
|
55
65
|
};
|
|
56
66
|
|
|
57
67
|
type FullstackFrameworks = keyof HandlersByFramework;
|
|
58
68
|
|
|
59
69
|
export interface FragnoInstantiatedFragment<
|
|
60
70
|
TRoutes extends readonly AnyFragnoRouteConfig[] = [],
|
|
61
|
-
TDeps =
|
|
71
|
+
TDeps = {},
|
|
62
72
|
TServices extends Record<string, unknown> = Record<string, unknown>,
|
|
63
73
|
> {
|
|
64
74
|
config: FragnoFragmentSharedConfig<TRoutes>;
|
|
@@ -90,21 +100,13 @@ export type AnyFragnoFragmentSharedConfig = FragnoFragmentSharedConfig<
|
|
|
90
100
|
readonly AnyFragnoRouteConfig[]
|
|
91
101
|
>;
|
|
92
102
|
|
|
93
|
-
interface FragmentDefinition<
|
|
94
|
-
TConfig,
|
|
95
|
-
TDeps = EmptyObject,
|
|
96
|
-
TServices extends Record<string, unknown> = EmptyObject,
|
|
97
|
-
> {
|
|
103
|
+
interface FragmentDefinition<TConfig, TDeps = {}, TServices extends Record<string, unknown> = {}> {
|
|
98
104
|
name: string;
|
|
99
105
|
dependencies?: (config: TConfig) => TDeps;
|
|
100
106
|
services?: (config: TConfig, deps: TDeps) => TServices;
|
|
101
107
|
}
|
|
102
108
|
|
|
103
|
-
export class FragmentBuilder<
|
|
104
|
-
TConfig,
|
|
105
|
-
TDeps = EmptyObject,
|
|
106
|
-
TServices extends Record<string, unknown> = EmptyObject,
|
|
107
|
-
> {
|
|
109
|
+
export class FragmentBuilder<TConfig, TDeps = {}, TServices extends Record<string, unknown> = {}> {
|
|
108
110
|
#definition: FragmentDefinition<TConfig, TDeps, TServices>;
|
|
109
111
|
|
|
110
112
|
constructor(definition: FragmentDefinition<TConfig, TDeps, TServices>) {
|
|
@@ -134,7 +136,6 @@ export class FragmentBuilder<
|
|
|
134
136
|
}
|
|
135
137
|
}
|
|
136
138
|
|
|
137
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
138
139
|
export function defineFragment<TConfig = {}>(name: string): FragmentBuilder<TConfig> {
|
|
139
140
|
return new FragmentBuilder({
|
|
140
141
|
name,
|
|
@@ -210,6 +211,14 @@ export function createFragment<
|
|
|
210
211
|
},
|
|
211
212
|
handlersFor: <T extends FullstackFrameworks>(framework: T): HandlersByFramework[T] => {
|
|
212
213
|
const handler = fragment.handler;
|
|
214
|
+
|
|
215
|
+
// LLMs hallucinate these values sometimes, solution isn't obvious so we throw this error
|
|
216
|
+
// @ts-expect-error TS2367
|
|
217
|
+
if (framework === "h3" || framework === "nuxt") {
|
|
218
|
+
throw new Error(`To get handlers for h3, use the 'fromWebHandler' utility function:
|
|
219
|
+
import { fromWebHandler } from "h3";
|
|
220
|
+
export default fromWebHandler(myFragment().handler);`);
|
|
221
|
+
}
|
|
213
222
|
const allHandlers = {
|
|
214
223
|
astro: { ALL: handler },
|
|
215
224
|
"react-router": {
|
|
@@ -234,6 +243,15 @@ export function createFragment<
|
|
|
234
243
|
HEAD: handler,
|
|
235
244
|
OPTIONS: handler,
|
|
236
245
|
},
|
|
246
|
+
"solid-start": {
|
|
247
|
+
GET: ({ request }: { request: Request }) => handler(request),
|
|
248
|
+
POST: ({ request }: { request: Request }) => handler(request),
|
|
249
|
+
PUT: ({ request }: { request: Request }) => handler(request),
|
|
250
|
+
DELETE: ({ request }: { request: Request }) => handler(request),
|
|
251
|
+
PATCH: ({ request }: { request: Request }) => handler(request),
|
|
252
|
+
HEAD: ({ request }: { request: Request }) => handler(request),
|
|
253
|
+
OPTIONS: ({ request }: { request: Request }) => handler(request),
|
|
254
|
+
},
|
|
237
255
|
} satisfies HandlersByFramework;
|
|
238
256
|
|
|
239
257
|
return allHandlers[framework];
|
|
@@ -45,7 +45,7 @@ describe("Request Middleware", () => {
|
|
|
45
45
|
expect(unauthorizedRes.status).toBe(401);
|
|
46
46
|
const unauthorizedBody = await unauthorizedRes.json();
|
|
47
47
|
expect(unauthorizedBody).toEqual({
|
|
48
|
-
|
|
48
|
+
message: "Unauthorized",
|
|
49
49
|
code: "UNAUTHORIZED",
|
|
50
50
|
});
|
|
51
51
|
|
|
@@ -134,7 +134,7 @@ describe("Request Middleware", () => {
|
|
|
134
134
|
expect(res.status).toBe(403);
|
|
135
135
|
|
|
136
136
|
expect(await res.json()).toEqual({
|
|
137
|
-
|
|
137
|
+
message: "Creating users has been disabled.",
|
|
138
138
|
code: "CREATE_USERS_DISABLED",
|
|
139
139
|
});
|
|
140
140
|
|
|
@@ -439,7 +439,7 @@ describe("Request Middleware", () => {
|
|
|
439
439
|
|
|
440
440
|
const body = await res.json();
|
|
441
441
|
expect(body).toEqual({
|
|
442
|
-
|
|
442
|
+
message: "Request validation failed in middleware",
|
|
443
443
|
code: "MIDDLEWARE_VALIDATION_ERROR",
|
|
444
444
|
});
|
|
445
445
|
});
|
|
@@ -51,16 +51,16 @@ export abstract class OutputContext<const TOutput, const TErrorCode extends stri
|
|
|
51
51
|
headers?: HeadersInit,
|
|
52
52
|
): Response {
|
|
53
53
|
if (typeof initOrStatus === "undefined") {
|
|
54
|
-
return Response.json({
|
|
54
|
+
return Response.json({ message: message, code }, { status: 500, headers });
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
if (typeof initOrStatus === "number") {
|
|
58
|
-
return Response.json({
|
|
58
|
+
return Response.json({ message: message, code }, { status: initOrStatus, headers });
|
|
59
59
|
}
|
|
60
60
|
|
|
61
61
|
const mergedHeaders = mergeHeaders(initOrStatus.headers, headers);
|
|
62
62
|
return Response.json(
|
|
63
|
-
{
|
|
63
|
+
{ message: message, code },
|
|
64
64
|
{ status: initOrStatus.status, headers: mergedHeaders },
|
|
65
65
|
);
|
|
66
66
|
}
|
package/src/api/route.ts
CHANGED
|
@@ -125,14 +125,7 @@ export function defineRoute<
|
|
|
125
125
|
return config;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
export type EmptyObject = {}; //Record<string, never>;
|
|
130
|
-
|
|
131
|
-
export function defineRoutes<
|
|
132
|
-
TConfig = EmptyObject,
|
|
133
|
-
TDeps = EmptyObject,
|
|
134
|
-
TServices = EmptyObject,
|
|
135
|
-
>() {
|
|
128
|
+
export function defineRoutes<TConfig = {}, TDeps = {}, TServices = {}>() {
|
|
136
129
|
return {
|
|
137
130
|
create: <
|
|
138
131
|
const TRoutes extends readonly FragnoRouteConfig<
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { test, expect, describe } from "vitest";
|
|
2
|
-
import { FragnoClientApiError } from "./client-error";
|
|
2
|
+
import { FragnoClientApiError, FragnoClientUnknownApiError } from "./client-error";
|
|
3
3
|
import { FragnoApiError } from "../api/error";
|
|
4
|
+
import { RequestOutputContext } from "../api/request-output-context";
|
|
4
5
|
|
|
5
6
|
describe("Error Conversion", () => {
|
|
6
7
|
test("should convert API error to client error", async () => {
|
|
@@ -12,4 +13,19 @@ describe("Error Conversion", () => {
|
|
|
12
13
|
expect(clientError.code).toBe("API_ERROR");
|
|
13
14
|
expect(clientError.status).toBe(500);
|
|
14
15
|
});
|
|
16
|
+
|
|
17
|
+
test("error() should never result in an unknown error", async () => {
|
|
18
|
+
const ctx = new RequestOutputContext();
|
|
19
|
+
const response = ctx.error({ message: "test", code: "MY_TEST_ERROR" }, { status: 400 });
|
|
20
|
+
|
|
21
|
+
expect(response).toBeInstanceOf(Response);
|
|
22
|
+
expect(response.status).toBe(400);
|
|
23
|
+
|
|
24
|
+
const clientError = await FragnoClientApiError.fromResponse(response);
|
|
25
|
+
expect(clientError).toBeInstanceOf(FragnoClientApiError);
|
|
26
|
+
expect(clientError).not.toBeInstanceOf(FragnoClientUnknownApiError);
|
|
27
|
+
expect(clientError.message).toBe("test");
|
|
28
|
+
expect(clientError.code).toBe("MY_TEST_ERROR");
|
|
29
|
+
expect(clientError.status).toBe(400);
|
|
30
|
+
});
|
|
15
31
|
});
|