@unisian/start 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/liquid.d.mts +12 -0
- package/dist/liquid.mjs +37 -0
- package/dist/proxy-input-rejected.mjs +5 -0
- package/dist/proxy-responses.d.mts +17 -0
- package/dist/proxy-responses.mjs +39 -0
- package/dist/proxy.d.mts +54 -0
- package/dist/proxy.mjs +104 -0
- package/dist/serve.d.mts +1 -1
- package/package.json +14 -10
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import * as Schema from "effect/Schema";
|
|
2
|
+
import { ReactNode } from "react";
|
|
3
|
+
//#region src/liquid.d.ts
|
|
4
|
+
export declare const liquid: (raw: string) => string;
|
|
5
|
+
export declare const t: (key: string, args?: Readonly<Record<string, string>>) => string;
|
|
6
|
+
export declare const stylesheet: (asset: string) => string;
|
|
7
|
+
export declare const script: (asset: string) => string;
|
|
8
|
+
export declare const renderSnippet: (name: string, params: Readonly<Record<string, string | number | boolean | null | undefined>>) => string;
|
|
9
|
+
declare const UnswappedLiquidToken_base: Schema.Class<UnswappedLiquidToken, Schema.TaggedStruct<"UnswappedLiquidToken", {}>, import("effect/Cause").YieldableError>;
|
|
10
|
+
export declare class UnswappedLiquidToken extends UnswappedLiquidToken_base {}
|
|
11
|
+
export declare const renderAppProxyPage: (node: ReactNode) => string;
|
|
12
|
+
//#endregion
|
package/dist/liquid.mjs
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as Predicate from "effect/Predicate";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
3
|
+
import { Base64, Hex } from "effect/encoding";
|
|
4
|
+
import * as Result from "effect/Result";
|
|
5
|
+
import { renderToStaticMarkup } from "react-dom/server";
|
|
6
|
+
//#region src/liquid.ts
|
|
7
|
+
const liquidNonce = (() => {
|
|
8
|
+
let nonce;
|
|
9
|
+
return () => {
|
|
10
|
+
nonce ??= Hex.encode(crypto.getRandomValues(/* @__PURE__ */ new Uint8Array(16)));
|
|
11
|
+
return nonce;
|
|
12
|
+
};
|
|
13
|
+
})();
|
|
14
|
+
const tokenPattern = () => new RegExp(`@@LQ${liquidNonce()}\\.([A-Za-z0-9+/=]+)@@`, "gu");
|
|
15
|
+
const liquid = (raw) => `@@LQ${liquidNonce()}.${Base64.encode(raw)}@@`;
|
|
16
|
+
const openerPattern = () => new RegExp(`\\{(?=[{%]|@@LQ${liquidNonce()}\\.)`, "gu");
|
|
17
|
+
const defuseTags = (markup) => markup.replace(openerPattern(), "{");
|
|
18
|
+
const liquidLiteral = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """).replaceAll("'", "’").replaceAll("{", "{").replaceAll("}", "}");
|
|
19
|
+
const t = (key, args = {}) => {
|
|
20
|
+
const argSource = Object.entries(args).map(([name, value]) => ` ${name}: '${liquidLiteral(value)}'`).join(",");
|
|
21
|
+
return liquid(`{{ '${key}' | ${argSource === "" ? "t" : `t:${argSource}`} }}`);
|
|
22
|
+
};
|
|
23
|
+
const stylesheet = (asset) => liquid(`{{ '${asset}' | asset_url | stylesheet_tag }}`);
|
|
24
|
+
const script = (asset) => liquid(`<script src="{{ '${asset}' | asset_url }}" type="module" data-ot-ignore><\/script>`);
|
|
25
|
+
const renderSnippet = (name, params) => {
|
|
26
|
+
const args = Object.entries(params).filter(([, value]) => value !== null && value !== void 0 && value !== "").map(([key, value]) => Predicate.isString(value) ? `${key}: '${liquidLiteral(value)}'` : `${key}: ${String(value)}`).join(", ");
|
|
27
|
+
return liquid(`{% render '${name}'${args === "" ? "" : `, ${args}`} %}`);
|
|
28
|
+
};
|
|
29
|
+
const authored = (base64) => Result.getOrElse(Base64.decodeString(base64), () => "");
|
|
30
|
+
var UnswappedLiquidToken = class extends Schema.TaggedError()("UnswappedLiquidToken", {}) {};
|
|
31
|
+
const renderAppProxyPage = (node) => {
|
|
32
|
+
const page = defuseTags(renderToStaticMarkup(node)).replace(tokenPattern(), (_token, base64) => authored(base64));
|
|
33
|
+
if (page.includes(`@@LQ${liquidNonce()}.`)) throw new UnswappedLiquidToken();
|
|
34
|
+
return page;
|
|
35
|
+
};
|
|
36
|
+
//#endregion
|
|
37
|
+
export { UnswappedLiquidToken, liquid, renderAppProxyPage, renderSnippet, script, stylesheet, t };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as Schema from "effect/Schema";
|
|
2
|
+
//#region src/proxy-responses.d.ts
|
|
3
|
+
export declare const proxyJson: (data: Schema.Json, init?: ResponseInit) => Response;
|
|
4
|
+
declare const proxyErrorStatus: {
|
|
5
|
+
readonly "bad-request": 400;
|
|
6
|
+
readonly forbidden: 403;
|
|
7
|
+
readonly internal: 500;
|
|
8
|
+
readonly "not-found": 404;
|
|
9
|
+
readonly unauthorized: 401;
|
|
10
|
+
};
|
|
11
|
+
export type ProxyErrorCode = keyof typeof proxyErrorStatus;
|
|
12
|
+
export declare const proxyError: (code: ProxyErrorCode, message: string) => Response;
|
|
13
|
+
export declare const proxyLiquid: (body: string, init?: ResponseInit) => Response;
|
|
14
|
+
export declare const LAYOUT_NONE = "{% layout none %}";
|
|
15
|
+
export declare const proxyRedirect: (location: string) => Response;
|
|
16
|
+
export declare const proxySaveRedirect: (location: string, fetched: boolean) => Response;
|
|
17
|
+
//#endregion
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
//#region src/proxy-responses.ts
|
|
2
|
+
const proxyJson = (data, init = {}) => {
|
|
3
|
+
const headers = new Headers(init.headers);
|
|
4
|
+
headers.set("Content-Type", "application/json; charset=utf-8");
|
|
5
|
+
headers.set("Cache-Control", "no-store");
|
|
6
|
+
return Response.json(data, {
|
|
7
|
+
...init,
|
|
8
|
+
headers
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
const proxyErrorStatus = {
|
|
12
|
+
"bad-request": 400,
|
|
13
|
+
forbidden: 403,
|
|
14
|
+
internal: 500,
|
|
15
|
+
"not-found": 404,
|
|
16
|
+
unauthorized: 401
|
|
17
|
+
};
|
|
18
|
+
const proxyError = (code, message) => proxyJson({ error: {
|
|
19
|
+
code,
|
|
20
|
+
message
|
|
21
|
+
} }, { status: proxyErrorStatus[code] });
|
|
22
|
+
const proxyLiquid = (body, init = {}) => {
|
|
23
|
+
const headers = new Headers(init.headers);
|
|
24
|
+
headers.set("Content-Type", "application/liquid");
|
|
25
|
+
headers.set("Cache-Control", "no-store");
|
|
26
|
+
headers.set("X-Robots-Tag", "noindex");
|
|
27
|
+
return new Response(body, {
|
|
28
|
+
...init,
|
|
29
|
+
headers
|
|
30
|
+
});
|
|
31
|
+
};
|
|
32
|
+
const LAYOUT_NONE = "{% layout none %}";
|
|
33
|
+
const proxyRedirect = (location) => new Response(null, {
|
|
34
|
+
headers: { Location: location },
|
|
35
|
+
status: 302
|
|
36
|
+
});
|
|
37
|
+
const proxySaveRedirect = (location, fetched) => fetched ? proxyJson({ location }) : proxyRedirect(location);
|
|
38
|
+
//#endregion
|
|
39
|
+
export { LAYOUT_NONE, proxyError, proxyJson, proxyLiquid, proxyRedirect, proxySaveRedirect };
|
package/dist/proxy.d.mts
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { LAYOUT_NONE, ProxyErrorCode, proxyError, proxyJson, proxyLiquid, proxyRedirect, proxySaveRedirect } from "./proxy-responses.mjs";
|
|
2
|
+
import * as Schema from "effect/Schema";
|
|
3
|
+
import { AnyContract, Capability } from "@unisian/capability";
|
|
4
|
+
import { RequestLogTags, RequestReference } from "@unisian/effect-observability";
|
|
5
|
+
import { CurrentCustomer, CurrentShop, LoggedInCustomer, ShopifyAdmin, ShopifyApp, ShopifyLayerConfig, ShopifyStorefront, StorefrontOperation } from "@unisian/shopify-auth";
|
|
6
|
+
import * as Layer from "effect/Layer";
|
|
7
|
+
import { HttpClient } from "effect/http";
|
|
8
|
+
//#region src/proxy.d.ts
|
|
9
|
+
export interface ProxyServeConfig<Services, ServicesError> extends ShopifyLayerConfig {
|
|
10
|
+
readonly appProxyPath: string;
|
|
11
|
+
readonly storefrontOperations: Readonly<Record<string, StorefrontOperation>>;
|
|
12
|
+
readonly services?: Layer.Layer<Services, ServicesError> | undefined;
|
|
13
|
+
}
|
|
14
|
+
export type AnyoneProvided<Services> = CurrentShop | CurrentCustomer | ShopifyAdmin | ShopifyStorefront | ShopifyApp | HttpClient.HttpClient | RequestReference | Services;
|
|
15
|
+
export type LoggedInProvided<Services> = AnyoneProvided<Services> | LoggedInCustomer;
|
|
16
|
+
export interface StorefrontLinks {
|
|
17
|
+
readonly login: (returnPath?: string) => string;
|
|
18
|
+
readonly url: (path: string) => string;
|
|
19
|
+
}
|
|
20
|
+
export type ProxyAnswer<Output> = {
|
|
21
|
+
readonly answer: "json";
|
|
22
|
+
} | {
|
|
23
|
+
readonly answer: "page";
|
|
24
|
+
readonly render: (output: Output) => string;
|
|
25
|
+
} | {
|
|
26
|
+
readonly answer: "fragment";
|
|
27
|
+
readonly render: (output: Output) => string;
|
|
28
|
+
} | {
|
|
29
|
+
readonly answer: "redirect";
|
|
30
|
+
readonly location: (output: Output, storefront: StorefrontLinks) => string;
|
|
31
|
+
readonly fetchField?: string | undefined;
|
|
32
|
+
};
|
|
33
|
+
type ProxyOptions<Output, Failure> = ProxyAnswer<Output> & {
|
|
34
|
+
readonly refuse?: ((failure: Failure, storefront: StorefrontLinks) => Response) | undefined;
|
|
35
|
+
};
|
|
36
|
+
type RefuseWhenFailing<Failure> = [Failure] extends [never] ? unknown : {
|
|
37
|
+
readonly refuse: (failure: Failure, storefront: StorefrontLinks) => Response;
|
|
38
|
+
};
|
|
39
|
+
export type ProxyOptionsFor<ContractType extends AnyContract> = ProxyOptions<ContractType["output"]["Type"], ContractType["failure"]["Type"]> & RefuseWhenFailing<ContractType["failure"]["Type"]>;
|
|
40
|
+
export interface ProxyCall {
|
|
41
|
+
readonly request: Request;
|
|
42
|
+
readonly context: {
|
|
43
|
+
readonly log: RequestLogTags;
|
|
44
|
+
};
|
|
45
|
+
readonly params?: Readonly<Record<string, string>> | undefined;
|
|
46
|
+
}
|
|
47
|
+
export type ProxyField = Schema.Json | File | readonly (string | File)[];
|
|
48
|
+
export type ProxyFields = Readonly<Record<string, ProxyField>>;
|
|
49
|
+
export declare const makeProxyServe: <Services = never, ServicesError = never>(config: () => ProxyServeConfig<Services, ServicesError>) => {
|
|
50
|
+
anyone: <ContractType extends AnyContract, Requirements extends AnyoneProvided<Services>>(capability: Capability<ContractType, Requirements>, options: ProxyOptionsFor<ContractType>) => ({ request, context: { log }, params }: ProxyCall) => Promise<Response>;
|
|
51
|
+
loggedIn: <ContractType extends AnyContract, Requirements extends LoggedInProvided<Services>>(capability: Capability<ContractType, Requirements>, options: ProxyOptionsFor<ContractType>) => ({ request, context: { log }, params }: ProxyCall) => Promise<Response>;
|
|
52
|
+
};
|
|
53
|
+
//#endregion
|
|
54
|
+
export { LAYOUT_NONE, type ProxyErrorCode, proxyError, proxyJson, proxyLiquid, proxyRedirect, proxySaveRedirect };
|
package/dist/proxy.mjs
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { respond } from "./middleware.mjs";
|
|
2
|
+
import { ProxyInputRejected } from "./proxy-input-rejected.mjs";
|
|
3
|
+
import { LAYOUT_NONE, proxyError, proxyJson, proxyLiquid, proxyRedirect, proxySaveRedirect } from "./proxy-responses.mjs";
|
|
4
|
+
import * as Schema from "effect/Schema";
|
|
5
|
+
import * as Option from "effect/Option";
|
|
6
|
+
import { RequestReference, logFailure } from "@unisian/effect-observability";
|
|
7
|
+
import { LoggedInCustomer, ShopifyAdmin, ShopifyStorefront, StorefrontOperationRegistry, appProxyIdentity, shopifyLayer, verifyAppProxy } from "@unisian/shopify-auth";
|
|
8
|
+
import * as Effect from "effect/Effect";
|
|
9
|
+
import * as Layer from "effect/Layer";
|
|
10
|
+
//#region src/proxy.ts
|
|
11
|
+
const shopifyQueryKeys = /* @__PURE__ */ new Set([
|
|
12
|
+
"logged_in_customer_id",
|
|
13
|
+
"path_prefix",
|
|
14
|
+
"shop",
|
|
15
|
+
"signature",
|
|
16
|
+
"timestamp"
|
|
17
|
+
]);
|
|
18
|
+
const fieldsOf = (entries) => {
|
|
19
|
+
const fields = /* @__PURE__ */ new Map();
|
|
20
|
+
for (const [key, value] of entries) if (!shopifyQueryKeys.has(key)) {
|
|
21
|
+
const earlier = fields.get(key);
|
|
22
|
+
fields.set(key, earlier === void 0 ? value : [...[earlier].flat(), value]);
|
|
23
|
+
}
|
|
24
|
+
return Object.fromEntries(fields);
|
|
25
|
+
};
|
|
26
|
+
const decodeJsonBody = Schema.decodeUnknownEffect(Schema.Record(Schema.String, Schema.Json));
|
|
27
|
+
const rejected = (cause) => new ProxyInputRejected({ cause });
|
|
28
|
+
const isForm = (type) => type.startsWith("application/x-www-form-urlencoded") || type.startsWith("multipart/form-data");
|
|
29
|
+
const bodyFields = (request) => {
|
|
30
|
+
const type = request.headers.get("content-type") ?? "";
|
|
31
|
+
if (type.startsWith("application/json")) return Effect.tryPromise({
|
|
32
|
+
catch: rejected,
|
|
33
|
+
try: () => request.json()
|
|
34
|
+
}).pipe(Effect.flatMap(decodeJsonBody), Effect.mapError(rejected));
|
|
35
|
+
if (isForm(type)) return Effect.tryPromise({
|
|
36
|
+
catch: rejected,
|
|
37
|
+
try: () => request.formData()
|
|
38
|
+
}).pipe(Effect.map((form) => fieldsOf(form.entries())));
|
|
39
|
+
return Effect.succeed({});
|
|
40
|
+
};
|
|
41
|
+
const buyerIp = (request) => {
|
|
42
|
+
const first = request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ?? "";
|
|
43
|
+
return first === "" ? null : first;
|
|
44
|
+
};
|
|
45
|
+
const visitedPath = (request, proxy, appProxyPath) => {
|
|
46
|
+
const url = new URL(request.url);
|
|
47
|
+
const query = new URLSearchParams([...url.searchParams].filter(([key]) => !shopifyQueryKeys.has(key))).toString();
|
|
48
|
+
return `${proxy.pathPrefix}${url.pathname.slice(appProxyPath.length)}${query === "" ? "" : `?${query}`}`;
|
|
49
|
+
};
|
|
50
|
+
const storefrontLinks = (request, proxy, appProxyPath) => {
|
|
51
|
+
const host = request.headers.get("x-forwarded-host") ?? proxy.shop;
|
|
52
|
+
const url = (path) => `https://${host}${path}`;
|
|
53
|
+
return {
|
|
54
|
+
login: (returnPath = visitedPath(request, proxy, appProxyPath)) => url(`/account/login?return_url=${encodeURIComponent(returnPath)}`),
|
|
55
|
+
url
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
const fetchedSave = (answer, fields) => answer.answer === "redirect" && answer.fetchField !== void 0 && fields[answer.fetchField] === "1";
|
|
59
|
+
const answerWith = (answer, output, encoded, fields, storefront) => {
|
|
60
|
+
if (answer.answer === "json") return proxyJson(encoded);
|
|
61
|
+
if (answer.answer === "page") return proxyLiquid(answer.render(output));
|
|
62
|
+
if (answer.answer === "fragment") return proxyLiquid(`${LAYOUT_NONE}${answer.render(output)}`);
|
|
63
|
+
return proxySaveRedirect(answer.location(output, storefront), fetchedSave(answer, fields));
|
|
64
|
+
};
|
|
65
|
+
const loggedOutAnswer = (answer, fields, storefront) => answer.answer === "page" || answer.answer === "redirect" && !fetchedSave(answer, fields) ? proxyRedirect(storefront.login()) : proxyError("unauthorized", "Login required");
|
|
66
|
+
const project = (config) => (contract, options, run) => {
|
|
67
|
+
const decodeInput = Schema.decodeUnknownEffect(contract.input);
|
|
68
|
+
const encodeOutput = Schema.encodeEffect(Schema.toCodecJson(contract.output));
|
|
69
|
+
const isFailure = Schema.is(contract.failure);
|
|
70
|
+
const refuseWith = (storefront) => (failure) => options.refuse === void 0 ? Effect.die(failure) : Effect.succeed(options.refuse(failure, storefront));
|
|
71
|
+
return ({ request, context: { log }, params = {} }) => {
|
|
72
|
+
const { appProxyPath, services, storefrontOperations, ...shopify } = config();
|
|
73
|
+
const layerFor = (proxy) => Layer.mergeAll(appProxyIdentity(proxy), ShopifyAdmin.layerForShop(proxy.shop), ShopifyStorefront.layerForShop(proxy.shop, buyerIp(request)), Layer.succeed(RequestReference, { reference: log.reference }), services ?? Layer.empty).pipe(Layer.orDie);
|
|
74
|
+
const served = Effect.gen(function* serveProxy() {
|
|
75
|
+
const proxy = yield* verifyAppProxy(request);
|
|
76
|
+
const body = yield* bodyFields(request);
|
|
77
|
+
const fields = {
|
|
78
|
+
...fieldsOf(new URL(request.url).searchParams),
|
|
79
|
+
...body,
|
|
80
|
+
...params
|
|
81
|
+
};
|
|
82
|
+
const handler = run(proxy);
|
|
83
|
+
const storefront = storefrontLinks(request, proxy, appProxyPath);
|
|
84
|
+
if (Option.isNone(handler)) return loggedOutAnswer(options, fields, storefront);
|
|
85
|
+
const input = yield* decodeInput(fields).pipe(Effect.mapError(rejected));
|
|
86
|
+
return yield* handler.value(input).pipe(Effect.flatMap((output) => Effect.orDie(encodeOutput(output)).pipe(Effect.map((encoded) => answerWith(options, output, encoded, fields, storefront)))), Effect.catchIf(isFailure, refuseWith(storefront)), Effect.provide(layerFor(proxy)));
|
|
87
|
+
}).pipe(Effect.catchTags({
|
|
88
|
+
InvalidAppProxy: (error) => Effect.logWarning("app proxy request rejected", { reason: error.reason }).pipe(Effect.as(proxyError("unauthorized", "Invalid app proxy signature"))),
|
|
89
|
+
ProxyInputRejected: () => Effect.succeed(proxyError("bad-request", "Invalid input"))
|
|
90
|
+
}), Effect.provide(Layer.mergeAll(shopifyLayer(shopify), StorefrontOperationRegistry.layer(storefrontOperations))), Effect.tapCause((cause) => logFailure("app proxy", cause)));
|
|
91
|
+
return respond(contract.name, log, () => proxyError("internal", `Something went wrong. Reference: ${log.reference}`))(served);
|
|
92
|
+
};
|
|
93
|
+
};
|
|
94
|
+
const makeProxyServe = (config) => {
|
|
95
|
+
const projectWith = project(config);
|
|
96
|
+
const anyone = (capability, options) => projectWith(capability.contract, options, () => Option.some((input) => capability.handler(input)));
|
|
97
|
+
const loggedIn = (capability, options) => projectWith(capability.contract, options, (proxy) => proxy.customer.pipe(Option.map((customer) => (input) => capability.handler(input).pipe(Effect.provideService(LoggedInCustomer, { customer })))));
|
|
98
|
+
return {
|
|
99
|
+
anyone,
|
|
100
|
+
loggedIn
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
//#endregion
|
|
104
|
+
export { LAYOUT_NONE, makeProxyServe, proxyError, proxyJson, proxyLiquid, proxyRedirect, proxySaveRedirect };
|
package/dist/serve.d.mts
CHANGED
|
@@ -4,8 +4,8 @@ import { AnyContract, Capability, InputOf } from "@unisian/capability";
|
|
|
4
4
|
import { RequestLogTags, RequestReference } from "@unisian/effect-observability";
|
|
5
5
|
import { AuthenticatedShop, CurrentShop, ShopifyAdmin, ShopifyApp, ShopifyLayerConfig } from "@unisian/shopify-auth";
|
|
6
6
|
import * as Layer from "effect/Layer";
|
|
7
|
-
import { ShopDomain } from "@unisian/shopify-sessions";
|
|
8
7
|
import { HttpClient } from "effect/http";
|
|
8
|
+
import { ShopDomain } from "@unisian/shopify-sessions";
|
|
9
9
|
//#region src/serve.d.ts
|
|
10
10
|
export interface ServeContext {
|
|
11
11
|
readonly shopify: AuthenticatedShop;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@unisian/start",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -16,7 +16,9 @@
|
|
|
16
16
|
"./react": "./dist/client.mjs",
|
|
17
17
|
"./serve": "./dist/serve.mjs",
|
|
18
18
|
"./middleware": "./dist/middleware.mjs",
|
|
19
|
-
"./function-id": "./dist/function-id.mjs"
|
|
19
|
+
"./function-id": "./dist/function-id.mjs",
|
|
20
|
+
"./proxy": "./dist/proxy.mjs",
|
|
21
|
+
"./liquid": "./dist/liquid.mjs"
|
|
20
22
|
},
|
|
21
23
|
"publishConfig": {
|
|
22
24
|
"access": "public"
|
|
@@ -31,11 +33,12 @@
|
|
|
31
33
|
"@tanstack/react-start": "1.168.60",
|
|
32
34
|
"effect": "4.0.6",
|
|
33
35
|
"react": "19.3.0",
|
|
36
|
+
"react-dom": "19.3.0",
|
|
34
37
|
"vite": "npm:@voidzero-dev/vite-plus-core@1.1.0",
|
|
35
|
-
"@unisian/capability": "0.
|
|
36
|
-
"@unisian/effect-observability": "0.
|
|
37
|
-
"@unisian/shopify-auth": "0.
|
|
38
|
-
"@unisian/shopify-sessions": "0.
|
|
38
|
+
"@unisian/capability": "0.4.0",
|
|
39
|
+
"@unisian/effect-observability": "0.4.0",
|
|
40
|
+
"@unisian/shopify-auth": "0.4.0",
|
|
41
|
+
"@unisian/shopify-sessions": "0.4.0"
|
|
39
42
|
},
|
|
40
43
|
"peerDependencies": {
|
|
41
44
|
"@tanstack/react-query": "^5.104.1",
|
|
@@ -44,10 +47,11 @@
|
|
|
44
47
|
"@tanstack/react-start": "^1.168.60",
|
|
45
48
|
"effect": "^4.0.2",
|
|
46
49
|
"react": "^19.3.0",
|
|
47
|
-
"
|
|
48
|
-
"@unisian/
|
|
49
|
-
"@unisian/
|
|
50
|
-
"@unisian/
|
|
50
|
+
"react-dom": "^19.3.0",
|
|
51
|
+
"@unisian/capability": "0.4.0",
|
|
52
|
+
"@unisian/effect-observability": "0.4.0",
|
|
53
|
+
"@unisian/shopify-sessions": "0.4.0",
|
|
54
|
+
"@unisian/shopify-auth": "0.4.0"
|
|
51
55
|
},
|
|
52
56
|
"scripts": {}
|
|
53
57
|
}
|