Compare commits
2 commits
main
...
order-expo
Author | SHA1 | Date | |
---|---|---|---|
![]() |
53b67c4505 | ||
![]() |
489e110e74 |
38 changed files with 31334 additions and 6 deletions
18
apps/orders-export/.env.example
Normal file
18
apps/orders-export/.env.example
Normal file
|
@ -0,0 +1,18 @@
|
|||
# The key used for metadata encryption. Required for production builds
|
||||
SECRET_KEY=
|
||||
|
||||
# APL Config
|
||||
# https://github.com/saleor/saleor-app-sdk/blob/main/docs/apl.md
|
||||
APL=file
|
||||
REST_APL_ENDPOINT=
|
||||
REST_APL_TOKEN=
|
||||
|
||||
APP_LOG_LEVEL=info
|
||||
|
||||
# Local development variables. When developped locally with Saleor inside docker, these can be set to:
|
||||
# APP_IFRAME_BASE_URL = http://localhost:3000, so Dashboard on host can access iframe
|
||||
# APP_API_BASE_URL=http://host.docker.internal:3000 - so Saleor can reach App running on host, from the container.
|
||||
# If developped with tunnels, set this empty, it will fallback to default Next's localhost:3000
|
||||
# https://docs.saleor.io/docs/3.x/developer/extending/apps/local-app-development
|
||||
APP_IFRAME_BASE_URL=
|
||||
APP_API_BASE_URL=
|
4
apps/orders-export/.eslintrc.json
Normal file
4
apps/orders-export/.eslintrc.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"root": true,
|
||||
"extends": ["saleor"]
|
||||
}
|
19
apps/orders-export/.graphqlrc.yml
Normal file
19
apps/orders-export/.graphqlrc.yml
Normal file
|
@ -0,0 +1,19 @@
|
|||
schema: graphql/schema.graphql
|
||||
documents: [graphql/**/*.graphql, src/**/*.ts, src/**/*.tsx]
|
||||
extensions:
|
||||
codegen:
|
||||
overwrite: true
|
||||
generates:
|
||||
generated/graphql.ts:
|
||||
config:
|
||||
dedupeFragments: true
|
||||
plugins:
|
||||
- typescript
|
||||
- typescript-operations
|
||||
- typescript-urql:
|
||||
documentVariablePrefix: "Untyped"
|
||||
fragmentVariablePrefix: "Untyped"
|
||||
- typed-document-node
|
||||
generated/schema.graphql:
|
||||
plugins:
|
||||
- schema-ast
|
39
apps/orders-export/graphql/fragments/order-base.graphql
Normal file
39
apps/orders-export/graphql/fragments/order-base.graphql
Normal file
|
@ -0,0 +1,39 @@
|
|||
fragment OrderBase on Order {
|
||||
id
|
||||
user {
|
||||
id
|
||||
email
|
||||
}
|
||||
channel {
|
||||
id
|
||||
slug
|
||||
name
|
||||
}
|
||||
userEmail
|
||||
shippingMethodName
|
||||
total {
|
||||
gross {
|
||||
amount
|
||||
currency
|
||||
}
|
||||
net {
|
||||
currency
|
||||
amount
|
||||
}
|
||||
}
|
||||
lines {
|
||||
productVariantId
|
||||
productSku
|
||||
variantName
|
||||
unitPrice {
|
||||
gross {
|
||||
amount
|
||||
}
|
||||
net {
|
||||
amount
|
||||
}
|
||||
}
|
||||
quantity
|
||||
}
|
||||
number
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
mutation UpdateAppMetadata($id: ID!, $input: [MetadataInput!]!) {
|
||||
updatePrivateMetadata(id: $id, input: $input) {
|
||||
item {
|
||||
privateMetadata {
|
||||
key
|
||||
value
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
apps/orders-export/graphql/queries/PaginatedOrders.graphql
Normal file
11
apps/orders-export/graphql/queries/PaginatedOrders.graphql
Normal file
|
@ -0,0 +1,11 @@
|
|||
query PaginatedOrders($after: String, $channel: String!) {
|
||||
orders(first: 100, after: $after, channel: $channel) {
|
||||
edges {
|
||||
node {
|
||||
... on Order {
|
||||
...OrderBase
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
30308
apps/orders-export/graphql/schema.graphql
Normal file
30308
apps/orders-export/graphql/schema.graphql
Normal file
File diff suppressed because it is too large
Load diff
0
apps/orders-export/graphql/subscriptions/.gitkeep
Normal file
0
apps/orders-export/graphql/subscriptions/.gitkeep
Normal file
5
apps/orders-export/next-env.d.ts
vendored
Normal file
5
apps/orders-export/next-env.d.ts
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
44
apps/orders-export/next.config.js
Normal file
44
apps/orders-export/next.config.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
const { z } = require("zod");
|
||||
const { withSentryConfig } = require("@sentry/nextjs");
|
||||
|
||||
const RequiredEnvs = z.object({
|
||||
APL: z.string().min(1),
|
||||
});
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = () => {
|
||||
try {
|
||||
RequiredEnvs.parse(process.env);
|
||||
} catch (e) {
|
||||
console.error("🚫 Missing required env variables, see message below");
|
||||
console.error(e.issues);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
return {
|
||||
reactStrictMode: true,
|
||||
// TODO Infer names dynamically from disk
|
||||
transpilePackages: ["@saleor/apps-shared", "@saleor/apps-ui", "@saleor/react-hook-form-macaw", "@saleor/trpc"],
|
||||
};
|
||||
};
|
||||
|
||||
const isSentryPropertiesInEnvironment =
|
||||
process.env.SENTRY_AUTH_TOKEN && process.env.SENTRY_PROJECT && process.env.SENTRY_ORG;
|
||||
|
||||
const configWithSentry = withSentryConfig(
|
||||
nextConfig,
|
||||
{
|
||||
silent: true,
|
||||
org: process.env.SENTRY_ORG,
|
||||
project: process.env.SENTRY_PROJECT,
|
||||
},
|
||||
{
|
||||
widenClientFileUpload: true,
|
||||
transpileClientSDK: true,
|
||||
tunnelRoute: "/monitoring",
|
||||
hideSourceMaps: true,
|
||||
disableLogger: true,
|
||||
}
|
||||
);
|
||||
|
||||
module.exports = isSentryPropertiesInEnvironment ? configWithSentry : nextConfig;
|
67
apps/orders-export/package.json
Normal file
67
apps/orders-export/package.json
Normal file
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
"name": "saleor-app-orders-export",
|
||||
"version": "0.0.0",
|
||||
"scripts": {
|
||||
"build": "pnpm generate && next build",
|
||||
"dev": "pnpm generate && NODE_OPTIONS='--inspect' next dev",
|
||||
"fetch-schema": "curl https://raw.githubusercontent.com/saleor/saleor/${npm_package_saleor_schemaVersion}/saleor/graphql/schema.graphql > graphql/schema.graphql",
|
||||
"generate": "graphql-codegen",
|
||||
"lint": "next lint",
|
||||
"lint:fix": "eslint --fix .",
|
||||
"start": "next start",
|
||||
"test": "vitest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hookform/resolvers": "^3.1.0",
|
||||
"@saleor/app-sdk": "0.43.1",
|
||||
"@saleor/apps-shared": "workspace:*",
|
||||
"@saleor/apps-ui": "workspace:*",
|
||||
"@saleor/macaw-ui": "0.8.0-pre.127",
|
||||
"@saleor/react-hook-form-macaw": "workspace:*",
|
||||
"@saleor/trpc": "workspace:*",
|
||||
"@sentry/nextjs": "7.67.0",
|
||||
"@tanstack/react-query": "^4.29.19",
|
||||
"@trpc/client": "10.38.1",
|
||||
"@trpc/next": "10.38.1",
|
||||
"@trpc/react-query": "10.38.1",
|
||||
"@trpc/server": "10.38.1",
|
||||
"@urql/exchange-auth": "^2.1.4",
|
||||
"@vitejs/plugin-react": "4.0.4",
|
||||
"graphql": "16.7.1",
|
||||
"graphql-tag": "^2.12.6",
|
||||
"jsdom": "^20.0.3",
|
||||
"next": "13.4.8",
|
||||
"pino": "^8.14.1",
|
||||
"pino-pretty": "^10.0.0",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-error-boundary": "4.0.10",
|
||||
"react-hook-form": "^7.43.9",
|
||||
"urql": "^4.0.4",
|
||||
"usehooks-ts": "^2.9.1",
|
||||
"vite": "4.4.8",
|
||||
"vitest": "0.34.1",
|
||||
"zod": "3.21.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@graphql-codegen/cli": "4.0.1",
|
||||
"@graphql-codegen/introspection": "4.0.0",
|
||||
"@graphql-codegen/typed-document-node": "5.0.1",
|
||||
"@graphql-codegen/typescript": "4.0.1",
|
||||
"@graphql-codegen/typescript-operations": "4.0.1",
|
||||
"@graphql-codegen/typescript-urql": "3.7.3",
|
||||
"@graphql-typed-document-node/core": "3.2.0",
|
||||
"@testing-library/react": "^14.0.0",
|
||||
"@testing-library/react-hooks": "^8.0.1",
|
||||
"@types/react": "18.2.5",
|
||||
"@types/react-dom": "18.2.5",
|
||||
"eslint": "8.46.0",
|
||||
"eslint-config-saleor": "workspace:*",
|
||||
"node-mocks-http": "^1.12.2",
|
||||
"typescript": "5.1.6"
|
||||
},
|
||||
"private": true,
|
||||
"saleor": {
|
||||
"schemaVersion": "3.14"
|
||||
}
|
||||
}
|
37
apps/orders-export/sentry.client.config.ts
Normal file
37
apps/orders-export/sentry.client.config.ts
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* This file configures the initialization of Sentry on the client.
|
||||
* The config you add here will be used whenever a users loads a page in their browser.
|
||||
* https://docs.sentry.io/platforms/javascript/guides/nextjs/
|
||||
*/
|
||||
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import pkg from "./package.json";
|
||||
|
||||
Sentry.init({
|
||||
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
||||
|
||||
// Adjust this value in production, or use tracesSampler for greater control
|
||||
tracesSampleRate: 0.5,
|
||||
|
||||
// Setting this option to true will print useful information to the console while you're setting up Sentry.
|
||||
debug: false,
|
||||
|
||||
replaysOnErrorSampleRate: 1.0,
|
||||
|
||||
/*
|
||||
* This sets the sample rate to be 10%. You may want this to be 100% while
|
||||
* in development and sample at a lower rate in production
|
||||
*/
|
||||
replaysSessionSampleRate: 0.1,
|
||||
|
||||
// You can remove this option if you're not planning to use the Sentry Session Replay feature:
|
||||
integrations: [
|
||||
new Sentry.Replay({
|
||||
// Additional Replay configuration goes in here, for example:
|
||||
maskAllText: true,
|
||||
blockAllMedia: true,
|
||||
}),
|
||||
],
|
||||
environment: process.env.SENTRY_ENVIRONMENT,
|
||||
release: `${pkg.name}@${pkg.version}`,
|
||||
});
|
21
apps/orders-export/sentry.edge.config.ts
Normal file
21
apps/orders-export/sentry.edge.config.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* This file configures the initialization of Sentry for edge features (middleware, edge routes, and so on).
|
||||
* The config you add here will be used whenever one of the edge features is loaded.
|
||||
* Note that this config is unrelated to the Vercel Edge Runtime and is also required when running locally.
|
||||
* https://docs.sentry.io/platforms/javascript/guides/nextjs/
|
||||
*/
|
||||
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import pkg from "./package.json";
|
||||
|
||||
Sentry.init({
|
||||
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
||||
|
||||
// Adjust this value in production, or use tracesSampler for greater control
|
||||
tracesSampleRate: 0.5,
|
||||
|
||||
// Setting this option to true will print useful information to the console while you're setting up Sentry.
|
||||
debug: false,
|
||||
environment: process.env.SENTRY_ENVIRONMENT,
|
||||
release: `${pkg.name}@${pkg.version}`,
|
||||
});
|
20
apps/orders-export/sentry.server.config.ts
Normal file
20
apps/orders-export/sentry.server.config.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* This file configures the initialization of Sentry on the server.
|
||||
* The config you add here will be used whenever the server handles a request.
|
||||
* https://docs.sentry.io/platforms/javascript/guides/nextjs/
|
||||
*/
|
||||
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import pkg from "./package.json";
|
||||
|
||||
Sentry.init({
|
||||
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
|
||||
|
||||
// Adjust this value in production, or use tracesSampler for greater control
|
||||
tracesSampleRate: 0.5,
|
||||
|
||||
// Setting this option to true will print useful information to the console while you're setting up Sentry.
|
||||
debug: false,
|
||||
environment: process.env.SENTRY_ENVIRONMENT,
|
||||
release: `${pkg.name}@${pkg.version}`,
|
||||
});
|
|
@ -0,0 +1,36 @@
|
|||
import { SettingsManager } from "@saleor/app-sdk/settings-manager";
|
||||
import { AppConfig } from "./app-config";
|
||||
import { createSettingsManager } from "./metadata-manager";
|
||||
import { createGraphQLClient } from "@saleor/apps-shared";
|
||||
import { AuthData } from "@saleor/app-sdk/APL";
|
||||
|
||||
export class AppConfigMetadataManager {
|
||||
public readonly metadataKey = "app-config-v1";
|
||||
|
||||
constructor(private mm: SettingsManager) {}
|
||||
|
||||
async get() {
|
||||
const metadata = await this.mm.get(this.metadataKey);
|
||||
|
||||
return metadata ? AppConfig.parse(metadata) : new AppConfig();
|
||||
}
|
||||
|
||||
set(config: AppConfig) {
|
||||
return this.mm.set({
|
||||
key: this.metadataKey,
|
||||
value: config.serialize(),
|
||||
});
|
||||
}
|
||||
|
||||
static createFromAuthData(authData: AuthData): AppConfigMetadataManager {
|
||||
const settingsManager = createSettingsManager(
|
||||
createGraphQLClient({
|
||||
saleorApiUrl: authData.saleorApiUrl,
|
||||
token: authData.token,
|
||||
}),
|
||||
authData.appId,
|
||||
);
|
||||
|
||||
return new AppConfigMetadataManager(settingsManager);
|
||||
}
|
||||
}
|
24
apps/orders-export/src/modules/configuration/app-config.ts
Normal file
24
apps/orders-export/src/modules/configuration/app-config.ts
Normal file
|
@ -0,0 +1,24 @@
|
|||
import { z } from "zod";
|
||||
import { RootConfig } from "./schemas/root-config.schema";
|
||||
|
||||
export class AppConfig {
|
||||
private rootData: RootConfig.Shape = {};
|
||||
|
||||
constructor(initialData?: RootConfig.Shape) {
|
||||
if (initialData) {
|
||||
this.rootData = RootConfig.Schema.parse(initialData);
|
||||
}
|
||||
}
|
||||
|
||||
static parse(serializedSchema: string) {
|
||||
return new AppConfig(JSON.parse(serializedSchema));
|
||||
}
|
||||
|
||||
serialize() {
|
||||
return JSON.stringify(this.rootData);
|
||||
}
|
||||
|
||||
getConfig() {
|
||||
return this.rootData;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
import { createLogger } from "@saleor/apps-shared";
|
||||
import { router } from "../trpc/trpc-server";
|
||||
|
||||
const logger = createLogger({ name: "configuration.router" });
|
||||
|
||||
export const configurationRouter = router({});
|
|
@ -0,0 +1,12 @@
|
|||
import { SettingsManager } from "@saleor/app-sdk/settings-manager";
|
||||
import { EncryptedMetadataManagerFactory } from "@saleor/apps-shared";
|
||||
import { Client } from "urql";
|
||||
|
||||
export const createSettingsManager = (
|
||||
client: Pick<Client, "query" | "mutation">,
|
||||
appId: string,
|
||||
): SettingsManager => {
|
||||
const metadataManagerFactory = new EncryptedMetadataManagerFactory(process.env.SECRET_KEY!);
|
||||
|
||||
return metadataManagerFactory.create(client, appId);
|
||||
};
|
|
@ -0,0 +1,12 @@
|
|||
import { z } from "zod";
|
||||
|
||||
export namespace RootConfig {
|
||||
/**
|
||||
* Store entire app config in single file
|
||||
* - Only one request
|
||||
* - Always transactional
|
||||
*/
|
||||
export const Schema = z.object({});
|
||||
|
||||
export type Shape = z.infer<typeof Schema>;
|
||||
}
|
22
apps/orders-export/src/modules/order/order-row.ts
Normal file
22
apps/orders-export/src/modules/order/order-row.ts
Normal file
|
@ -0,0 +1,22 @@
|
|||
export interface OrderRowLine {
|
||||
productVariantId: string;
|
||||
productSku: string;
|
||||
variantName: string;
|
||||
quantity: number;
|
||||
unitPriceGrossAmount: number;
|
||||
unitPriceNetAmount: number;
|
||||
}
|
||||
|
||||
// todo add more fields
|
||||
export interface OrderRowFull extends OrderRowLine {
|
||||
id: string;
|
||||
number: string;
|
||||
userId: string;
|
||||
userEmail: string;
|
||||
channelId: string;
|
||||
channelSlug: string;
|
||||
shippingMethodName: string;
|
||||
totalGrossAmount: string;
|
||||
totalNetAmount: string;
|
||||
orderCurrency: string;
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
import { verifyJWT } from "@saleor/app-sdk/verify-jwt";
|
||||
import { middleware, procedure } from "./trpc-server";
|
||||
import { TRPCError } from "@trpc/server";
|
||||
import { ProtectedHandlerError } from "@saleor/app-sdk/handlers/next";
|
||||
import { saleorApp } from "../../saleor-app";
|
||||
import { createGraphQLClient, logger } from "@saleor/apps-shared";
|
||||
|
||||
const attachAppToken = middleware(async ({ ctx, next }) => {
|
||||
logger.debug("attachAppToken middleware");
|
||||
|
||||
if (!ctx.saleorApiUrl) {
|
||||
logger.debug("ctx.saleorApiUrl not found, throwing");
|
||||
|
||||
throw new TRPCError({
|
||||
code: "BAD_REQUEST",
|
||||
message: "Missing saleorApiUrl in request",
|
||||
});
|
||||
}
|
||||
|
||||
const authData = await saleorApp.apl.get(ctx.saleorApiUrl);
|
||||
|
||||
if (!authData) {
|
||||
logger.debug("authData not found, throwing 401");
|
||||
|
||||
throw new TRPCError({
|
||||
code: "UNAUTHORIZED",
|
||||
message: "Missing auth data",
|
||||
});
|
||||
}
|
||||
|
||||
return next({
|
||||
ctx: {
|
||||
appToken: authData.token,
|
||||
saleorApiUrl: authData.saleorApiUrl,
|
||||
appId: authData.appId,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
const validateClientToken = middleware(async ({ ctx, next, meta }) => {
|
||||
logger.debug(
|
||||
{
|
||||
permissions: meta?.requiredClientPermissions,
|
||||
},
|
||||
"Calling validateClientToken middleware with permissions required",
|
||||
);
|
||||
|
||||
if (!ctx.token) {
|
||||
throw new TRPCError({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
message: "Missing token in request. This middleware can be used only in frontend",
|
||||
});
|
||||
}
|
||||
|
||||
if (!ctx.appId) {
|
||||
throw new TRPCError({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
message: "Missing appId in request. This middleware can be used after auth is attached",
|
||||
});
|
||||
}
|
||||
|
||||
if (!ctx.saleorApiUrl) {
|
||||
throw new TRPCError({
|
||||
code: "INTERNAL_SERVER_ERROR",
|
||||
message:
|
||||
"Missing saleorApiUrl in request. This middleware can be used after auth is attached",
|
||||
});
|
||||
}
|
||||
|
||||
if (!ctx.ssr) {
|
||||
try {
|
||||
logger.debug("trying to verify JWT token from frontend");
|
||||
logger.debug({ token: ctx.token ? `${ctx.token[0]}...` : undefined });
|
||||
|
||||
await verifyJWT({
|
||||
appId: ctx.appId,
|
||||
token: ctx.token,
|
||||
saleorApiUrl: ctx.saleorApiUrl,
|
||||
requiredPermissions: meta?.requiredClientPermissions ?? [],
|
||||
});
|
||||
} catch (e) {
|
||||
logger.debug("JWT verification failed, throwing");
|
||||
throw new ProtectedHandlerError("JWT verification failed: ", "JWT_VERIFICATION_FAILED");
|
||||
}
|
||||
}
|
||||
|
||||
return next({
|
||||
ctx: {
|
||||
...ctx,
|
||||
saleorApiUrl: ctx.saleorApiUrl,
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Construct common graphQL client and attach it to the context
|
||||
*
|
||||
* Can be used only if called from the frontend (react-query),
|
||||
* otherwise jwks validation will fail (if createCaller used)
|
||||
*
|
||||
* TODO Rethink middleware composition to enable safe server-side router calls
|
||||
*/
|
||||
export const protectedClientProcedure = procedure
|
||||
.use(attachAppToken)
|
||||
.use(validateClientToken)
|
||||
.use(async ({ ctx, next }) => {
|
||||
const client = createGraphQLClient({ saleorApiUrl: ctx.saleorApiUrl, token: ctx.appToken });
|
||||
|
||||
return next({
|
||||
ctx: {
|
||||
apiClient: client,
|
||||
appToken: ctx.appToken,
|
||||
saleorApiUrl: ctx.saleorApiUrl,
|
||||
appId: ctx.appId!,
|
||||
},
|
||||
});
|
||||
});
|
8
apps/orders-export/src/modules/trpc/trpc-app-router.ts
Normal file
8
apps/orders-export/src/modules/trpc/trpc-app-router.ts
Normal file
|
@ -0,0 +1,8 @@
|
|||
import { configurationRouter } from "../configuration/configuration.router";
|
||||
import { router } from "./trpc-server";
|
||||
|
||||
export const appRouter = router({
|
||||
configuration: configurationRouter,
|
||||
});
|
||||
|
||||
export type AppRouter = typeof appRouter;
|
15
apps/orders-export/src/modules/trpc/trpc-client.ts
Normal file
15
apps/orders-export/src/modules/trpc/trpc-client.ts
Normal file
|
@ -0,0 +1,15 @@
|
|||
import { createTRPCNext } from "@trpc/next";
|
||||
|
||||
import { createHttpBatchLink } from "@saleor/trpc";
|
||||
import { appBridgeInstance } from "../../pages/_app";
|
||||
import { AppRouter } from "./trpc-app-router";
|
||||
|
||||
export const trpcClient = createTRPCNext<AppRouter>({
|
||||
config() {
|
||||
return {
|
||||
links: [createHttpBatchLink(appBridgeInstance)],
|
||||
queryClientConfig: { defaultOptions: { queries: { refetchOnWindowFocus: false } } },
|
||||
};
|
||||
},
|
||||
ssr: false,
|
||||
});
|
31
apps/orders-export/src/modules/trpc/trpc-server.ts
Normal file
31
apps/orders-export/src/modules/trpc/trpc-server.ts
Normal file
|
@ -0,0 +1,31 @@
|
|||
import { initTRPC } from "@trpc/server";
|
||||
import { TrpcContext } from "@saleor/trpc";
|
||||
import { Permission } from "@saleor/app-sdk/types";
|
||||
import { ZodError } from "zod";
|
||||
|
||||
interface Meta {
|
||||
requiredClientPermissions?: Permission[];
|
||||
updateWebhooks?: boolean;
|
||||
}
|
||||
|
||||
const t = initTRPC
|
||||
.context<TrpcContext>()
|
||||
.meta<Meta>()
|
||||
.create({
|
||||
errorFormatter({ shape, error }) {
|
||||
return {
|
||||
...shape,
|
||||
data: {
|
||||
...shape.data,
|
||||
zodError:
|
||||
error.code === "BAD_REQUEST" && error.cause instanceof ZodError
|
||||
? error.cause.flatten()
|
||||
: null,
|
||||
},
|
||||
};
|
||||
},
|
||||
});
|
||||
|
||||
export const router = t.router;
|
||||
export const procedure = t.procedure;
|
||||
export const middleware = t.middleware;
|
45
apps/orders-export/src/pages/_app.tsx
Normal file
45
apps/orders-export/src/pages/_app.tsx
Normal file
|
@ -0,0 +1,45 @@
|
|||
import "@saleor/macaw-ui/next/style";
|
||||
import { trpcClient } from "@/modules/trpc/trpc-client";
|
||||
import { AppBridge, AppBridgeProvider } from "@saleor/app-sdk/app-bridge";
|
||||
import { RoutePropagator } from "@saleor/app-sdk/app-bridge/next";
|
||||
import { GraphQLProvider, NoSSRWrapper, ThemeSynchronizer } from "@saleor/apps-shared";
|
||||
import { Box, ThemeProvider } from "@saleor/macaw-ui/next";
|
||||
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
|
||||
import { AppProps } from "next/app";
|
||||
|
||||
/**
|
||||
* Ensure instance is a singleton.
|
||||
* TODO: This is React 18 issue, consider hiding this workaround inside app-sdk
|
||||
*/
|
||||
export const appBridgeInstance = typeof window !== "undefined" ? new AppBridge() : undefined;
|
||||
|
||||
const queryClient = new QueryClient({
|
||||
defaultOptions: {
|
||||
queries: {
|
||||
refetchOnWindowFocus: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
function NextApp({ Component, pageProps }: AppProps) {
|
||||
return (
|
||||
<NoSSRWrapper>
|
||||
<AppBridgeProvider appBridgeInstance={appBridgeInstance}>
|
||||
<GraphQLProvider>
|
||||
<ThemeProvider>
|
||||
<ThemeSynchronizer />
|
||||
<RoutePropagator />
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<Box padding={10}>
|
||||
<Component {...pageProps} />
|
||||
</Box>
|
||||
</QueryClientProvider>
|
||||
</ThemeProvider>
|
||||
</GraphQLProvider>
|
||||
</AppBridgeProvider>
|
||||
</NoSSRWrapper>
|
||||
);
|
||||
}
|
||||
|
||||
export default trpcClient.withTRPC(NextApp);
|
13
apps/orders-export/src/pages/_document.tsx
Normal file
13
apps/orders-export/src/pages/_document.tsx
Normal file
|
@ -0,0 +1,13 @@
|
|||
import { Html, Head, Main, NextScript } from "next/document";
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<Html lang="en">
|
||||
<Head />
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
45
apps/orders-export/src/pages/api/manifest.ts
Normal file
45
apps/orders-export/src/pages/api/manifest.ts
Normal file
|
@ -0,0 +1,45 @@
|
|||
import { createManifestHandler } from "@saleor/app-sdk/handlers/next";
|
||||
import { AppManifest } from "@saleor/app-sdk/types";
|
||||
|
||||
import packageJson from "../../../package.json";
|
||||
|
||||
export default createManifestHandler({
|
||||
async manifestFactory({ appBaseUrl }) {
|
||||
const iframeBaseUrl = process.env.APP_IFRAME_BASE_URL ?? appBaseUrl;
|
||||
const apiBaseURL = process.env.APP_API_BASE_URL ?? appBaseUrl;
|
||||
|
||||
const manifest: AppManifest = {
|
||||
about: "Export Saleor orders to spreadsheets",
|
||||
appUrl: iframeBaseUrl,
|
||||
author: "Saleor Commerce",
|
||||
/*
|
||||
* brand: {
|
||||
* logo: {
|
||||
* default: `${apiBaseURL}/logo.png`,
|
||||
* },
|
||||
* },
|
||||
*/
|
||||
dataPrivacyUrl: "https://saleor.io/legal/privacy/",
|
||||
extensions: [
|
||||
/**
|
||||
* Optionally, extend Dashboard with custom UIs
|
||||
* https://docs.saleor.io/docs/3.x/developer/extending/apps/extending-dashboard-with-apps
|
||||
*/
|
||||
],
|
||||
homepageUrl: "https://github.com/saleor/apps",
|
||||
id: "saleor.app.orders-export",
|
||||
name: "Orders Export",
|
||||
permissions: ["MANAGE_ORDERS"],
|
||||
requiredSaleorVersion: ">=3.14 <4",
|
||||
supportUrl: "https://github.com/saleor/apps/discussions",
|
||||
tokenTargetUrl: `${apiBaseURL}/api/register`,
|
||||
version: packageJson.version,
|
||||
/*
|
||||
* TODO Add webhooks disabled and enable then when configured
|
||||
*/
|
||||
webhooks: [],
|
||||
};
|
||||
|
||||
return manifest;
|
||||
},
|
||||
});
|
23
apps/orders-export/src/pages/api/register.ts
Normal file
23
apps/orders-export/src/pages/api/register.ts
Normal file
|
@ -0,0 +1,23 @@
|
|||
import { saleorApp } from "@/saleor-app";
|
||||
import { createAppRegisterHandler } from "@saleor/app-sdk/handlers/next";
|
||||
|
||||
const allowedUrlsPattern = process.env.ALLOWED_DOMAIN_PATTERN;
|
||||
|
||||
/**
|
||||
* Required endpoint, called by Saleor to install app.
|
||||
* It will exchange tokens with app, so saleorApp.apl will contain token
|
||||
*/
|
||||
export default createAppRegisterHandler({
|
||||
apl: saleorApp.apl,
|
||||
allowedSaleorUrls: [
|
||||
(url) => {
|
||||
if (allowedUrlsPattern) {
|
||||
const regex = new RegExp(allowedUrlsPattern);
|
||||
|
||||
return regex.test(url);
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
],
|
||||
});
|
9
apps/orders-export/src/pages/api/trpc/[trpc].ts
Normal file
9
apps/orders-export/src/pages/api/trpc/[trpc].ts
Normal file
|
@ -0,0 +1,9 @@
|
|||
import * as trpcNext from "@trpc/server/adapters/next";
|
||||
|
||||
import { appRouter } from "../../../modules/trpc/trpc-app-router";
|
||||
import { createTrpcContext } from "@saleor/trpc";
|
||||
|
||||
export default trpcNext.createNextApiHandler({
|
||||
router: appRouter,
|
||||
createContext: createTrpcContext,
|
||||
});
|
28
apps/orders-export/src/pages/configuration.tsx
Normal file
28
apps/orders-export/src/pages/configuration.tsx
Normal file
|
@ -0,0 +1,28 @@
|
|||
import { AppHeader } from "@/modules/ui/app-header";
|
||||
import { useAppBridge } from "@saleor/app-sdk/app-bridge";
|
||||
import { Layout } from "@saleor/apps-ui";
|
||||
import { Box, Text } from "@saleor/macaw-ui/next";
|
||||
import { NextPage } from "next";
|
||||
|
||||
const ConfigurationPage: NextPage = () => {
|
||||
const { appBridgeState } = useAppBridge();
|
||||
|
||||
if (!appBridgeState) {
|
||||
throw new Error("AppBridge is not available.");
|
||||
}
|
||||
|
||||
if (appBridgeState.user?.permissions.includes("MANAGE_APPS") === false) {
|
||||
return <Text>You do not have permission to access this page.</Text>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<AppHeader />
|
||||
<Layout.AppSection marginBottom={14} heading="todo">
|
||||
TODO
|
||||
</Layout.AppSection>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConfigurationPage;
|
30
apps/orders-export/src/pages/index.tsx
Normal file
30
apps/orders-export/src/pages/index.tsx
Normal file
|
@ -0,0 +1,30 @@
|
|||
import { NextPage } from "next";
|
||||
import { useAppBridge } from "@saleor/app-sdk/app-bridge";
|
||||
import { useEffect } from "react";
|
||||
import { useIsMounted } from "usehooks-ts";
|
||||
import { useRouter } from "next/router";
|
||||
import { isInIframe } from "@saleor/apps-shared";
|
||||
|
||||
const IndexPage: NextPage = () => {
|
||||
const { appBridgeState } = useAppBridge();
|
||||
const isMounted = useIsMounted();
|
||||
const { replace } = useRouter();
|
||||
|
||||
useEffect(() => {
|
||||
if (isMounted() && appBridgeState?.ready) {
|
||||
replace("/configuration");
|
||||
}
|
||||
}, [isMounted, appBridgeState?.ready, replace]);
|
||||
|
||||
if (isInIframe()) {
|
||||
return <p>Loading</p>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>Saleor App - Orders Export</h1>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default IndexPage;
|
35
apps/orders-export/src/saleor-app.ts
Normal file
35
apps/orders-export/src/saleor-app.ts
Normal file
|
@ -0,0 +1,35 @@
|
|||
import { APL, FileAPL, SaleorCloudAPL, UpstashAPL } from "@saleor/app-sdk/APL";
|
||||
import { SaleorApp } from "@saleor/app-sdk/saleor-app";
|
||||
|
||||
const aplType = process.env.APL ?? "file";
|
||||
|
||||
export let apl: APL;
|
||||
|
||||
switch (aplType) {
|
||||
case "upstash":
|
||||
apl = new UpstashAPL();
|
||||
|
||||
break;
|
||||
case "file":
|
||||
apl = new FileAPL();
|
||||
|
||||
break;
|
||||
case "saleor-cloud": {
|
||||
if (!process.env.REST_APL_ENDPOINT || !process.env.REST_APL_TOKEN) {
|
||||
throw new Error("Rest APL is not configured - missing env variables. Check saleor-app.ts");
|
||||
}
|
||||
|
||||
apl = new SaleorCloudAPL({
|
||||
resourceUrl: process.env.REST_APL_ENDPOINT,
|
||||
token: process.env.REST_APL_TOKEN,
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new Error("Invalid APL config, ");
|
||||
}
|
||||
}
|
||||
export const saleorApp = new SaleorApp({
|
||||
apl,
|
||||
});
|
1
apps/orders-export/src/setup-tests.ts
Normal file
1
apps/orders-export/src/setup-tests.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export {};
|
23
apps/orders-export/tsconfig.json
Normal file
23
apps/orders-export/tsconfig.json
Normal file
|
@ -0,0 +1,23 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true,
|
||||
"paths": {
|
||||
"@/*": ["./src/*"]
|
||||
}
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
28
apps/orders-export/turbo.json
Normal file
28
apps/orders-export/turbo.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"extends": ["//"],
|
||||
"$schema": "https://turbo.build/schema.json",
|
||||
"pipeline": {
|
||||
"build": {
|
||||
"env": [
|
||||
"APL",
|
||||
"APP_DEBUG",
|
||||
"NODE_ENV",
|
||||
"SECRET_KEY",
|
||||
"ALLOWED_DOMAIN_PATTERN",
|
||||
"REST_APL_ENDPOINT",
|
||||
"REST_APL_TOKEN",
|
||||
"NEXT_PUBLIC_VERCEL_ENV",
|
||||
"VERCEL_URL",
|
||||
"PORT",
|
||||
"SENTRY_ORG",
|
||||
"SENTRY_PROJECT",
|
||||
"SENTRY_DSN",
|
||||
"SENTRY_AUTH_TOKEN",
|
||||
"NEXT_PUBLIC_SENTRY_DSN",
|
||||
"SENTRY_ENVIRONMENT",
|
||||
"APP_IFRAME_BASE_URL",
|
||||
"APP_API_BASE_URL"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
17
apps/orders-export/vitest.config.ts
Normal file
17
apps/orders-export/vitest.config.ts
Normal file
|
@ -0,0 +1,17 @@
|
|||
import react from "@vitejs/plugin-react";
|
||||
|
||||
import { defineConfig } from "vitest/config";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [react()],
|
||||
test: {
|
||||
passWithNoTests: true,
|
||||
environment: "jsdom",
|
||||
setupFiles: "./src/setup-tests.ts",
|
||||
css: false,
|
||||
alias: {
|
||||
"@": "./src",
|
||||
},
|
||||
},
|
||||
});
|
157
pnpm-lock.yaml
157
pnpm-lock.yaml
|
@ -1,5 +1,9 @@
|
|||
lockfileVersion: '6.0'
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
overrides:
|
||||
'@saleor/app-sdk': 0.43.1
|
||||
|
||||
|
@ -896,6 +900,145 @@ importers:
|
|||
specifier: 5.1.6
|
||||
version: 5.1.6
|
||||
|
||||
apps/orders-export:
|
||||
dependencies:
|
||||
'@hookform/resolvers':
|
||||
specifier: ^3.1.0
|
||||
version: 3.1.0(react-hook-form@7.44.3)
|
||||
'@saleor/app-sdk':
|
||||
specifier: 0.43.1
|
||||
version: 0.43.1(graphql@16.7.1)(next@13.4.8)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@saleor/apps-shared':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/shared
|
||||
'@saleor/apps-ui':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/ui
|
||||
'@saleor/macaw-ui':
|
||||
specifier: 0.8.0-pre.127
|
||||
version: 0.8.0-pre.127(@types/react-dom@18.2.5)(@types/react@18.2.5)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@saleor/react-hook-form-macaw':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/react-hook-form-macaw
|
||||
'@saleor/trpc':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/trpc
|
||||
'@sentry/nextjs':
|
||||
specifier: 7.67.0
|
||||
version: 7.67.0(next@13.4.8)(react@18.2.0)
|
||||
'@tanstack/react-query':
|
||||
specifier: ^4.29.19
|
||||
version: 4.29.19(react-dom@18.2.0)(react@18.2.0)
|
||||
'@trpc/client':
|
||||
specifier: 10.38.1
|
||||
version: 10.38.1(@trpc/server@10.38.1)
|
||||
'@trpc/next':
|
||||
specifier: 10.38.1
|
||||
version: 10.38.1(@tanstack/react-query@4.29.19)(@trpc/client@10.38.1)(@trpc/react-query@10.38.1)(@trpc/server@10.38.1)(next@13.4.8)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@trpc/react-query':
|
||||
specifier: 10.38.1
|
||||
version: 10.38.1(@tanstack/react-query@4.29.19)(@trpc/client@10.38.1)(@trpc/server@10.38.1)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@trpc/server':
|
||||
specifier: 10.38.1
|
||||
version: 10.38.1
|
||||
'@urql/exchange-auth':
|
||||
specifier: ^2.1.4
|
||||
version: 2.1.4(graphql@16.7.1)
|
||||
'@vitejs/plugin-react':
|
||||
specifier: 4.0.4
|
||||
version: 4.0.4(vite@4.4.8)
|
||||
graphql:
|
||||
specifier: 16.7.1
|
||||
version: 16.7.1
|
||||
graphql-tag:
|
||||
specifier: ^2.12.6
|
||||
version: 2.12.6(graphql@16.7.1)
|
||||
jsdom:
|
||||
specifier: ^20.0.3
|
||||
version: 20.0.3
|
||||
next:
|
||||
specifier: 13.4.8
|
||||
version: 13.4.8(@babel/core@7.22.11)(react-dom@18.2.0)(react@18.2.0)
|
||||
pino:
|
||||
specifier: ^8.14.1
|
||||
version: 8.14.1
|
||||
pino-pretty:
|
||||
specifier: ^10.0.0
|
||||
version: 10.0.0
|
||||
react:
|
||||
specifier: 18.2.0
|
||||
version: 18.2.0
|
||||
react-dom:
|
||||
specifier: 18.2.0
|
||||
version: 18.2.0(react@18.2.0)
|
||||
react-error-boundary:
|
||||
specifier: 4.0.10
|
||||
version: 4.0.10(react@18.2.0)
|
||||
react-hook-form:
|
||||
specifier: ^7.43.9
|
||||
version: 7.44.3(react@18.2.0)
|
||||
urql:
|
||||
specifier: ^4.0.4
|
||||
version: 4.0.4(graphql@16.7.1)(react@18.2.0)
|
||||
usehooks-ts:
|
||||
specifier: ^2.9.1
|
||||
version: 2.9.1(react-dom@18.2.0)(react@18.2.0)
|
||||
vite:
|
||||
specifier: 4.4.8
|
||||
version: 4.4.8(@types/node@18.15.3)
|
||||
vitest:
|
||||
specifier: 0.34.1
|
||||
version: 0.34.1(jsdom@20.0.3)
|
||||
zod:
|
||||
specifier: 3.21.4
|
||||
version: 3.21.4
|
||||
devDependencies:
|
||||
'@graphql-codegen/cli':
|
||||
specifier: 4.0.1
|
||||
version: 4.0.1(@babel/core@7.22.11)(@types/node@18.15.3)(graphql@16.7.1)
|
||||
'@graphql-codegen/introspection':
|
||||
specifier: 4.0.0
|
||||
version: 4.0.0(graphql@16.7.1)
|
||||
'@graphql-codegen/typed-document-node':
|
||||
specifier: 5.0.1
|
||||
version: 5.0.1(graphql@16.7.1)
|
||||
'@graphql-codegen/typescript':
|
||||
specifier: 4.0.1
|
||||
version: 4.0.1(graphql@16.7.1)
|
||||
'@graphql-codegen/typescript-operations':
|
||||
specifier: 4.0.1
|
||||
version: 4.0.1(graphql@16.7.1)
|
||||
'@graphql-codegen/typescript-urql':
|
||||
specifier: 3.7.3
|
||||
version: 3.7.3(graphql-tag@2.12.6)(graphql@16.7.1)
|
||||
'@graphql-typed-document-node/core':
|
||||
specifier: 3.2.0
|
||||
version: 3.2.0(graphql@16.7.1)
|
||||
'@testing-library/react':
|
||||
specifier: ^14.0.0
|
||||
version: 14.0.0(react-dom@18.2.0)(react@18.2.0)
|
||||
'@testing-library/react-hooks':
|
||||
specifier: ^8.0.1
|
||||
version: 8.0.1(@types/react@18.2.5)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@types/react':
|
||||
specifier: 18.2.5
|
||||
version: 18.2.5
|
||||
'@types/react-dom':
|
||||
specifier: 18.2.5
|
||||
version: 18.2.5
|
||||
eslint:
|
||||
specifier: 8.46.0
|
||||
version: 8.46.0
|
||||
eslint-config-saleor:
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/eslint-config-saleor
|
||||
node-mocks-http:
|
||||
specifier: ^1.12.2
|
||||
version: 1.12.2
|
||||
typescript:
|
||||
specifier: 5.1.6
|
||||
version: 5.1.6
|
||||
|
||||
apps/products-feed:
|
||||
dependencies:
|
||||
'@aws-sdk/client-s3':
|
||||
|
@ -5782,6 +5925,7 @@ packages:
|
|||
engines: {node: '>=6.9.0'}
|
||||
dependencies:
|
||||
regenerator-runtime: 0.13.11
|
||||
dev: true
|
||||
|
||||
/@babel/template@7.22.5:
|
||||
resolution: {integrity: sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==}
|
||||
|
@ -12876,6 +13020,7 @@ packages:
|
|||
|
||||
/chevrotain@6.5.0:
|
||||
resolution: {integrity: sha512-BwqQ/AgmKJ8jcMEjaSnfMybnKMgGTrtDKowfTP3pX4jwVy0kNjRsT/AP6h+wC3+3NC+X8X15VWBnTCQlX+wQFg==}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
regexp-to-ast: 0.4.0
|
||||
dev: false
|
||||
|
@ -15738,7 +15883,7 @@ packages:
|
|||
graphql: ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
|
||||
dependencies:
|
||||
graphql: 16.7.1
|
||||
tslib: 2.6.1
|
||||
tslib: 2.6.2
|
||||
|
||||
/graphql-ws@5.14.0(graphql@16.7.1):
|
||||
resolution: {integrity: sha512-itrUTQZP/TgswR4GSSYuwWUzrE/w5GhbwM2GX3ic2U7aw33jgEsayfIlvaj7/GcIvZgNMzsPTrE5hqPuFUiE5g==}
|
||||
|
@ -19455,7 +19600,7 @@ packages:
|
|||
peerDependencies:
|
||||
react: '>=16.13.1'
|
||||
dependencies:
|
||||
'@babel/runtime': 7.22.6
|
||||
'@babel/runtime': 7.22.11
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
|
@ -19948,6 +20093,7 @@ packages:
|
|||
|
||||
/regexp-to-ast@0.4.0:
|
||||
resolution: {integrity: sha512-4qf/7IsIKfSNHQXSwial1IFmfM1Cc/whNBQqRwe0V2stPe7KmN1U0tWQiIx6JiirgSrisjE0eECdNf7Tav1Ntw==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
|
@ -21181,6 +21327,7 @@ packages:
|
|||
|
||||
/tiny-emitter@2.1.0:
|
||||
resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
|
@ -21342,6 +21489,7 @@ packages:
|
|||
|
||||
/tslib@2.6.1:
|
||||
resolution: {integrity: sha512-t0hLfiEKfMUoqhG+U1oid7Pva4bbDPHYfJNiB7BiIjRkj1pyC++4N3huJfqY6aRH6VTB0rvtzQwjM4K6qpfOig==}
|
||||
dev: false
|
||||
|
||||
/tslib@2.6.2:
|
||||
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
|
||||
|
@ -21697,6 +21845,7 @@ packages:
|
|||
/unorm@1.6.0:
|
||||
resolution: {integrity: sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA==}
|
||||
engines: {node: '>= 0.4.0'}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
optional: true
|
||||
|
||||
|
@ -22615,7 +22764,3 @@ packages:
|
|||
|
||||
/zod@3.21.4:
|
||||
resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==}
|
||||
|
||||
settings:
|
||||
autoInstallPeers: true
|
||||
excludeLinksFromLockfile: false
|
||||
|
|
Loading…
Reference in a new issue