@unisian/start 0.0.0-placeholder → 0.0.0-stage

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/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # Temporary Holding Version
2
+
3
+ This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
package/package.json CHANGED
@@ -1,53 +1,6 @@
1
1
  {
2
2
  "name": "@unisian/start",
3
- "version": "0.0.0-placeholder",
4
- "license": "MIT",
5
- "repository": {
6
- "type": "git",
7
- "url": "git+https://github.com/baoagency/unisian-next.git",
8
- "directory": "packages/start"
9
- },
10
- "files": [
11
- "dist"
12
- ],
13
- "type": "module",
14
- "exports": {
15
- ".": "./dist/index.mjs",
16
- "./react": "./dist/client.mjs",
17
- "./serve": "./dist/serve.mjs",
18
- "./middleware": "./dist/middleware.mjs",
19
- "./function-id": "./dist/function-id.mjs"
20
- },
21
- "publishConfig": {
22
- "access": "public"
23
- },
24
- "dependencies": {
25
- "@tanstack/react-form": "1.33.5"
26
- },
27
- "devDependencies": {
28
- "@tanstack/react-query": "5.104.1",
29
- "@tanstack/react-router": "1.170.41",
30
- "@tanstack/react-router-ssr-query": "1.167.3",
31
- "@tanstack/react-start": "1.168.60",
32
- "effect": "4.0.2",
33
- "react": "19.3.0",
34
- "vite": "npm:@voidzero-dev/vite-plus-core@1.1.0",
35
- "@unisian/capability": "0.1.0",
36
- "@unisian/effect-observability": "0.1.0",
37
- "@unisian/shopify-auth": "0.1.0",
38
- "@unisian/shopify-sessions": "0.1.0"
39
- },
40
- "peerDependencies": {
41
- "@tanstack/react-query": "^5.104.1",
42
- "@tanstack/react-router": "^1.170.41",
43
- "@tanstack/react-router-ssr-query": "^1.167.3",
44
- "@tanstack/react-start": "^1.168.60",
45
- "effect": "^4.0.2",
46
- "react": "^19.3.0",
47
- "@unisian/capability": "0.1.0",
48
- "@unisian/shopify-auth": "0.1.0",
49
- "@unisian/effect-observability": "0.1.0",
50
- "@unisian/shopify-sessions": "0.1.0"
51
- },
52
- "scripts": {}
3
+ "version": "0.0.0-stage",
4
+ "stub": true,
5
+ "description": "Temporary package placeholder for staged publishing"
53
6
  }
@@ -1,15 +0,0 @@
1
- import * as Schema from "effect/Schema";
2
- //#region src/browser-error-contract.d.ts
3
- export declare const browserErrorLimits: {
4
- readonly message: 2000;
5
- readonly path: 500;
6
- readonly stack: 8000;
7
- };
8
- export declare const reportBrowserErrorContract: import("@unisian/capability").Contract<"reportBrowserError", Schema.Struct<{
9
- readonly message: Schema.String;
10
- readonly path: Schema.String;
11
- readonly stack: Schema.optional<Schema.String>;
12
- }>, Schema.Struct<{
13
- readonly reference: Schema.String;
14
- }>, Schema.Never>;
15
- //#endregion
@@ -1,20 +0,0 @@
1
- import * as Schema from "effect/Schema";
2
- import { defineContract } from "@unisian/capability";
3
- //#region src/browser-error-contract.ts
4
- const browserErrorLimits = {
5
- message: 2e3,
6
- path: 500,
7
- stack: 8e3
8
- };
9
- const reportBrowserErrorContract = defineContract("reportBrowserError", {
10
- description: "Log an error that broke a page in the merchant's browser, under this request's reference, and return the reference for the error page to show",
11
- failure: Schema.Never,
12
- input: Schema.Struct({
13
- message: Schema.String.check(Schema.isMaxLength(browserErrorLimits.message)),
14
- path: Schema.String.check(Schema.isMaxLength(browserErrorLimits.path)),
15
- stack: Schema.optional(Schema.String.check(Schema.isMaxLength(browserErrorLimits.stack)))
16
- }),
17
- output: Schema.Struct({ reference: Schema.String })
18
- });
19
- //#endregion
20
- export { browserErrorLimits, reportBrowserErrorContract };
@@ -1,8 +0,0 @@
1
- import { reportBrowserErrorContract } from "./browser-error-contract.mjs";
2
- import { command } from "./client.mjs";
3
- import { QueryClient } from "@tanstack/react-query";
4
- //#region src/browser-error-report.d.ts
5
- export type BrowserErrorReport = ReturnType<typeof command<typeof reportBrowserErrorContract>>;
6
- export declare const reportBrowserErrors: (queryClient: QueryClient, report: BrowserErrorReport) => (cause: unknown) => void;
7
- export declare const useBrowserErrorReference: (report: BrowserErrorReport, cause: unknown) => string | undefined;
8
- //#endregion
@@ -1,29 +0,0 @@
1
- import { caughtError, decodeReport } from "./report.mjs";
2
- import { browserErrorLimits, reportBrowserErrorContract } from "./browser-error-contract.mjs";
3
- import * as Schema from "effect/Schema";
4
- import { MutationObserver, useMutationState } from "@tanstack/react-query";
5
- //#region src/browser-error-report.ts
6
- const reportedMessage = (cause) => caughtError(cause).message.slice(0, browserErrorLimits.message);
7
- const reportBrowserErrors = (queryClient, report) => (cause) => {
8
- if (decodeReport(cause).reference !== "none") return;
9
- new MutationObserver(queryClient, report).mutate({
10
- message: reportedMessage(cause),
11
- path: globalThis.location.pathname.slice(0, browserErrorLimits.path),
12
- stack: caughtError(cause).stack?.slice(0, browserErrorLimits.stack)
13
- }).catch(() => null);
14
- };
15
- const isReportInput = Schema.is(reportBrowserErrorContract.input);
16
- const isReportOutput = Schema.is(reportBrowserErrorContract.output);
17
- const useBrowserErrorReference = (report, cause) => {
18
- const message = reportedMessage(cause);
19
- return useMutationState({
20
- filters: {
21
- mutationKey: report.mutationKey,
22
- predicate: ({ state }) => isReportInput(state.variables) && state.variables.message === message,
23
- status: "success"
24
- },
25
- select: ({ state }) => isReportOutput(state.data) ? state.data.reference : void 0
26
- }).at(-1);
27
- };
28
- //#endregion
29
- export { reportBrowserErrors, useBrowserErrorReference };
package/dist/client.d.mts DELETED
@@ -1,31 +0,0 @@
1
- import { CapabilityFailureError } from "./failure.mjs";
2
- import { BrowserErrorReport, reportBrowserErrors, useBrowserErrorReference } from "./browser-error-report.mjs";
3
- import { useCommandForm } from "./command-form.mjs";
4
- import { AppRouterOptions, createAppRouter } from "./router.mjs";
5
- import * as Schema from "effect/Schema";
6
- import * as Option from "effect/Option";
7
- import { AnyContract, FailureOf, InputOf, OutputOf } from "@unisian/capability";
8
- import { QueryClient, QueryKey, UseSuspenseQueryOptions } from "@tanstack/react-query";
9
- //#region src/client.d.ts
10
- export type ServerFn<ContractType extends AnyContract> = (options: {
11
- readonly data: InputOf<ContractType>["Encoded"];
12
- readonly headers?: Record<string, string>;
13
- }) => Promise<Schema.Json>;
14
- export interface PerCall {
15
- readonly headers?: Record<string, string> | undefined;
16
- readonly key?: Schema.Json | undefined;
17
- }
18
- export declare const query: <ContractType extends AnyContract>(contract: ContractType, serverFn: ServerFn<ContractType>) => (input: InputOf<ContractType>["Type"], perCall?: PerCall) => import("@tanstack/react-query").OmitKeyof<import("@tanstack/react-query").UseQueryOptions<Schema.Json, CapabilityFailureError<FailureOf<ContractType>["Type"]>, OutputOf<ContractType>["Type"], readonly [ContractType["name"], InputOf<ContractType>["Encoded"], ...Schema.Json[]]>, "queryFn"> & {
19
- queryFn?: import("@tanstack/react-query").QueryFunction<Schema.Json, readonly [ContractType["name"], InputOf<ContractType>["Encoded"], ...Schema.Json[]], never>;
20
- } & import("@tanstack/react-query").QueryKeyWithDataTag<readonly [ContractType["name"], InputOf<ContractType>["Encoded"], ...Schema.Json[]], Schema.Json, CapabilityFailureError<FailureOf<ContractType>["Type"]>>;
21
- export declare const cachedData: <Output>(client: QueryClient, options: {
22
- readonly queryKey: QueryKey;
23
- readonly select?: ((encoded: Schema.Json) => Output) | undefined;
24
- }) => Output | undefined;
25
- export declare const cachedQuery: <QueryFnData, QueryError, Data, Key extends QueryKey>(client: QueryClient, options: UseSuspenseQueryOptions<QueryFnData, QueryError, Data, Key>) => Promise<Option.Option<Data>>;
26
- export interface CommandOptions extends PerCall {
27
- readonly invalidates: readonly AnyContract[];
28
- }
29
- export declare const command: <ContractType extends AnyContract>(contract: ContractType, serverFn: ServerFn<ContractType>, options: CommandOptions) => import("@tanstack/react-query").WithRequired<import("@tanstack/react-query").UseMutationOptions<OutputOf<ContractType>["Type"], CapabilityFailureError<FailureOf<ContractType>["Type"]>, InputOf<ContractType>["Type"], unknown>, "mutationKey">;
30
- //#endregion
31
- export { type AppRouterOptions, type BrowserErrorReport, createAppRouter, reportBrowserErrors, useBrowserErrorReference, useCommandForm };
package/dist/client.mjs DELETED
@@ -1,63 +0,0 @@
1
- import { CapabilityFailureError } from "./failure.mjs";
2
- import { wireFor } from "./wire.mjs";
3
- import { reportBrowserErrors, useBrowserErrorReference } from "./browser-error-report.mjs";
4
- import { useCommandForm } from "./command-form.mjs";
5
- import { createAppRouter } from "./router.mjs";
6
- import * as Schema from "effect/Schema";
7
- import * as Option from "effect/Option";
8
- import { environmentManager, mutationOptions, queryOptions } from "@tanstack/react-query";
9
- //#region src/client.ts
10
- const transportRetries = 2;
11
- const keyFor = (head, { key }) => key === void 0 ? head : [...head, key];
12
- const callerFor = (contract, serverFn) => {
13
- const encodeInput = Schema.encodeSync(contract.input);
14
- const decodeOutput = Schema.decodeUnknownSync(Schema.toCodecJson(contract.output));
15
- const { decode } = wireFor(contract.failure);
16
- return {
17
- call: (data, { headers }) => serverFn(headers === void 0 ? { data } : {
18
- data,
19
- headers
20
- }).then(decode),
21
- decodeOutput,
22
- encodeInput
23
- };
24
- };
25
- const query = (contract, serverFn) => {
26
- const { encodeInput, decodeOutput, call } = callerFor(contract, serverFn);
27
- return (input, perCall = {}) => {
28
- const data = encodeInput(input);
29
- return queryOptions({
30
- queryFn: () => call(data, perCall),
31
- queryKey: keyFor([contract.name, data], perCall),
32
- retry: (failureCount, error) => !(error instanceof CapabilityFailureError) && failureCount < (environmentManager.isServer() ? 0 : transportRetries),
33
- select: decodeOutput
34
- });
35
- };
36
- };
37
- const cachedData = (client, options) => {
38
- const encoded = client.getQueryData(options.queryKey);
39
- return encoded === void 0 ? void 0 : options.select?.(encoded);
40
- };
41
- const cachedQuery = (client, options) => {
42
- const { queryFn } = options;
43
- return queryFn === void 0 ? Promise.resolve(Option.none()) : client.query({
44
- ...options,
45
- queryFn,
46
- staleTime: "static"
47
- }).then(Option.some);
48
- };
49
- const command = (contract, serverFn, options) => {
50
- const { encodeInput, decodeOutput, call } = callerFor(contract, serverFn);
51
- return mutationOptions({
52
- mutationFn: async (input, { client }) => {
53
- try {
54
- return decodeOutput(await call(encodeInput(input), options));
55
- } finally {
56
- await Promise.all(options.invalidates.map(({ name }) => client.invalidateQueries({ queryKey: [name] })));
57
- }
58
- },
59
- mutationKey: keyFor([contract.name], options)
60
- });
61
- };
62
- //#endregion
63
- export { cachedData, cachedQuery, command, createAppRouter, query, reportBrowserErrors, useBrowserErrorReference, useCommandForm };
@@ -1,36 +0,0 @@
1
- import { CapabilityFailureError } from "./failure.mjs";
2
- import { InputSchema } from "@unisian/capability";
3
- import { UseMutationOptions } from "@tanstack/react-query";
4
- //#region src/command-form.d.ts
5
- interface FormMessage {
6
- readonly message: string;
7
- }
8
- interface SavingForm<Values> {
9
- readonly fieldInfo: object;
10
- readonly reset: (values: Values) => void;
11
- setErrorMap(errorMap: {
12
- readonly onSubmit: {
13
- readonly fields: Record<string, FormMessage>;
14
- readonly form: readonly FormMessage[] | undefined;
15
- };
16
- }): void;
17
- }
18
- export declare const useCommandForm: <Input extends InputSchema, Failure>({ command, contract, defaultValues, onSaved }: {
19
- readonly command: UseMutationOptions<Input["Type"], CapabilityFailureError<Failure>, Input["Type"]>;
20
- readonly contract: {
21
- readonly input: Input;
22
- };
23
- readonly defaultValues: Input["Type"];
24
- readonly onSaved?: (output: Input["Type"]) => void;
25
- }) => {
26
- defaultValues: Input["Encoded"];
27
- onSubmit: ({ formApi, value }: {
28
- readonly formApi: SavingForm<Input["Encoded"]>;
29
- readonly value: Input["Encoded"];
30
- }) => Promise<void>;
31
- validationLogic: import("@tanstack/react-form").ValidationLogicFn;
32
- validators: {
33
- onDynamic: import("effect/StandardSchema").StandardSchemaV1<Input["Encoded"], Input["Type"]> & Input;
34
- };
35
- };
36
- //#endregion
@@ -1,61 +0,0 @@
1
- import { CapabilityFailureError, isFieldIssues, isMerchantFacing } from "./failure.mjs";
2
- import { input } from "./input.mjs";
3
- import * as Schema from "effect/Schema";
4
- import { useMutation } from "@tanstack/react-query";
5
- import { revalidateLogic } from "@tanstack/react-form";
6
- //#region src/command-form.ts
7
- const issuesIn = (cause) => {
8
- if (!(cause instanceof CapabilityFailureError)) return [];
9
- const failure = cause.failure;
10
- return [...isMerchantFacing(failure) ? [{
11
- message: failure.merchantMessage,
12
- path: []
13
- }] : [], ...isFieldIssues(failure) ? failure.fieldIssues : []];
14
- };
15
- const fieldName = (path) => path.map((segment, index) => {
16
- if (/^\d+$/u.test(segment)) return `[${segment}]`;
17
- return index === 0 ? segment : `.${segment}`;
18
- }).join("");
19
- const showOnForm = (form, cause) => {
20
- const issues = issuesIn(cause).map((issue) => ({
21
- ...issue,
22
- name: fieldName(issue.path)
23
- }));
24
- if (issues.length === 0) return false;
25
- const isField = (name) => Object.hasOwn(form.fieldInfo, name);
26
- const fields = Object.fromEntries(issues.filter(({ name }) => isField(name)).toReversed().map(({ message, name }) => [name, { message }]));
27
- const formIssues = issues.filter(({ name }) => !isField(name)).map(({ message }) => ({ message }));
28
- form.setErrorMap({ onSubmit: {
29
- fields,
30
- form: formIssues.length === 0 ? void 0 : formIssues
31
- } });
32
- return true;
33
- };
34
- const commandFormOptions = (contract, save, defaultValues, onSaved) => {
35
- const encodeSync = Schema.encodeSync(contract.input);
36
- const encode = (values) => encodeSync(values, { disableChecks: true });
37
- const decode = Schema.decodeUnknownSync(contract.input);
38
- return {
39
- defaultValues: encode(defaultValues),
40
- onSubmit: ({ formApi, value }) => save(decode(value)).then((output) => {
41
- formApi.reset(encode(output));
42
- onSaved?.(output);
43
- }, (error) => {
44
- showOnForm(formApi, error);
45
- }),
46
- validationLogic: revalidateLogic({
47
- mode: "submit",
48
- modeAfterSubmission: "change"
49
- }),
50
- validators: { onDynamic: input(contract) }
51
- };
52
- };
53
- const useCommandForm = ({ command, contract, defaultValues, onSaved }) => {
54
- const { mutateAsync } = useMutation({
55
- ...command,
56
- throwOnError: (error) => issuesIn(error).length === 0
57
- });
58
- return commandFormOptions(contract, mutateAsync, defaultValues, onSaved);
59
- };
60
- //#endregion
61
- export { commandFormOptions, showOnForm, useCommandForm };
@@ -1,33 +0,0 @@
1
- import { ErrorReport } from "./report.mjs";
2
- import * as Schema from "effect/Schema";
3
- //#region src/failure.d.ts
4
- export declare class CapabilityFailureError<Failure = unknown> extends Error {
5
- readonly name = "CapabilityFailureError";
6
- readonly report: ErrorReport;
7
- readonly failure: Failure | undefined;
8
- constructor(report: ErrorReport, failure: Failure | undefined);
9
- }
10
- export declare const isFailure: <Failure extends {
11
- readonly _tag: string;
12
- }, const Tag extends Failure["_tag"]>(error: CapabilityFailureError<Failure> | Error | null | undefined, tag: Tag) => error is CapabilityFailureError<Extract<Failure, {
13
- readonly _tag: Tag;
14
- }>>;
15
- export declare const MerchantFacing: Schema.Struct<{
16
- readonly merchantMessage: Schema.String;
17
- }>;
18
- export declare const isMerchantFacing: <I>(input: I) => input is I & {
19
- readonly merchantMessage: string;
20
- };
21
- export declare const FieldIssues: Schema.Struct<{
22
- readonly fieldIssues: Schema.$Array<Schema.Struct<{
23
- readonly message: Schema.String;
24
- readonly path: Schema.$Array<Schema.String>;
25
- }>>;
26
- }>;
27
- export declare const isFieldIssues: <I>(input: I) => input is I & {
28
- readonly fieldIssues: readonly {
29
- readonly message: string;
30
- readonly path: readonly string[];
31
- }[];
32
- };
33
- //#endregion
package/dist/failure.mjs DELETED
@@ -1,24 +0,0 @@
1
- import { encodeReport } from "./report.mjs";
2
- import * as Predicate from "effect/Predicate";
3
- import * as Schema from "effect/Schema";
4
- //#region src/failure.ts
5
- var CapabilityFailureError = class extends Error {
6
- name = "CapabilityFailureError";
7
- report;
8
- failure;
9
- constructor(report, failure) {
10
- super(encodeReport(report));
11
- this.report = report;
12
- this.failure = failure;
13
- }
14
- };
15
- const isFailure = (error, tag) => error instanceof CapabilityFailureError && Predicate.isTagged(error.failure, tag);
16
- const MerchantFacing = Schema.Struct({ merchantMessage: Schema.String });
17
- const isMerchantFacing = Schema.is(MerchantFacing);
18
- const FieldIssues = Schema.Struct({ fieldIssues: Schema.Array(Schema.Struct({
19
- message: Schema.String,
20
- path: Schema.Array(Schema.String)
21
- })) });
22
- const isFieldIssues = Schema.is(FieldIssues);
23
- //#endregion
24
- export { CapabilityFailureError, FieldIssues, MerchantFacing, isFailure, isFieldIssues, isMerchantFacing };
@@ -1,7 +0,0 @@
1
- //#region src/function-id.d.ts
2
- export declare const serverFnNameFor: (contractName: string) => string;
3
- export declare const functionIds: () => ({ filename, functionName }: {
4
- filename: string;
5
- functionName: string;
6
- }) => string;
7
- //#endregion
@@ -1,18 +0,0 @@
1
- //#region src/function-id.ts
2
- const SERVER_FN_SUFFIX = "Fn";
3
- const HANDLER_SUFFIX = `${SERVER_FN_SUFFIX}_createServerFn_handler`;
4
- const serverFnNameFor = (contractName) => `${contractName}${SERVER_FN_SUFFIX}`;
5
- const functionIds = () => {
6
- const sources = /* @__PURE__ */ new Map();
7
- return ({ filename, functionName }) => {
8
- const source = `${filename} ${functionName}`;
9
- if (!functionName.endsWith(HANDLER_SUFFIX)) throw new Error(`${source}: name a server function <contract name>${SERVER_FN_SUFFIX}, so its URL names the contract`);
10
- const id = functionName.slice(0, -HANDLER_SUFFIX.length);
11
- const claimed = sources.get(id);
12
- if (claimed !== void 0 && claimed !== source) throw new Error(`${source} and ${claimed} both serve ${id}; declare its server function once and import it`);
13
- sources.set(id, source);
14
- return id;
15
- };
16
- };
17
- //#endregion
18
- export { functionIds, serverFnNameFor };
package/dist/index.d.mts DELETED
@@ -1,5 +0,0 @@
1
- import { ErrorReport, PUBLIC_ERROR_MESSAGE, decodeReport, encodeReport, errorPageHtml, errorReport } from "./report.mjs";
2
- import { CapabilityFailureError, FieldIssues, MerchantFacing, isFailure, isFieldIssues, isMerchantFacing } from "./failure.mjs";
3
- import { browserErrorLimits, reportBrowserErrorContract } from "./browser-error-contract.mjs";
4
- import { input } from "./input.mjs";
5
- export { CapabilityFailureError, ErrorReport, FieldIssues, MerchantFacing, PUBLIC_ERROR_MESSAGE, browserErrorLimits, decodeReport, encodeReport, errorPageHtml, errorReport, input, isFailure, isFieldIssues, isMerchantFacing, reportBrowserErrorContract };
package/dist/index.mjs DELETED
@@ -1,5 +0,0 @@
1
- import { ErrorReport, PUBLIC_ERROR_MESSAGE, decodeReport, encodeReport, errorPageHtml, errorReport } from "./report.mjs";
2
- import { CapabilityFailureError, FieldIssues, MerchantFacing, isFailure, isFieldIssues, isMerchantFacing } from "./failure.mjs";
3
- import { input } from "./input.mjs";
4
- import { browserErrorLimits, reportBrowserErrorContract } from "./browser-error-contract.mjs";
5
- export { CapabilityFailureError, ErrorReport, FieldIssues, MerchantFacing, PUBLIC_ERROR_MESSAGE, browserErrorLimits, decodeReport, encodeReport, errorPageHtml, errorReport, input, isFailure, isFieldIssues, isMerchantFacing, reportBrowserErrorContract };
package/dist/input.d.mts DELETED
@@ -1,6 +0,0 @@
1
- import { InputSchema } from "@unisian/capability";
2
- //#region src/input.d.ts
3
- export declare const input: <Input extends InputSchema>(contract: {
4
- readonly input: Input;
5
- }) => import("effect/StandardSchema").StandardSchemaV1<Input["Encoded"], Input["Type"]> & Input;
6
- //#endregion
package/dist/input.mjs DELETED
@@ -1,5 +0,0 @@
1
- import * as Schema from "effect/Schema";
2
- //#region src/input.ts
3
- const input = (contract) => Schema.toStandardSchemaV1(contract.input);
4
- //#endregion
5
- export { input };
@@ -1,14 +0,0 @@
1
- import * as Cause from "effect/Cause";
2
- import { RequestLogTags, RunTags } from "@unisian/effect-observability";
3
- import * as Effect from "effect/Effect";
4
- import { RequestServerFn } from "@tanstack/react-start";
5
- //#region src/middleware.d.ts
6
- export interface RequestMiddlewareAnswers<Added, E> {
7
- readonly refuse: (request: Request, cause: Cause.Cause<E>, log: RequestLogTags) => Response;
8
- readonly answer?: (response: Response, added: Added) => Response;
9
- }
10
- export declare const respond: <E>(name: string, tags: RunTags, refuse: (cause: Cause.Cause<E>) => Response) => (effect: Effect.Effect<Response, E>) => Promise<Response>;
11
- export declare const requestMiddleware: <Added extends object, E>(name: string, run: (request: Request, pathname: string) => Effect.Effect<Added, E>, answers: RequestMiddlewareAnswers<Added, E>) => RequestServerFn<object, undefined, Added & {
12
- readonly log: RequestLogTags;
13
- }>;
14
- //#endregion
@@ -1,24 +0,0 @@
1
- import * as Exit from "effect/Exit";
2
- import { requestLogTags, runPromiseExit } from "@unisian/effect-observability";
3
- //#region src/middleware.ts
4
- const isSettled = (effect) => Exit.isExit(effect);
5
- const settle = (name, tags, effect) => isSettled(effect) ? Promise.resolve(effect) : effect.pipe(runPromiseExit(name, tags));
6
- const respond = (name, tags, refuse) => async (effect) => {
7
- const exit = await settle(name, tags, effect);
8
- return Exit.isSuccess(exit) ? exit.value : refuse(exit.cause);
9
- };
10
- const requestMiddleware = (name, run, answers) => async ({ request, pathname, next: route }) => {
11
- const log = requestLogTags(request);
12
- const exit = await settle(name, log, run(request, pathname));
13
- if (Exit.isFailure(exit)) return answers.refuse(request, exit.cause, log);
14
- const result = await route({ context: {
15
- ...exit.value,
16
- log
17
- } });
18
- return answers.answer === void 0 ? result : {
19
- ...result,
20
- response: answers.answer(result.response, exit.value)
21
- };
22
- };
23
- //#endregion
24
- export { requestMiddleware, respond };
@@ -1,10 +0,0 @@
1
- import { RequestReference } from "@unisian/effect-observability";
2
- //#region src/report-browser-error.d.ts
3
- export declare const reportBrowserError: import("@unisian/capability").Capability<import("@unisian/capability").Contract<"reportBrowserError", import("effect/Schema").Struct<{
4
- readonly message: import("effect/Schema").String;
5
- readonly path: import("effect/Schema").String;
6
- readonly stack: import("effect/Schema").optional<import("effect/Schema").String>;
7
- }>, import("effect/Schema").Struct<{
8
- readonly reference: import("effect/Schema").String;
9
- }>, import("effect/Schema").Never>, RequestReference>;
10
- //#endregion
@@ -1,15 +0,0 @@
1
- import { reportBrowserErrorContract } from "./browser-error-contract.mjs";
2
- import { implement } from "@unisian/capability";
3
- import { RequestReference } from "@unisian/effect-observability";
4
- import * as Effect from "effect/Effect";
5
- //#region src/report-browser-error.ts
6
- const reportBrowserError = implement(reportBrowserErrorContract, ({ message, path, stack }) => Effect.gen(function* logBrowserError() {
7
- yield* Effect.logError("browser error", {
8
- browserMessage: message,
9
- browserPath: path,
10
- browserStack: stack ?? null
11
- });
12
- return yield* RequestReference;
13
- }));
14
- //#endregion
15
- export { reportBrowserError };
package/dist/report.d.mts DELETED
@@ -1,15 +0,0 @@
1
- import * as Schema from "effect/Schema";
2
- import * as Cause from "effect/Cause";
3
- //#region src/report.d.ts
4
- export declare const PUBLIC_ERROR_MESSAGE = "Something went wrong loading this app. Try again, and if it keeps happening, quote this reference.";
5
- export declare const ErrorReport: Schema.Struct<{
6
- readonly diagnostic: Schema.optional<Schema.String>;
7
- readonly merchantMessage: Schema.optional<Schema.String>;
8
- readonly reference: Schema.String;
9
- }>;
10
- export type ErrorReport = typeof ErrorReport.Type;
11
- export declare const errorReport: (reference: string, cause: Cause.Cause<unknown>, merchantMessage?: string) => ErrorReport;
12
- export declare const encodeReport: (report: ErrorReport) => string;
13
- export declare const decodeReport: (cause: unknown) => ErrorReport;
14
- export declare const errorPageHtml: (report: ErrorReport) => string;
15
- //#endregion
package/dist/report.mjs DELETED
@@ -1,31 +0,0 @@
1
- import * as Schema from "effect/Schema";
2
- import * as Cause from "effect/Cause";
3
- import * as Option from "effect/Option";
4
- //#region src/report.ts
5
- const PUBLIC_ERROR_MESSAGE = "Something went wrong loading this app. Try again, and if it keeps happening, quote this reference.";
6
- const ErrorReport = Schema.Struct({
7
- diagnostic: Schema.optional(Schema.String),
8
- merchantMessage: Schema.optional(Schema.String),
9
- reference: Schema.String
10
- });
11
- const REPORT_PREFIX = "error-report:";
12
- const decodeReportJson = Schema.decodeUnknownOption(Schema.fromJsonString(ErrorReport));
13
- const errorReport = (reference, cause, merchantMessage) => ({
14
- diagnostic: import.meta.env.DEV ? Cause.pretty(cause) : void 0,
15
- merchantMessage,
16
- reference
17
- });
18
- const encodeReport = (report) => `${REPORT_PREFIX}${JSON.stringify(report)}`;
19
- const caughtError = (cause) => cause instanceof Error ? cause : new Error(String(cause));
20
- const decodeReport = (cause) => {
21
- const { message } = caughtError(cause);
22
- if (!message.startsWith(REPORT_PREFIX)) return import.meta.env.DEV ? {
23
- diagnostic: message,
24
- reference: "none"
25
- } : { reference: "none" };
26
- return Option.getOrElse(decodeReportJson(message.slice(13)), () => ({ reference: "none" }));
27
- };
28
- const escapeHtml = (text) => text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;");
29
- const errorPageHtml = (report) => `<!doctype html><html lang="en"><head><meta charset="utf-8"><title>Something went wrong</title></head><body><main><h1>Something went wrong</h1><p>${escapeHtml(PUBLIC_ERROR_MESSAGE)}</p><p>Reference: <code>${escapeHtml(report.reference)}</code></p>${report.diagnostic === void 0 ? "" : `<pre>${escapeHtml(report.diagnostic)}</pre>`}</main></body></html>`;
30
- //#endregion
31
- export { ErrorReport, PUBLIC_ERROR_MESSAGE, caughtError, decodeReport, encodeReport, errorPageHtml, errorReport };
package/dist/router.d.mts DELETED
@@ -1,10 +0,0 @@
1
- import { BrowserErrorReport } from "./browser-error-report.mjs";
2
- import { AnyRoute, Router, RouterOptions } from "@tanstack/react-router";
3
- //#region src/router.d.ts
4
- type PageComponents = Required<Pick<RouterOptions<AnyRoute, "never">, "defaultErrorComponent">> & Pick<RouterOptions<AnyRoute, "never">, "defaultNotFoundComponent" | "defaultPendingComponent">;
5
- export type AppRouterOptions<RouteTree extends AnyRoute> = PageComponents & {
6
- readonly routeTree: RouteTree;
7
- readonly browserErrorReport: BrowserErrorReport;
8
- };
9
- export declare const createAppRouter: <RouteTree extends AnyRoute>({ routeTree, browserErrorReport, ...components }: AppRouterOptions<RouteTree>) => Router<RouteTree>;
10
- //#endregion
package/dist/router.mjs DELETED
@@ -1,27 +0,0 @@
1
- import { reportBrowserErrors } from "./browser-error-report.mjs";
2
- import { QueryClient } from "@tanstack/react-query";
3
- import { createRouter } from "@tanstack/react-router";
4
- import { setupRouterSsrQueryIntegration } from "@tanstack/react-router-ssr-query";
5
- //#region src/router.ts
6
- const createAppRouter = ({ routeTree, browserErrorReport, ...components }) => {
7
- const queryClient = new QueryClient({ defaultOptions: { queries: {
8
- gcTime: 6e5,
9
- refetchOnWindowFocus: false,
10
- staleTime: 3e5
11
- } } });
12
- const router = createRouter({
13
- ...components,
14
- context: { queryClient },
15
- defaultOnCatch: reportBrowserErrors(queryClient, browserErrorReport),
16
- defaultPreload: "viewport",
17
- routeTree,
18
- scrollRestoration: true
19
- });
20
- setupRouterSsrQueryIntegration({
21
- queryClient,
22
- router
23
- });
24
- return router;
25
- };
26
- //#endregion
27
- export { createAppRouter };
package/dist/serve.d.mts DELETED
@@ -1,31 +0,0 @@
1
- import { reportBrowserError } from "./report-browser-error.mjs";
2
- import * as Schema from "effect/Schema";
3
- import { AnyContract, Capability, InputOf } from "@unisian/capability";
4
- import { RequestLogTags, RequestReference } from "@unisian/effect-observability";
5
- import { AuthenticatedShop, CurrentShop, ShopifyAdmin, ShopifyApp, ShopifyLayerConfig } from "@unisian/shopify-auth";
6
- import * as Layer from "effect/Layer";
7
- import { ShopDomain } from "@unisian/shopify-sessions";
8
- import { HttpClient } from "effect/http";
9
- //#region src/serve.d.ts
10
- export interface ServeContext {
11
- readonly shopify: AuthenticatedShop;
12
- readonly log: RequestLogTags;
13
- }
14
- export interface ServeConfig<Services, ServicesError> extends ShopifyLayerConfig {
15
- readonly services?: Layer.Layer<Services, ServicesError> | undefined;
16
- }
17
- export type Provided<Services> = CurrentShop | ShopifyAdmin | ShopifyApp | HttpClient.HttpClient | RequestReference | Services;
18
- export interface ServerFnCall<ContractType extends AnyContract, Context> {
19
- readonly data: InputOf<ContractType>["Type"];
20
- readonly context: Context;
21
- readonly serverFnMeta: {
22
- readonly name: string;
23
- };
24
- }
25
- export declare const serveWith: <Context, Services, ServicesError>(callerOf: (context: Context) => {
26
- readonly shop: ShopDomain;
27
- readonly log: RequestLogTags;
28
- }, layerFor: (context: Context) => Layer.Layer<Services, ServicesError>) => <ContractType extends AnyContract, Requirements extends Services | CurrentShop | RequestReference>(capability: Capability<ContractType, Requirements>) => ({ data, context, serverFnMeta }: ServerFnCall<ContractType, Context>) => Promise<Schema.Json>;
29
- export declare const makeServe: <Services = never, ServicesError = never>(config: () => ServeConfig<Services, ServicesError>) => <ContractType extends AnyContract, Requirements extends (HttpClient.HttpClient | ShopifyAdmin | ShopifyApp | Exclude<Services, CurrentShop | RequestReference>) | CurrentShop | RequestReference>(capability: Capability<ContractType, Requirements>) => ({ data, context, serverFnMeta }: ServerFnCall<ContractType, ServeContext>) => Promise<Schema.Json>;
30
- //#endregion
31
- export { reportBrowserError };
package/dist/serve.mjs DELETED
@@ -1,34 +0,0 @@
1
- import { wireFor } from "./wire.mjs";
2
- import { serverFnNameFor } from "./function-id.mjs";
3
- import { reportBrowserError } from "./report-browser-error.mjs";
4
- import * as Schema from "effect/Schema";
5
- import { RequestReference, logFailure, runPromiseExit } from "@unisian/effect-observability";
6
- import { CurrentShop, ShopifyAdmin, shopifyLayer } from "@unisian/shopify-auth";
7
- import * as Effect from "effect/Effect";
8
- import * as Layer from "effect/Layer";
9
- //#region src/serve.ts
10
- const serveWith = (callerOf, layerFor) => (capability) => {
11
- const { contract } = capability;
12
- const encodeOutput = Schema.encodeEffect(Schema.toCodecJson(contract.output));
13
- const wire = wireFor(contract.failure);
14
- const serverFnName = serverFnNameFor(contract.name);
15
- return async ({ data, context, serverFnMeta }) => {
16
- if (serverFnMeta.name !== serverFnName) throw new Error(`${serverFnMeta.name} serves ${contract.name}; name it ${serverFnName} so its URL names the contract`);
17
- const { shop, log } = callerOf(context);
18
- const layer = Layer.mergeAll(layerFor(context), Layer.succeed(CurrentShop, { shop }), Layer.succeed(RequestReference, { reference: log.reference })).pipe(Layer.orDie);
19
- const exit = await capability.handler(data).pipe(Effect.flatMap((output) => Effect.orDie(encodeOutput(output))), Effect.provide(layer), Effect.tapCause((cause) => logFailure("server function", cause)), runPromiseExit(contract.name, {
20
- ...log,
21
- shop
22
- }));
23
- return wire.encode(exit, log.reference);
24
- };
25
- };
26
- const makeServe = (config) => serveWith((context) => ({
27
- log: context.log,
28
- shop: context.shopify.shop
29
- }), (context) => {
30
- const { services, ...shopify } = config();
31
- return Layer.mergeAll(ShopifyAdmin.layer(context.shopify).pipe(Layer.provideMerge(shopifyLayer(shopify))), services ?? Layer.empty);
32
- });
33
- //#endregion
34
- export { makeServe, reportBrowserError, serveWith };
package/dist/wire.mjs DELETED
@@ -1,32 +0,0 @@
1
- import { ErrorReport, errorReport } from "./report.mjs";
2
- import { CapabilityFailureError, isMerchantFacing } from "./failure.mjs";
3
- import * as Schema from "effect/Schema";
4
- import * as Cause from "effect/Cause";
5
- import * as Exit from "effect/Exit";
6
- //#region src/wire.ts
7
- const isErrorReport = Schema.is(ErrorReport);
8
- const reportIn = (cause) => cause.reasons.filter(Cause.isDieReason).map((reason) => reason.defect).find(isErrorReport) ?? { reference: "none" };
9
- const wireFor = (failure) => {
10
- const codec = Schema.toCodecJson(Schema.Exit(Schema.Json, failure, Schema.Defect()));
11
- const encodeExit = Schema.encodeSync(codec);
12
- const decodeExit = Schema.decodeSync(codec);
13
- const isContractFailure = Schema.is(failure);
14
- const encode = (exit, reference) => {
15
- if (Exit.isSuccess(exit)) return encodeExit(exit);
16
- const failures = exit.cause.reasons.filter(Cause.isFailReason).filter((reason) => isContractFailure(reason.error));
17
- const merchantMessage = failures.map((reason) => reason.error).find(isMerchantFacing)?.merchantMessage;
18
- return encodeExit(Exit.failCause(Cause.fromReasons([...failures, Cause.makeDieReason(errorReport(reference, exit.cause, merchantMessage))])));
19
- };
20
- const decode = (wire) => {
21
- const exit = decodeExit(wire);
22
- if (Exit.isSuccess(exit)) return exit.value;
23
- const contractFailure = exit.cause.reasons.find(Cause.isFailReason)?.error;
24
- throw new CapabilityFailureError(reportIn(exit.cause), contractFailure);
25
- };
26
- return {
27
- decode,
28
- encode
29
- };
30
- };
31
- //#endregion
32
- export { wireFor };