@unchainedshop/api 5.0.0-alpha.1 → 5.0.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,9 @@
1
+ import type { UnchainedCore } from '@unchainedshop/core';
2
+ import type { Enrollment } from '@unchainedshop/core-enrollments';
3
+ import DataLoader from 'dataloader';
4
+ declare const _default: (unchainedAPI: UnchainedCore) => DataLoader<{
5
+ orderId: string;
6
+ }, Enrollment | null, {
7
+ orderId: string;
8
+ }>;
9
+ export default _default;
@@ -0,0 +1,17 @@
1
+ import DataLoader from 'dataloader';
2
+ export default (unchainedAPI) => new DataLoader(async (queries) => {
3
+ const orderIds = [...new Set(queries.map((q) => q.orderId).filter(Boolean))];
4
+ const requestedOrderIds = new Set(orderIds);
5
+ const enrollments = await unchainedAPI.modules.enrollments.findEnrollmentsByOrderIds({
6
+ orderIds,
7
+ });
8
+ const enrollmentMap = {};
9
+ for (const enrollment of enrollments) {
10
+ for (const period of enrollment.periods || []) {
11
+ if (period.orderId && requestedOrderIds.has(period.orderId) && !enrollmentMap[period.orderId]) {
12
+ enrollmentMap[period.orderId] = enrollment;
13
+ }
14
+ }
15
+ }
16
+ return queries.map((q) => enrollmentMap[q.orderId] ?? null);
17
+ });
@@ -171,6 +171,11 @@ declare const loaders: (unchainedAPI: UnchainedCore) => {
171
171
  }, import("@unchainedshop/core-quotations").Quotation | null, {
172
172
  quotationId: string;
173
173
  }>;
174
+ tokenExportStatusLoader: import("dataloader")<{
175
+ token: import("@unchainedshop/core-warehousing").TokenSurrogate;
176
+ }, import("@unchainedshop/core-warehousing").TokenStatus, {
177
+ token: import("@unchainedshop/core-warehousing").TokenSurrogate;
178
+ }>;
174
179
  };
175
180
  export type UnchainedLoaders = Awaited<ReturnType<typeof loaders>>;
176
181
  export default loaders;
@@ -27,6 +27,7 @@ import paymentProviderLoader from "./paymentProviderLoader.js";
27
27
  import warehousingProviderLoader from "./warehousingProviderLoader.js";
28
28
  import orderLoader from "./orderLoader.js";
29
29
  import quotationLoader from "./quotationLoader.js";
30
+ import tokenExportStatusLoader from "./tokenExportStatusLoader.js";
30
31
  const loaders = (unchainedAPI) => {
31
32
  return {
32
33
  assortmentLoader: assortmentLoader(unchainedAPI),
@@ -58,6 +59,7 @@ const loaders = (unchainedAPI) => {
58
59
  warehousingProviderLoader: warehousingProviderLoader(unchainedAPI),
59
60
  orderLoader: orderLoader(unchainedAPI),
60
61
  quotationLoader: quotationLoader(unchainedAPI),
62
+ tokenExportStatusLoader: tokenExportStatusLoader(unchainedAPI),
61
63
  };
62
64
  };
63
65
  export default loaders;
@@ -0,0 +1,9 @@
1
+ import type { UnchainedCore } from '@unchainedshop/core';
2
+ import type { OrderDelivery } from '@unchainedshop/core-orders';
3
+ import DataLoader from 'dataloader';
4
+ declare const _default: (unchainedAPI: UnchainedCore) => DataLoader<{
5
+ orderDeliveryId: string;
6
+ }, OrderDelivery | null, {
7
+ orderDeliveryId: string;
8
+ }>;
9
+ export default _default;
@@ -0,0 +1,12 @@
1
+ import DataLoader from 'dataloader';
2
+ export default (unchainedAPI) => new DataLoader(async (queries) => {
3
+ const orderDeliveryIds = [...new Set(queries.map((q) => q.orderDeliveryId).filter(Boolean))];
4
+ const deliveries = await unchainedAPI.modules.orders.deliveries.findDeliveries({
5
+ orderDeliveryIds,
6
+ });
7
+ const deliveryMap = {};
8
+ for (const delivery of deliveries) {
9
+ deliveryMap[delivery._id] = delivery;
10
+ }
11
+ return queries.map((q) => deliveryMap[q.orderDeliveryId] ?? null);
12
+ });
@@ -0,0 +1,9 @@
1
+ import type { UnchainedCore } from '@unchainedshop/core';
2
+ import type { OrderDiscount } from '@unchainedshop/core-orders';
3
+ import DataLoader from 'dataloader';
4
+ declare const _default: (unchainedAPI: UnchainedCore) => DataLoader<{
5
+ orderId: string;
6
+ }, OrderDiscount[], {
7
+ orderId: string;
8
+ }>;
9
+ export default _default;
@@ -0,0 +1,17 @@
1
+ import DataLoader from 'dataloader';
2
+ export default (unchainedAPI) => new DataLoader(async (queries) => {
3
+ const orderIds = [...new Set(queries.map((q) => q.orderId).filter(Boolean))];
4
+ const discounts = await unchainedAPI.modules.orders.discounts.findOrderDiscounts({
5
+ orderIds,
6
+ });
7
+ const discountsMap = {};
8
+ for (const discount of discounts) {
9
+ if (!discountsMap[discount.orderId]) {
10
+ discountsMap[discount.orderId] = [discount];
11
+ }
12
+ else {
13
+ discountsMap[discount.orderId].push(discount);
14
+ }
15
+ }
16
+ return queries.map((q) => discountsMap[q.orderId] || []);
17
+ });
@@ -0,0 +1,9 @@
1
+ import type { UnchainedCore } from '@unchainedshop/core';
2
+ import type { OrderPayment } from '@unchainedshop/core-orders';
3
+ import DataLoader from 'dataloader';
4
+ declare const _default: (unchainedAPI: UnchainedCore) => DataLoader<{
5
+ orderPaymentId: string;
6
+ }, OrderPayment | null, {
7
+ orderPaymentId: string;
8
+ }>;
9
+ export default _default;
@@ -0,0 +1,12 @@
1
+ import DataLoader from 'dataloader';
2
+ export default (unchainedAPI) => new DataLoader(async (queries) => {
3
+ const orderPaymentIds = [...new Set(queries.map((q) => q.orderPaymentId).filter(Boolean))];
4
+ const payments = await unchainedAPI.modules.orders.payments.findOrderPayments({
5
+ orderPaymentIds,
6
+ });
7
+ const paymentMap = {};
8
+ for (const payment of payments) {
9
+ paymentMap[payment._id] = payment;
10
+ }
11
+ return queries.map((q) => paymentMap[q.orderPaymentId] ?? null);
12
+ });
@@ -0,0 +1,9 @@
1
+ import type { UnchainedCore } from '@unchainedshop/core';
2
+ import type { OrderPosition } from '@unchainedshop/core-orders';
3
+ import DataLoader from 'dataloader';
4
+ declare const _default: (unchainedAPI: UnchainedCore) => DataLoader<{
5
+ orderId: string;
6
+ }, OrderPosition[], {
7
+ orderId: string;
8
+ }>;
9
+ export default _default;
@@ -0,0 +1,17 @@
1
+ import DataLoader from 'dataloader';
2
+ export default (unchainedAPI) => new DataLoader(async (queries) => {
3
+ const orderIds = [...new Set(queries.map((q) => q.orderId).filter(Boolean))];
4
+ const positions = await unchainedAPI.modules.orders.positions.findOrderPositions({
5
+ orderIds,
6
+ });
7
+ const positionsMap = {};
8
+ for (const position of positions) {
9
+ if (!positionsMap[position.orderId]) {
10
+ positionsMap[position.orderId] = [position];
11
+ }
12
+ else {
13
+ positionsMap[position.orderId].push(position);
14
+ }
15
+ }
16
+ return queries.map((q) => positionsMap[q.orderId] || []);
17
+ });
@@ -0,0 +1,9 @@
1
+ import type { UnchainedCore } from '@unchainedshop/core';
2
+ import type { TokenSurrogate, TokenStatus as TokenStatusType } from '@unchainedshop/core-warehousing';
3
+ import DataLoader from 'dataloader';
4
+ declare const _default: (unchainedAPI: UnchainedCore) => DataLoader<{
5
+ token: TokenSurrogate;
6
+ }, TokenStatusType, {
7
+ token: TokenSurrogate;
8
+ }>;
9
+ export default _default;
@@ -0,0 +1,31 @@
1
+ import { TokenStatus } from '@unchainedshop/core-warehousing';
2
+ import { WorkStatus } from '@unchainedshop/core-worker';
3
+ import DataLoader from 'dataloader';
4
+ export default (unchainedAPI) => new DataLoader(async (queries) => {
5
+ const tokensNeedingWorkCheck = [];
6
+ for (const { token } of queries) {
7
+ if (!(token.walletAddress && !token.userId)) {
8
+ tokensNeedingWorkCheck.push(token);
9
+ }
10
+ }
11
+ let exportingTokenIds;
12
+ if (tokensNeedingWorkCheck.length > 0) {
13
+ const workItems = await unchainedAPI.modules.worker.findWorkQueue({
14
+ types: ['EXPORT_TOKEN'],
15
+ status: [WorkStatus.NEW, WorkStatus.ALLOCATED],
16
+ });
17
+ exportingTokenIds = new Set(workItems.map((item) => item.input?.token?._id).filter(Boolean));
18
+ }
19
+ else {
20
+ exportingTokenIds = new Set();
21
+ }
22
+ return queries.map(({ token }) => {
23
+ if (token.walletAddress && !token.userId) {
24
+ return TokenStatus.DECENTRALIZED;
25
+ }
26
+ if (exportingTokenIds.has(token._id)) {
27
+ return TokenStatus.EXPORTING;
28
+ }
29
+ return TokenStatus.CENTRALIZED;
30
+ });
31
+ });
@@ -3,7 +3,7 @@ import { getNormalizedAssortmentDetails } from "../../../utils/getNormalizedAsso
3
3
  export default async function getAssortment(context, params) {
4
4
  const { modules } = context;
5
5
  const { assortmentId, slug } = params;
6
- let query = {};
6
+ let query;
7
7
  if (assortmentId)
8
8
  query = { assortmentId };
9
9
  else if (slug)
@@ -3,7 +3,7 @@ import { getNormalizedProductDetails } from "../../../utils/getNormalizedProduct
3
3
  export default async function getProduct(context, params) {
4
4
  const { modules } = context;
5
5
  const { productId, slug, sku } = params;
6
- let query = {};
6
+ let query;
7
7
  if (productId)
8
8
  query = { productId };
9
9
  else if (slug)
@@ -65,9 +65,8 @@ export async function createAuthContext(params, authConfig) {
65
65
  const fingerprintCookie = getCookie(UNCHAINED_FINGERPRINT_COOKIE_NAME);
66
66
  const verifyToken = createAuthHandler(authConfig);
67
67
  const authResult = token ? await verifyToken(token) : {};
68
- let fingerprintValid = true;
69
68
  if (authResult.fingerprintHash && fingerprintCookie) {
70
- fingerprintValid = verifyFingerprint(fingerprintCookie, authResult.fingerprintHash);
69
+ const fingerprintValid = verifyFingerprint(fingerprintCookie, authResult.fingerprintHash);
71
70
  if (!fingerprintValid) {
72
71
  logger.warn('Token sidejacking detected: fingerprint mismatch', {
73
72
  userId: authResult.userId,
@@ -81,7 +80,6 @@ export async function createAuthContext(params, authConfig) {
81
80
  logger.warn('Token sidejacking detected: fingerprint cookie missing', {
82
81
  userId: authResult.userId,
83
82
  });
84
- fingerprintValid = false;
85
83
  authResult.userId = undefined;
86
84
  authResult.tokenVersion = undefined;
87
85
  authResult.impersonatorId = undefined;
@@ -775,12 +775,12 @@ declare const types: {
775
775
  Token: {
776
776
  product: (token: import("@unchainedshop/core-warehousing").TokenSurrogate, params: never, { loaders }: import("../../context.ts").Context) => Promise<import("@unchainedshop/core-products").Product>;
777
777
  user: (token: import("@unchainedshop/core-warehousing").TokenSurrogate, params: never, { loaders }: import("../../context.ts").Context) => Promise<import("@unchainedshop/core-users").User | null>;
778
- status: (token: import("@unchainedshop/core-warehousing").TokenSurrogate, params: never, { services }: import("../../context.ts").Context) => Promise<import("@unchainedshop/core-warehousing").TokenStatus>;
778
+ status: (token: import("@unchainedshop/core-warehousing").TokenSurrogate, params: never, { loaders }: import("../../context.ts").Context) => Promise<import("@unchainedshop/core-warehousing").TokenStatus>;
779
779
  ercMetadata: (token: import("@unchainedshop/core-warehousing").TokenSurrogate, { forceLocale }: {
780
780
  forceLocale: string;
781
781
  }, context: import("../../context.ts").Context) => Promise<any>;
782
- isInvalidateable: (token: import("@unchainedshop/core-warehousing").TokenSurrogate, _params: never, { services }: import("../../context.ts").Context) => Promise<boolean>;
783
- accessKey: (token: import("@unchainedshop/core-warehousing").TokenSurrogate, params: never, requestContext: import("../../context.ts").Context) => Promise<string | null>;
782
+ isInvalidateable: (token: import("@unchainedshop/core-warehousing").TokenSurrogate, _params: never, { loaders, services }: import("../../context.ts").Context) => Promise<boolean>;
783
+ accessKey: (token: import("@unchainedshop/core-warehousing").TokenSurrogate, params: never, requestContext: import("../../context.ts").Context) => Promise<string>;
784
784
  };
785
785
  User: import("./user-types.ts").UserHelperTypes;
786
786
  WebAuthnCredentials: {
@@ -3,10 +3,10 @@ import type { TokenSurrogate } from '@unchainedshop/core-warehousing';
3
3
  export declare const Token: {
4
4
  product: (token: TokenSurrogate, params: never, { loaders }: Context) => Promise<import("@unchainedshop/core-products").Product>;
5
5
  user: (token: TokenSurrogate, params: never, { loaders }: Context) => Promise<import("@unchainedshop/core-users").User | null>;
6
- status: (token: TokenSurrogate, params: never, { services }: Context) => Promise<import("@unchainedshop/core-warehousing").TokenStatus>;
6
+ status: (token: TokenSurrogate, params: never, { loaders }: Context) => Promise<import("@unchainedshop/core-warehousing").TokenStatus>;
7
7
  ercMetadata: (token: TokenSurrogate, { forceLocale }: {
8
8
  forceLocale: string;
9
9
  }, context: Context) => Promise<any>;
10
- isInvalidateable: (token: TokenSurrogate, _params: never, { services }: Context) => Promise<boolean>;
11
- accessKey: (token: TokenSurrogate, params: never, requestContext: Context) => Promise<string | null>;
10
+ isInvalidateable: (token: TokenSurrogate, _params: never, { loaders, services }: Context) => Promise<boolean>;
11
+ accessKey: (token: TokenSurrogate, params: never, requestContext: Context) => Promise<string>;
12
12
  };
@@ -9,8 +9,8 @@ export const Token = {
9
9
  return null;
10
10
  return loaders.userLoader.load({ userId: token.userId });
11
11
  },
12
- status: async (token, params, { services }) => {
13
- return services.warehousing.resolveTokenStatus({ token });
12
+ status: async (token, params, { loaders }) => {
13
+ return loaders.tokenExportStatusLoader.load({ token });
14
14
  },
15
15
  ercMetadata: async (token, { forceLocale }, context) => {
16
16
  const { loaders, services } = context;
@@ -21,12 +21,13 @@ export const Token = {
21
21
  locale: forceLocale ? new Intl.Locale(forceLocale) : context.locale,
22
22
  });
23
23
  },
24
- isInvalidateable: async (token, _params, { services }) => {
25
- return services.warehousing.isTokenInvalidateable({ token });
24
+ isInvalidateable: async (token, _params, { loaders, services }) => {
25
+ const product = await loaders.productLoader.load({ productId: token.productId });
26
+ return services.warehousing.isTokenInvalidateable({ token, product });
26
27
  },
27
28
  accessKey: async (token, params, requestContext) => {
28
29
  const { modules } = requestContext;
29
30
  await checkAction(requestContext, actions.updateToken, [undefined, { tokenId: token._id }]);
30
- return modules.warehousing.buildAccessKeyForToken(token._id);
31
+ return modules.warehousing.buildAccessKeyFromToken(token);
31
32
  },
32
33
  };
@@ -20,7 +20,7 @@ export interface PushSubscription {
20
20
  }
21
21
  export interface Web3Address {
22
22
  address: string;
23
- nonce?: number;
23
+ nonce?: string;
24
24
  verified: boolean;
25
25
  }
26
26
  export interface WebAuthnCredentials {
package/lib/roles/all.js CHANGED
@@ -20,7 +20,7 @@ export const all = (role, actions) => {
20
20
  if (isOwnedByUser)
21
21
  return true;
22
22
  const accessKeyHeader = context.getHeader('x-token-accesskey');
23
- const accessKey = await context.modules.warehousing.buildAccessKeyForToken(tokenId);
23
+ const accessKey = await context.modules.warehousing.buildAccessKeyFromToken(token);
24
24
  if (accessKeyHeader && accessKey && (await timingSafeStringEqual(accessKeyHeader, accessKey)))
25
25
  return true;
26
26
  return false;
@@ -82,7 +82,7 @@ export default [
82
82
 
83
83
  type Web3Address {
84
84
  address: String!
85
- nonce: Int
85
+ nonce: String
86
86
  verified: Boolean!
87
87
  }
88
88
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@unchainedshop/api",
3
3
  "description": "GraphQL API layer for the Unchained Engine with Express/Fastify adapters and MCP server",
4
- "version": "5.0.0-alpha.1",
4
+ "version": "5.0.0-alpha.2",
5
5
  "main": "lib/api-index.js",
6
6
  "types": "lib/api-index.d.ts",
7
7
  "type": "module",
@@ -62,7 +62,7 @@
62
62
  "peerDependencies": {
63
63
  "@ai-sdk/mcp": "^1",
64
64
  "@fastify/cookie": ">= 11 < 12",
65
- "@fastify/multipart": ">= 9 < 10",
65
+ "@fastify/multipart": ">= 10 < 11",
66
66
  "@fastify/static": ">= 9 < 10",
67
67
  "@modelcontextprotocol/sdk": ">= 1 < 2",
68
68
  "ai": ">= 6 < 7",
@@ -123,7 +123,7 @@
123
123
  "devDependencies": {
124
124
  "@ai-sdk/mcp": "^1.0.5",
125
125
  "@fastify/cookie": "^11.0.2",
126
- "@fastify/multipart": "^9.0.3",
126
+ "@fastify/multipart": "^10.0.0",
127
127
  "@fastify/static": "^9.0.0",
128
128
  "@modelcontextprotocol/sdk": "^1.15.1",
129
129
  "@types/cookie-parser": "^1.4.8",