@torpor/build 1.0.0 → 1.0.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/dist/{flattenHeaders-C_YYLdOq.mjs → Server-BnjLJleZ.mjs} +2 -12
- package/dist/Server-BnjLJleZ.mjs.map +1 -0
- package/dist/{Server-C_IKav_e.d.mts → Server-Cler2w5O.d.mts} +3 -48
- package/dist/Server-Cler2w5O.d.mts.map +1 -0
- package/dist/ServerEvent-CTx3Q-hk.d.mts +48 -0
- package/dist/ServerEvent-CTx3Q-hk.d.mts.map +1 -0
- package/dist/{Site-CMa153FA.d.mts → Site-CE0_o5Ci.d.mts} +3 -2
- package/dist/Site-CE0_o5Ci.d.mts.map +1 -0
- package/dist/bin/index.js +1 -1
- package/dist/clientEntry.d.mts +1 -0
- package/dist/clientEntry.mjs +59 -0
- package/dist/clientEntry.mjs.map +1 -0
- package/dist/clientEntryDev.d.mts +1 -0
- package/dist/clientEntryDev.mjs +7 -0
- package/dist/clientEntryDev.mjs.map +1 -0
- package/dist/dev.d.mts +3 -0
- package/dist/dev.mjs +3 -0
- package/dist/endpoint-EULRGCKA.mjs +108 -0
- package/dist/endpoint-EULRGCKA.mjs.map +1 -0
- package/dist/flattenHeaders-DM5qsSqO.mjs +13 -0
- package/dist/flattenHeaders-DM5qsSqO.mjs.map +1 -0
- package/dist/index.d.mts +1 -1
- package/dist/load-BntzvmwL.mjs +228 -0
- package/dist/load-BntzvmwL.mjs.map +1 -0
- package/dist/nav.mjs +1 -225
- package/dist/nav.mjs.map +1 -1
- package/dist/openapi.d.mts +1 -1
- package/dist/run.d.mts +1 -1
- package/dist/run.d.mts.map +1 -1
- package/dist/run.mjs +1 -1
- package/dist/{runOpenApi-BUi1RH1l.mjs → runOpenApi-C8eI2Fh0.mjs} +54 -16
- package/dist/runOpenApi-C8eI2Fh0.mjs.map +1 -0
- package/dist/server/Server.d.mts +2 -0
- package/dist/server/Server.mjs +2 -0
- package/dist/server.d.mts +2 -1
- package/dist/server.d.mts.map +1 -1
- package/dist/server.mjs +2 -1
- package/dist/server.mjs.map +1 -1
- package/dist/serverEntry-1qY3wZig.mjs +351 -0
- package/dist/serverEntry-1qY3wZig.mjs.map +1 -0
- package/dist/serverEntry-BAO8m03V.d.mts +6 -0
- package/dist/serverEntry-BAO8m03V.d.mts.map +1 -0
- package/dist/serverEntry.d.mts +2 -0
- package/dist/serverEntry.mjs +2 -0
- package/dist/test.d.mts +2 -2
- package/dist/test.mjs +2 -104
- package/dist/test.mjs.map +1 -1
- package/package.json +9 -8
- package/dist/Server-C_IKav_e.d.mts.map +0 -1
- package/dist/Site-CMa153FA.d.mts.map +0 -1
- package/dist/flattenHeaders-C_YYLdOq.mjs.map +0 -1
- package/dist/runOpenApi-BUi1RH1l.mjs.map +0 -1
- package/src/bin/index.ts +0 -22
- package/src/dev.ts +0 -10
- package/src/form/formDataToRecord.ts +0 -23
- package/src/form/readForm.ts +0 -71
- package/src/form.ts +0 -4
- package/src/index.ts +0 -82
- package/src/nav/api.test-d.ts +0 -79
- package/src/nav/api.ts +0 -104
- package/src/nav/formSubmit.ts +0 -70
- package/src/nav/load.ts +0 -9
- package/src/nav/loadData.ts +0 -134
- package/src/nav/navigate.ts +0 -189
- package/src/nav/reload.ts +0 -9
- package/src/nav/route.ts +0 -38
- package/src/nav.ts +0 -6
- package/src/openapi/docsHtml.ts +0 -25
- package/src/openapi/document.ts +0 -154
- package/src/openapi/plugin.ts +0 -78
- package/src/openapi/types.ts +0 -82
- package/src/openapi.ts +0 -14
- package/src/response/TypedResponse.ts +0 -17
- package/src/response/badRequest.ts +0 -31
- package/src/response/created.ts +0 -31
- package/src/response/forbidden.ts +0 -21
- package/src/response/found.ts +0 -30
- package/src/response/methodNotAllowed.ts +0 -18
- package/src/response/movedPermanently.ts +0 -25
- package/src/response/notFound.ts +0 -24
- package/src/response/notModified.ts +0 -34
- package/src/response/ok.ts +0 -38
- package/src/response/permanentRedirect.ts +0 -30
- package/src/response/response.ts +0 -22
- package/src/response/seeOther.ts +0 -19
- package/src/response/serverError.ts +0 -24
- package/src/response/temporaryRedirect.ts +0 -31
- package/src/response/transfer.ts +0 -14
- package/src/response/unauthorized.ts +0 -21
- package/src/response/unprocessable.ts +0 -30
- package/src/response.ts +0 -37
- package/src/run/depCache.ts +0 -175
- package/src/run/devPlugin.ts +0 -128
- package/src/run/prepareTemplate.ts +0 -42
- package/src/run/run.ts +0 -86
- package/src/run/runBuild.ts +0 -128
- package/src/run/runDev.ts +0 -177
- package/src/run/runOpenApi.ts +0 -52
- package/src/run/runPreview.ts +0 -84
- package/src/run/staleTorpCopies.ts +0 -100
- package/src/run.ts +0 -7
- package/src/schema.ts +0 -7
- package/src/server/CookieHelper.ts +0 -46
- package/src/server/HeaderHelper.ts +0 -25
- package/src/server/Server.ts +0 -168
- package/src/server/ServerEvent.ts +0 -62
- package/src/server/connect/connectMiddleware.ts +0 -85
- package/src/server/connect/flattenHeaders.ts +0 -22
- package/src/server/connect/nodeMessageToNodeResponse.ts +0 -89
- package/src/server/connect/requestToNodeMessage.ts +0 -29
- package/src/server/contentType.ts +0 -85
- package/src/server/types/HttpMethod.ts +0 -12
- package/src/server/types/MiddlewareFunction.ts +0 -22
- package/src/server/types/ServerFunction.ts +0 -7
- package/src/server.ts +0 -7
- package/src/site/Router.ts +0 -175
- package/src/site/Site.ts +0 -388
- package/src/site/checkLayoutSlots.ts +0 -114
- package/src/site/checkRoutes.ts +0 -435
- package/src/site/clientEntry.ts +0 -121
- package/src/site/clientEntryDev.ts +0 -3
- package/src/site/defaultAdapter.ts +0 -11
- package/src/site/layoutSlots.ts +0 -53
- package/src/site/manifest.ts +0 -173
- package/src/site/serverEntry.ts +0 -648
- package/src/state/$page.ts +0 -25
- package/src/state/$serverPage.ts +0 -22
- package/src/state/client.ts +0 -15
- package/src/state.ts +0 -3
- package/src/test/runTest.ts +0 -538
- package/src/test.ts +0 -3
- package/src/types/Adapter.ts +0 -20
- package/src/types/ClientState.ts +0 -8
- package/src/types/InternalState.ts +0 -7
- package/src/types/Jsonify.ts +0 -20
- package/src/types/LayoutHandler.ts +0 -5
- package/src/types/LayoutPath.ts +0 -10
- package/src/types/ManifestRoute.ts +0 -6
- package/src/types/PageData.test-d.ts +0 -136
- package/src/types/PageData.ts +0 -35
- package/src/types/PageEndPoint.ts +0 -55
- package/src/types/PageForm.test-d.ts +0 -78
- package/src/types/PageForm.ts +0 -29
- package/src/types/PageLoadEvent.ts +0 -26
- package/src/types/PageLoadReturn.ts +0 -13
- package/src/types/PageProps.ts +0 -14
- package/src/types/PageServerAction.ts +0 -13
- package/src/types/PageServerEndPoint.test-d.ts +0 -105
- package/src/types/PageServerEndPoint.ts +0 -110
- package/src/types/PageServerLoad.ts +0 -15
- package/src/types/PageState.ts +0 -8
- package/src/types/ParseRouteParams.test-d.ts +0 -84
- package/src/types/ParseRouteParams.ts +0 -64
- package/src/types/Route.ts +0 -30
- package/src/types/RouteHandler.ts +0 -23
- package/src/types/RouteLayoutHandler.ts +0 -5
- package/src/types/RouteMatchResult.ts +0 -7
- package/src/types/RouteType.ts +0 -19
- package/src/types/ServerEndPoint.test-d.ts +0 -77
- package/src/types/ServerEndPoint.ts +0 -169
- package/src/types/ServerHook.ts +0 -18
- package/src/types/ServerLoadEvent.ts +0 -82
- package/src/types/ServerRequest.ts +0 -15
- package/src/types/SitePlugin.ts +0 -24
- package/src/types/StandardSchema.ts +0 -109
- package/src/utils/pathToRegex.ts +0 -17
- package/src/utils/pathTrie.ts +0 -182
- package/src/utils/searchParamsToRecord.ts +0 -18
- package/src/utils/torporPackages.ts +0 -150
- package/src/utils/tsconfigAliases.ts +0 -90
- package/src/validation/ValidationError.ts +0 -18
- package/src/validation/endpoint.ts +0 -83
- package/src/validation/validate.ts +0 -26
- package/src/vite-env.d.ts +0 -2
package/src/form/readForm.ts
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The values read from a form submission, coerced by the spec's default
|
|
3
|
-
* value types: string defaults produce strings, number defaults produce
|
|
4
|
-
* numbers, boolean defaults produce booleans, and array defaults produce
|
|
5
|
-
* arrays of the element type (an empty array default produces strings).
|
|
6
|
-
*/
|
|
7
|
-
export type FormValues<Spec extends Record<string, unknown>> = {
|
|
8
|
-
[K in keyof Spec]: FormValue<Spec[K]>;
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
type FormValue<T> = T extends boolean
|
|
12
|
-
? boolean
|
|
13
|
-
: T extends number
|
|
14
|
-
? number
|
|
15
|
-
: T extends readonly unknown[]
|
|
16
|
-
? [T[number]] extends [never]
|
|
17
|
-
? string[]
|
|
18
|
-
: T[number][]
|
|
19
|
-
: string;
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Reads and coerces the submitted fields of a form action, e.g.
|
|
23
|
-
*
|
|
24
|
-
* ```ts
|
|
25
|
-
* const data = await readForm(request, {
|
|
26
|
-
* title: "",
|
|
27
|
-
* done: false,
|
|
28
|
-
* priority: 0,
|
|
29
|
-
* tags: [],
|
|
30
|
-
* });
|
|
31
|
-
* // data: { title: string; done: boolean; priority: number; tags: string[] }
|
|
32
|
-
* ```
|
|
33
|
-
*
|
|
34
|
-
* The spec object declares the expected fields; each key's default value
|
|
35
|
-
* determines the type and the fallback when the field is missing or can't be
|
|
36
|
-
* coerced. Booleans follow checkbox semantics (unchecked fields are absent,
|
|
37
|
-
* so they come back as `false`).
|
|
38
|
-
*
|
|
39
|
-
* @param request The action event's request, or its FormData directly
|
|
40
|
-
* @param spec An object mapping field names to default values
|
|
41
|
-
* @returns The coerced field values
|
|
42
|
-
*/
|
|
43
|
-
export default async function readForm<Spec extends Record<string, unknown>>(
|
|
44
|
-
request: Request | FormData,
|
|
45
|
-
spec: Spec,
|
|
46
|
-
): Promise<FormValues<Spec>> {
|
|
47
|
-
const formData = request instanceof FormData ? request : await request.formData();
|
|
48
|
-
const result: Record<string, unknown> = {};
|
|
49
|
-
for (const key of Object.keys(spec)) {
|
|
50
|
-
const defaultValue = spec[key];
|
|
51
|
-
// oxlint-disable-next-line typescript/no-base-to-string
|
|
52
|
-
const values = formData.getAll(key).map((v) => v.toString());
|
|
53
|
-
result[key] = Array.isArray(defaultValue)
|
|
54
|
-
? values.map((value) => coerceValue(value, defaultValue[0]))
|
|
55
|
-
: coerceValue(values[0], defaultValue);
|
|
56
|
-
}
|
|
57
|
-
return result as FormValues<Spec>;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function coerceValue(value: string | undefined, defaultValue: unknown): unknown {
|
|
61
|
-
if (typeof defaultValue === "number") {
|
|
62
|
-
const n = value === undefined || value === "" ? NaN : Number(value);
|
|
63
|
-
return Number.isNaN(n) ? defaultValue : n;
|
|
64
|
-
}
|
|
65
|
-
if (typeof defaultValue === "boolean") {
|
|
66
|
-
// Unchecked checkboxes are not submitted at all
|
|
67
|
-
if (value === undefined) return false;
|
|
68
|
-
return !(value === "" || value === "false" || value === "off");
|
|
69
|
-
}
|
|
70
|
-
return value ?? defaultValue ?? "";
|
|
71
|
-
}
|
package/src/form.ts
DELETED
package/src/index.ts
DELETED
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
import Router from "./site/Router";
|
|
2
|
-
import Site from "./site/Site";
|
|
3
|
-
import type Adapter from "./types/Adapter";
|
|
4
|
-
import type { Jsonify } from "./types/Jsonify";
|
|
5
|
-
import type { MergePageData, PageData } from "./types/PageData";
|
|
6
|
-
import type { PageForm } from "./types/PageForm";
|
|
7
|
-
import type PageEndPoint from "./types/PageEndPoint";
|
|
8
|
-
import type PageLoadEvent from "./types/PageLoadEvent";
|
|
9
|
-
import type { PageLoadReturn } from "./types/PageLoadReturn";
|
|
10
|
-
import type PageProps from "./types/PageProps";
|
|
11
|
-
import type {
|
|
12
|
-
ParseRouteParams,
|
|
13
|
-
RouteArgs,
|
|
14
|
-
RouteArgsOf,
|
|
15
|
-
RouteParamsOf,
|
|
16
|
-
} from "./types/ParseRouteParams";
|
|
17
|
-
import type PageServerAction from "./types/PageServerAction";
|
|
18
|
-
import type PageServerEndPoint from "./types/PageServerEndPoint";
|
|
19
|
-
import type {
|
|
20
|
-
ActionBody,
|
|
21
|
-
LoadParams,
|
|
22
|
-
LoadQuery,
|
|
23
|
-
PageServerActionSchemas,
|
|
24
|
-
} from "./types/PageServerEndPoint";
|
|
25
|
-
import type PageServerLoad from "./types/PageServerLoad";
|
|
26
|
-
import type TypedResponse from "./response/TypedResponse";
|
|
27
|
-
import type { UntypedResponse } from "./response/TypedResponse";
|
|
28
|
-
import type ServerEndPoint from "./types/ServerEndPoint";
|
|
29
|
-
import type {
|
|
30
|
-
ParamsOf,
|
|
31
|
-
SchemaBody,
|
|
32
|
-
SchemaQuery,
|
|
33
|
-
ServerEndPointSchemas,
|
|
34
|
-
} from "./types/ServerEndPoint";
|
|
35
|
-
import type ServerHook from "./types/ServerHook";
|
|
36
|
-
import type ServerLoadEvent from "./types/ServerLoadEvent";
|
|
37
|
-
import type { FormDataRecord, QueryRecord } from "./types/ServerLoadEvent";
|
|
38
|
-
import type ServerRequest from "./types/ServerRequest";
|
|
39
|
-
import type { StandardSchemaV1 } from "./types/StandardSchema";
|
|
40
|
-
import type SitePlugin from "./types/SitePlugin";
|
|
41
|
-
|
|
42
|
-
// NOTE: Don't export any general functionality from here -- it may result in
|
|
43
|
-
// Vite errors like `Could not resolve '../pkg' in lightningcss` etc
|
|
44
|
-
|
|
45
|
-
export { Site, Router };
|
|
46
|
-
|
|
47
|
-
export type {
|
|
48
|
-
Adapter,
|
|
49
|
-
PageEndPoint,
|
|
50
|
-
PageServerEndPoint,
|
|
51
|
-
PageServerActionSchemas,
|
|
52
|
-
ActionBody,
|
|
53
|
-
LoadQuery,
|
|
54
|
-
LoadParams,
|
|
55
|
-
PageServerLoad,
|
|
56
|
-
PageServerAction,
|
|
57
|
-
ServerEndPoint,
|
|
58
|
-
ServerEndPointSchemas,
|
|
59
|
-
SchemaBody,
|
|
60
|
-
SchemaQuery,
|
|
61
|
-
ParamsOf,
|
|
62
|
-
ServerRequest,
|
|
63
|
-
ServerHook,
|
|
64
|
-
ServerLoadEvent,
|
|
65
|
-
SitePlugin,
|
|
66
|
-
FormDataRecord,
|
|
67
|
-
QueryRecord,
|
|
68
|
-
StandardSchemaV1,
|
|
69
|
-
PageLoadEvent,
|
|
70
|
-
ParseRouteParams,
|
|
71
|
-
RouteArgs,
|
|
72
|
-
RouteArgsOf,
|
|
73
|
-
RouteParamsOf,
|
|
74
|
-
Jsonify,
|
|
75
|
-
TypedResponse,
|
|
76
|
-
UntypedResponse,
|
|
77
|
-
PageData,
|
|
78
|
-
MergePageData,
|
|
79
|
-
PageForm,
|
|
80
|
-
PageLoadReturn,
|
|
81
|
-
PageProps,
|
|
82
|
-
};
|
package/src/nav/api.test-d.ts
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
import type { ApiMethods } from "./api";
|
|
2
|
-
import makeApi from "./api";
|
|
3
|
-
import ok from "../response/ok";
|
|
4
|
-
import type TypedResponse from "../response/TypedResponse";
|
|
5
|
-
import type ServerLoadEvent from "../types/ServerLoadEvent";
|
|
6
|
-
|
|
7
|
-
// A sample endpoint shape, as inferred from a `+server` default export
|
|
8
|
-
interface TimeEndPoint {
|
|
9
|
-
get: (event: ServerLoadEvent<"/api/time">) => Promise<TypedResponse<{ time: number }>>;
|
|
10
|
-
post: (event: ServerLoadEvent<"/api/time">) => Promise<Response>;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
// An endpoint whose handler annotates an expected request body
|
|
14
|
-
interface CreatePostEndPoint {
|
|
15
|
-
post: (
|
|
16
|
-
event: ServerLoadEvent<"/api/posts", { title: string }>,
|
|
17
|
-
) => Promise<TypedResponse<{ id: number }>>;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
type Equals<A, B> =
|
|
21
|
-
(<T>() => T extends A ? 1 : 2) extends <T>() => T extends B ? 1 : 2 ? true : false;
|
|
22
|
-
type Expect<T extends true> = T;
|
|
23
|
-
|
|
24
|
-
// The get caller resolves to the typed JSON body (or a raw Response)
|
|
25
|
-
export type R01 = Expect<
|
|
26
|
-
Equals<
|
|
27
|
-
ApiMethods<TimeEndPoint>["get"],
|
|
28
|
-
(body?: BodyInit | object, init?: RequestInit) => Promise<{ time: number } | Response>
|
|
29
|
-
>
|
|
30
|
-
>;
|
|
31
|
-
|
|
32
|
-
// Handlers that return a plain Response fall back to unknown
|
|
33
|
-
export type R02 = Expect<
|
|
34
|
-
Equals<
|
|
35
|
-
ApiMethods<TimeEndPoint>["post"],
|
|
36
|
-
(body?: BodyInit | object, init?: RequestInit) => Promise<unknown>
|
|
37
|
-
>
|
|
38
|
-
>;
|
|
39
|
-
|
|
40
|
-
// Only defined handlers are callable
|
|
41
|
-
export type R03 = Expect<Equals<keyof ApiMethods<TimeEndPoint>, "get" | "post">>;
|
|
42
|
-
|
|
43
|
-
// An annotated event body types the client call's body param
|
|
44
|
-
export type R04 = Expect<
|
|
45
|
-
Equals<
|
|
46
|
-
ApiMethods<CreatePostEndPoint>["post"],
|
|
47
|
-
(body?: { title: string }, init?: RequestInit) => Promise<{ id: number } | Response>
|
|
48
|
-
>
|
|
49
|
-
>;
|
|
50
|
-
|
|
51
|
-
// Usage: params are enforced by the route path
|
|
52
|
-
export const timeApi: ApiMethods<TimeEndPoint> = makeApi<"/api/time", TimeEndPoint>("/api/time");
|
|
53
|
-
export const postApi: ApiMethods<TimeEndPoint> = makeApi<"/api/posts/[id]", TimeEndPoint>(
|
|
54
|
-
"/api/posts/[id]",
|
|
55
|
-
{
|
|
56
|
-
id: "5",
|
|
57
|
-
},
|
|
58
|
-
);
|
|
59
|
-
// @ts-expect-error missing params
|
|
60
|
-
void makeApi<"/api/posts/[id]", TimeEndPoint>("/api/posts/[id]");
|
|
61
|
-
// @ts-expect-error params are not accepted for static paths
|
|
62
|
-
void makeApi<"/api/time", TimeEndPoint>("/api/time", {});
|
|
63
|
-
// @ts-expect-error 'nope' is not a param of /api/posts/[id]
|
|
64
|
-
void makeApi<"/api/posts/[id]", TimeEndPoint>("/api/posts/[id]", { nope: "1" });
|
|
65
|
-
|
|
66
|
-
// Usage: an annotated body is checked at the call site
|
|
67
|
-
export const createApi: ApiMethods<CreatePostEndPoint> = makeApi<"/api/posts", CreatePostEndPoint>(
|
|
68
|
-
"/api/posts",
|
|
69
|
-
);
|
|
70
|
-
void createApi.post({ title: "Hello" });
|
|
71
|
-
// @ts-expect-error 'name' is not part of the endpoint's expected body
|
|
72
|
-
void createApi.post({ name: "Hello" });
|
|
73
|
-
// @ts-expect-error 'title' is required
|
|
74
|
-
void createApi.post({});
|
|
75
|
-
|
|
76
|
-
// ok() typing: object bodies are typed, strings are not
|
|
77
|
-
export const typed: TypedResponse<{ time: number }> = ok({ time: 5 });
|
|
78
|
-
export const untypedResponse: Response = ok("plain");
|
|
79
|
-
export const noBodyResponse: Response = ok();
|
package/src/nav/api.ts
DELETED
|
@@ -1,104 +0,0 @@
|
|
|
1
|
-
import type { ExactRouteParams, ParseRouteParams } from "../types/ParseRouteParams";
|
|
2
|
-
import type TypedResponse from "../response/TypedResponse";
|
|
3
|
-
import route from "./route";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* The value a client call resolves to: the endpoint's typed JSON body when it
|
|
7
|
-
* returns a typed response, or the raw Response when the body isn't JSON.
|
|
8
|
-
* Untyped endpoints resolve to `unknown`.
|
|
9
|
-
*/
|
|
10
|
-
type ApiResult<Handler> = Handler extends (event: any) => any
|
|
11
|
-
? Awaited<ReturnType<Handler>> extends TypedResponse<infer Body>
|
|
12
|
-
? Body | Response
|
|
13
|
-
: unknown
|
|
14
|
-
: never;
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* The body a client call accepts: the endpoint event's annotated JSON body
|
|
18
|
-
* type (from `ServerLoadEvent<Route, Body>`), or a loose value when the
|
|
19
|
-
* handler doesn't annotate one.
|
|
20
|
-
*/
|
|
21
|
-
type ApiRequestBody<Handler> = Handler extends (event: infer Event) => any
|
|
22
|
-
? Event extends { json: () => Promise<infer Body> }
|
|
23
|
-
? unknown extends Body
|
|
24
|
-
? BodyInit | object
|
|
25
|
-
: Body
|
|
26
|
-
: BodyInit | object
|
|
27
|
-
: BodyInit | object;
|
|
28
|
-
|
|
29
|
-
/** The callable methods of an endpoint, one per handler it defines. */
|
|
30
|
-
export type ApiMethods<Endpoint extends object> = {
|
|
31
|
-
[Method in string & keyof Endpoint as NonNullable<Endpoint[Method]> extends (event: any) => any
|
|
32
|
-
? Method
|
|
33
|
-
: never]: (
|
|
34
|
-
body?: ApiRequestBody<NonNullable<Endpoint[Method]>>,
|
|
35
|
-
init?: RequestInit,
|
|
36
|
-
) => Promise<ApiResult<NonNullable<Endpoint[Method]>>>;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const METHODS = ["get", "post", "patch", "put", "del", "options", "head"] as const;
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* Creates typed client callers for a `+server` endpoint, e.g.
|
|
43
|
-
*
|
|
44
|
-
* ```ts
|
|
45
|
-
* import type endpoint from "@/routes/api/posts/[id]/+server";
|
|
46
|
-
* const post = makeApi<"/api/posts/[id]", typeof endpoint>("/api/posts/[id]");
|
|
47
|
-
* const data = await post.get(); // typed from the endpoint's responses
|
|
48
|
-
* ```
|
|
49
|
-
*
|
|
50
|
-
* Method names match the endpoint's handlers (`del` for DELETE). Params are
|
|
51
|
-
* filled in and URI-encoded from the route path, and JSON responses are
|
|
52
|
-
* parsed; non-JSON responses resolve to the raw Response. Failed requests
|
|
53
|
-
* (non-2xx) throw.
|
|
54
|
-
*
|
|
55
|
-
* @param path The endpoint's route path
|
|
56
|
-
* @param params The route params, required if the path has dynamic segments
|
|
57
|
-
* @returns An object with one caller per handler defined by the endpoint
|
|
58
|
-
*/
|
|
59
|
-
export default function makeApi<
|
|
60
|
-
Route extends string,
|
|
61
|
-
Endpoint extends object,
|
|
62
|
-
Params extends ParseRouteParams<Route> = ParseRouteParams<Route>,
|
|
63
|
-
>(
|
|
64
|
-
path: Route,
|
|
65
|
-
// NOTE: The rest args conditional must stay INLINE — routing it through a
|
|
66
|
-
// type alias defeats `Params` inference, and excess keys stop being checked
|
|
67
|
-
...args: string extends Route
|
|
68
|
-
? [params?: Record<string, string>]
|
|
69
|
-
: keyof ParseRouteParams<Route> extends never
|
|
70
|
-
? []
|
|
71
|
-
: [params: Params & ExactRouteParams<Params, ParseRouteParams<Route>>]
|
|
72
|
-
): ApiMethods<Endpoint> {
|
|
73
|
-
const url = route(path as string, ...(args as [params?: Record<string, string>]));
|
|
74
|
-
const api: Record<string, (body?: unknown, init?: RequestInit) => Promise<unknown>> = {};
|
|
75
|
-
for (const method of METHODS) {
|
|
76
|
-
api[method] = (body, init) => request(method, url, body, init);
|
|
77
|
-
}
|
|
78
|
-
return api as ApiMethods<Endpoint>;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
async function request(
|
|
82
|
-
method: string,
|
|
83
|
-
url: string,
|
|
84
|
-
body: unknown,
|
|
85
|
-
init?: RequestInit,
|
|
86
|
-
): Promise<unknown> {
|
|
87
|
-
// `del` is the endpoint handler name for DELETE requests
|
|
88
|
-
const httpMethod = method === "del" ? "delete" : method;
|
|
89
|
-
const options: RequestInit = { method: httpMethod.toUpperCase(), ...init };
|
|
90
|
-
if (body !== undefined && options.body === undefined && method !== "get" && method !== "head") {
|
|
91
|
-
if (typeof body === "object") {
|
|
92
|
-
options.headers ??= { "Content-Type": "application/json" };
|
|
93
|
-
options.body = JSON.stringify(body);
|
|
94
|
-
} else {
|
|
95
|
-
options.body = body as BodyInit;
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
const response = await fetch(url, options);
|
|
99
|
-
if (!response.ok) {
|
|
100
|
-
throw new Error(`API ${method.toUpperCase()} ${url} failed: ${response.status}`);
|
|
101
|
-
}
|
|
102
|
-
const contentType = response.headers.get("Content-Type") ?? "";
|
|
103
|
-
return contentType.includes("application/json") ? await response.json() : response;
|
|
104
|
-
}
|
package/src/nav/formSubmit.ts
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
import $page from "../state/$page";
|
|
2
|
-
import load from "./load";
|
|
3
|
-
import reload from "./reload";
|
|
4
|
-
|
|
5
|
-
export default async function formSubmit(e: SubmitEvent): Promise<void> {
|
|
6
|
-
// We'll take it from here
|
|
7
|
-
e.preventDefault();
|
|
8
|
-
|
|
9
|
-
// Reset $page on submit and link navigation
|
|
10
|
-
// TODO: Should we put data in $page.data??
|
|
11
|
-
$page.form = undefined;
|
|
12
|
-
|
|
13
|
-
// Perform a POST fetch to the form's action location
|
|
14
|
-
const form = e.target as HTMLFormElement;
|
|
15
|
-
const button = e.submitter as HTMLButtonElement;
|
|
16
|
-
// HACK: submit buttons have their formAction set to the current page if not
|
|
17
|
-
// set, so we can't just check whether it's empty and fallback to form action
|
|
18
|
-
//const action = button?.formAction ?? form.action;
|
|
19
|
-
let action = form.action || document.URL;
|
|
20
|
-
if (button && button.formAction && button.formAction !== document.URL) {
|
|
21
|
-
action = button.formAction;
|
|
22
|
-
}
|
|
23
|
-
const options = {
|
|
24
|
-
body: new FormData(form),
|
|
25
|
-
method: "POST",
|
|
26
|
-
mode: "cors",
|
|
27
|
-
credentials: "same-origin",
|
|
28
|
-
// Add a special header which tells the server that we want the raw
|
|
29
|
-
// error response (if applicable) rather than the reloaded page that you
|
|
30
|
-
// would get if submitting a page without javascript
|
|
31
|
-
headers: {
|
|
32
|
-
"X-Torpor-Form-Submit": "",
|
|
33
|
-
},
|
|
34
|
-
};
|
|
35
|
-
const response = await fetch(
|
|
36
|
-
action,
|
|
37
|
-
// @ts-ignore -- don't know what it's complaining about here
|
|
38
|
-
options,
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
if (
|
|
42
|
-
response.status === 200 &&
|
|
43
|
-
response.headers.has("Content-Disposition") &&
|
|
44
|
-
/attachment;\s*filename="(.+?)"/.test(response.headers.get("Content-Disposition")!)
|
|
45
|
-
) {
|
|
46
|
-
// TODO: Is this enough to check to see if we have a file that should download?
|
|
47
|
-
// Download the file by creating a dummy <a> and clicking it
|
|
48
|
-
const downloadFileName =
|
|
49
|
-
response.headers.get("Content-Disposition")?.match(/attachment;\s*filename="(.+?)"/)![1] ??
|
|
50
|
-
"file";
|
|
51
|
-
const downloadLink = document.createElement("a");
|
|
52
|
-
downloadLink.href = window.URL.createObjectURL(await response.blob());
|
|
53
|
-
downloadLink.download = downloadFileName;
|
|
54
|
-
downloadLink.click();
|
|
55
|
-
} else if (response.status === 200 && response.headers.has("X-Torpor-Form-Redirect")) {
|
|
56
|
-
// TODO: Should do the correct type of redirect e.g. seeOther etc
|
|
57
|
-
const redirect = response.headers.get("Location")!;
|
|
58
|
-
await load(redirect);
|
|
59
|
-
window.location.href = redirect;
|
|
60
|
-
} else if (
|
|
61
|
-
(response.status >= 200 && response.status <= 299) ||
|
|
62
|
-
(response.status >= 400 && response.status <= 499)
|
|
63
|
-
) {
|
|
64
|
-
// Put the response into `$page.form`, and it will be put into `$props` in reload
|
|
65
|
-
if (response.headers.get("Content-Type") === "application/json") {
|
|
66
|
-
$page.form = await response.json();
|
|
67
|
-
}
|
|
68
|
-
await reload();
|
|
69
|
-
}
|
|
70
|
-
}
|
package/src/nav/load.ts
DELETED
package/src/nav/loadData.ts
DELETED
|
@@ -1,134 +0,0 @@
|
|
|
1
|
-
import client from "../state/client";
|
|
2
|
-
import type LayoutPath from "../types/LayoutPath";
|
|
3
|
-
import type PageEndPoint from "../types/PageEndPoint";
|
|
4
|
-
import type PageServerEndPoint from "../types/PageServerEndPoint";
|
|
5
|
-
import type RouteHandler from "../types/RouteHandler";
|
|
6
|
-
|
|
7
|
-
export default async function loadData(
|
|
8
|
-
handler: RouteHandler,
|
|
9
|
-
params: Record<string, string>,
|
|
10
|
-
path: string,
|
|
11
|
-
query: URLSearchParams,
|
|
12
|
-
newLayoutStack: LayoutPath[],
|
|
13
|
-
clientEndPoint: PageEndPoint | undefined,
|
|
14
|
-
serverEndPoint: PageServerEndPoint | undefined,
|
|
15
|
-
prefetch = false,
|
|
16
|
-
): Promise<Record<string, string> | void> {
|
|
17
|
-
let data = {};
|
|
18
|
-
if (handler.layouts) {
|
|
19
|
-
let layoutStack = client.layoutStack;
|
|
20
|
-
for (let [i, layout] of handler.layouts.entries()) {
|
|
21
|
-
let layoutPath = layout.path;
|
|
22
|
-
for (let key in params) {
|
|
23
|
-
layoutPath = layoutPath.replace(`[${key}]`, params[key]);
|
|
24
|
-
}
|
|
25
|
-
if (layoutStack.at(i)?.path === layoutPath) {
|
|
26
|
-
// We've already loaded this layout, we can just re-use its data and UI
|
|
27
|
-
if (!prefetch) {
|
|
28
|
-
layoutStack[i].reuse = true;
|
|
29
|
-
}
|
|
30
|
-
newLayoutStack[i] = layoutStack[i];
|
|
31
|
-
Object.assign(data, layoutStack[i].data);
|
|
32
|
-
} else {
|
|
33
|
-
const stackLayout = { path: layoutPath, data: {}, reuse: false, slotRegion: null };
|
|
34
|
-
const layoutEndPoint: PageEndPoint | undefined = (await layout.endPoint())?.default;
|
|
35
|
-
const layoutServerEndPoint: PageServerEndPoint | undefined =
|
|
36
|
-
layout.serverEndPoint && (await layout.serverEndPoint())?.default;
|
|
37
|
-
const layoutResponse = await loadClientAndServerData(
|
|
38
|
-
stackLayout.data,
|
|
39
|
-
document.location.origin + layoutPath,
|
|
40
|
-
query,
|
|
41
|
-
params,
|
|
42
|
-
layoutEndPoint,
|
|
43
|
-
layoutServerEndPoint,
|
|
44
|
-
);
|
|
45
|
-
if (layoutResponse?.ok === false) {
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
Object.assign(data, stackLayout.data);
|
|
49
|
-
newLayoutStack.push(stackLayout);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
let endPointResponse = await loadClientAndServerData(
|
|
54
|
-
data,
|
|
55
|
-
document.location.origin + path,
|
|
56
|
-
query,
|
|
57
|
-
params,
|
|
58
|
-
clientEndPoint,
|
|
59
|
-
serverEndPoint,
|
|
60
|
-
);
|
|
61
|
-
if (endPointResponse?.ok === false) {
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
return data;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async function loadClientAndServerData(
|
|
68
|
-
data: Record<string, any>,
|
|
69
|
-
location: string,
|
|
70
|
-
query: URLSearchParams,
|
|
71
|
-
params: Record<string, string>,
|
|
72
|
-
clientEndPoint?: PageEndPoint,
|
|
73
|
-
serverEndPoint?: PageServerEndPoint,
|
|
74
|
-
): Promise<Response | undefined | void> {
|
|
75
|
-
let prefetchedData = client.prefetchedData;
|
|
76
|
-
|
|
77
|
-
if (clientEndPoint?.load) {
|
|
78
|
-
const clientKey = location + (query.size > 0 ? `?${query}` : "");
|
|
79
|
-
if (prefetchedData[clientKey]) {
|
|
80
|
-
Object.assign(data, prefetchedData[clientKey]);
|
|
81
|
-
} else {
|
|
82
|
-
const clientUrl = new URL(document.location.href);
|
|
83
|
-
for (let [name, value] of query) {
|
|
84
|
-
clientUrl.searchParams.append(name, value);
|
|
85
|
-
}
|
|
86
|
-
const clientParams = buildClientParams(clientUrl, params, data);
|
|
87
|
-
const clientResponse = await clientEndPoint.load(clientParams);
|
|
88
|
-
if (clientResponse) {
|
|
89
|
-
if (clientResponse.ok) {
|
|
90
|
-
if (clientResponse.headers.get("Content-Type")?.includes("application/json")) {
|
|
91
|
-
const clientData = await clientResponse.json();
|
|
92
|
-
Object.assign(data, clientData);
|
|
93
|
-
prefetchedData[clientKey] = clientData;
|
|
94
|
-
}
|
|
95
|
-
} else {
|
|
96
|
-
return clientResponse;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
if (serverEndPoint?.load) {
|
|
103
|
-
const serverLocation = location.replace(/\/$/, "") + "/~server";
|
|
104
|
-
const serverKey = serverLocation + (query.size > 0 ? `?${query}` : "");
|
|
105
|
-
if (prefetchedData[serverKey]) {
|
|
106
|
-
Object.assign(data, prefetchedData[serverKey]);
|
|
107
|
-
} else {
|
|
108
|
-
const serverUrl = new URL(serverLocation);
|
|
109
|
-
for (let [name, value] of query) {
|
|
110
|
-
serverUrl.searchParams.append(name, value);
|
|
111
|
-
}
|
|
112
|
-
const serverResponse = await fetch(serverUrl);
|
|
113
|
-
if (serverResponse) {
|
|
114
|
-
if (serverResponse.ok) {
|
|
115
|
-
if (serverResponse.headers.get("Content-Type")?.includes("application/json")) {
|
|
116
|
-
const serverData = await serverResponse.json();
|
|
117
|
-
Object.assign(data, serverData);
|
|
118
|
-
prefetchedData[serverKey] = serverData;
|
|
119
|
-
}
|
|
120
|
-
} else {
|
|
121
|
-
return serverResponse;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
function buildClientParams(url: URL, params: Record<string, string>, data: Record<string, any>) {
|
|
129
|
-
return {
|
|
130
|
-
url,
|
|
131
|
-
params,
|
|
132
|
-
data,
|
|
133
|
-
};
|
|
134
|
-
}
|