@webiny/api-core 6.5.0-beta.0 → 6.5.0-beta.10
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/ApiCoreFeature.js +4 -0
- package/ApiCoreFeature.js.map +1 -1
- package/createApiCore.d.ts +1 -1
- package/createApiCore.js +3 -1
- package/createApiCore.js.map +1 -1
- package/features/ai/Ai.js +16 -6
- package/features/ai/Ai.js.map +1 -1
- package/features/ai/AiModelRegistry.d.ts +3 -1
- package/features/ai/AiModelRegistry.js +16 -7
- package/features/ai/AiModelRegistry.js.map +1 -1
- package/features/ai/DEVELOPERS.md +69 -12
- package/features/ai/OpenRouterSdkFactory.d.ts +10 -0
- package/features/ai/OpenRouterSdkFactory.js +112 -0
- package/features/ai/OpenRouterSdkFactory.js.map +1 -0
- package/features/ai/RemoteAiModelCatalog.d.ts +13 -0
- package/features/ai/RemoteAiModelCatalog.js +74 -0
- package/features/ai/RemoteAiModelCatalog.js.map +1 -0
- package/features/ai/abstractions.d.ts +25 -0
- package/features/ai/abstractions.js +2 -1
- package/features/ai/abstractions.js.map +1 -1
- package/features/ai/feature.js +4 -0
- package/features/ai/feature.js.map +1 -1
- package/features/ai/index.d.ts +2 -2
- package/features/ai/index.js +1 -1
- package/features/debugger/Debugger.d.ts +23 -0
- package/features/debugger/Debugger.js +134 -0
- package/features/debugger/Debugger.js.map +1 -0
- package/features/debugger/GraphQLDebuggerTransport.d.ts +26 -0
- package/features/debugger/GraphQLDebuggerTransport.js +45 -0
- package/features/debugger/GraphQLDebuggerTransport.js.map +1 -0
- package/features/debugger/__tests__/matchNamespace.test.d.ts +1 -0
- package/features/debugger/__tests__/matchNamespace.test.js +67 -0
- package/features/debugger/__tests__/matchNamespace.test.js.map +1 -0
- package/features/debugger/__tests__/serialize.test.d.ts +1 -0
- package/features/debugger/__tests__/serialize.test.js +220 -0
- package/features/debugger/__tests__/serialize.test.js.map +1 -0
- package/features/debugger/abstractions.d.ts +100 -0
- package/features/debugger/abstractions.js +6 -0
- package/features/debugger/abstractions.js.map +1 -0
- package/features/debugger/feature.d.ts +5 -0
- package/features/debugger/feature.js +15 -0
- package/features/debugger/feature.js.map +1 -0
- package/features/debugger/index.d.ts +7 -0
- package/features/debugger/index.js +7 -0
- package/features/debugger/limits.d.ts +18 -0
- package/features/debugger/limits.js +11 -0
- package/features/debugger/limits.js.map +1 -0
- package/features/debugger/matchNamespace.d.ts +21 -0
- package/features/debugger/matchNamespace.js +48 -0
- package/features/debugger/matchNamespace.js.map +1 -0
- package/features/debugger/permissions.d.ts +33 -0
- package/features/debugger/permissions.js +12 -0
- package/features/debugger/permissions.js.map +1 -0
- package/features/debugger/permissionsSchema.d.ts +17 -0
- package/features/debugger/permissionsSchema.js +18 -0
- package/features/debugger/permissionsSchema.js.map +1 -0
- package/features/debugger/plugins.d.ts +4 -0
- package/features/debugger/plugins.js +82 -0
- package/features/debugger/plugins.js.map +1 -0
- package/features/debugger/serialize.d.ts +11 -0
- package/features/debugger/serialize.js +121 -0
- package/features/debugger/serialize.js.map +1 -0
- package/features/httpClient/HttpClient.d.ts +7 -0
- package/features/httpClient/HttpClient.js +72 -0
- package/features/httpClient/HttpClient.js.map +1 -0
- package/features/httpClient/HttpRequestError.d.ts +10 -0
- package/features/httpClient/HttpRequestError.js +13 -0
- package/features/httpClient/HttpRequestError.js.map +1 -0
- package/features/httpClient/HttpStatusError.d.ts +12 -0
- package/features/httpClient/HttpStatusError.js +12 -0
- package/features/httpClient/HttpStatusError.js.map +1 -0
- package/features/httpClient/abstractions.d.ts +32 -0
- package/features/httpClient/abstractions.js +5 -0
- package/features/httpClient/abstractions.js.map +1 -0
- package/features/httpClient/feature.d.ts +4 -0
- package/features/httpClient/feature.js +11 -0
- package/features/httpClient/feature.js.map +1 -0
- package/features/httpClient/index.d.ts +3 -0
- package/features/httpClient/index.js +3 -0
- package/features/users/shared/loaders.js.map +1 -1
- package/graphql/wcp/graphql.js +2 -0
- package/graphql/wcp/graphql.js.map +1 -1
- package/idp/JwksCache.d.ts +7 -2
- package/idp/JwksCache.js +43 -16
- package/idp/JwksCache.js.map +1 -1
- package/idp/feature.js +1 -2
- package/idp/feature.js.map +1 -1
- package/legacy/security/plugins/secureHeaders.js +1 -0
- package/legacy/security/plugins/secureHeaders.js.map +1 -1
- package/package.json +25 -24
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { Request, RequestId, createBeforeHandlerPlugin, createHandlerResultPlugin } from "@webiny/handler";
|
|
2
|
+
import { Debugger } from "./abstractions.js";
|
|
3
|
+
import { IdentityContext } from "../security/IdentityContext/index.js";
|
|
4
|
+
import { parseNamespaceHeader } from "./matchNamespace.js";
|
|
5
|
+
import { canCaptureDebugData } from "./permissions.js";
|
|
6
|
+
import { GRAPHQL_TARGET } from "./GraphQLDebuggerTransport.js";
|
|
7
|
+
const DEBUG_HEADER = "x-webiny-debug";
|
|
8
|
+
const DEBUG_PERMISSION = "dev-tools.debug";
|
|
9
|
+
const isPlainObject = (value)=>"object" == typeof value && null !== value && !Array.isArray(value);
|
|
10
|
+
const isGraphQLResult = (value)=>isPlainObject(value) && ("data" in value || "errors" in value);
|
|
11
|
+
const resolveTarget = (payload)=>{
|
|
12
|
+
const candidate = Array.isArray(payload) ? payload.at(-1) : payload;
|
|
13
|
+
return isGraphQLResult(candidate) ? candidate : void 0;
|
|
14
|
+
};
|
|
15
|
+
const readOperationNames = (body)=>{
|
|
16
|
+
const operations = Array.isArray(body) ? body : [
|
|
17
|
+
body
|
|
18
|
+
];
|
|
19
|
+
return operations.map((operation)=>isPlainObject(operation) && "string" == typeof operation.operationName ? operation.operationName : void 0).filter((name)=>Boolean(name));
|
|
20
|
+
};
|
|
21
|
+
const createStartPlugin = ()=>createBeforeHandlerPlugin(async (context)=>{
|
|
22
|
+
try {
|
|
23
|
+
const request = context.container.resolve(Request);
|
|
24
|
+
const requested = parseNamespaceHeader(request.headers[DEBUG_HEADER]);
|
|
25
|
+
const namespaces = requested.length > 0 ? requested : [
|
|
26
|
+
"*"
|
|
27
|
+
];
|
|
28
|
+
context.container.resolve(Debugger).start({
|
|
29
|
+
namespaces
|
|
30
|
+
});
|
|
31
|
+
} catch {}
|
|
32
|
+
});
|
|
33
|
+
const createFlushPlugin = ()=>createHandlerResultPlugin(async (context, payload)=>{
|
|
34
|
+
try {
|
|
35
|
+
const debuggerService = context.container.resolve(Debugger);
|
|
36
|
+
const identityContext = context.container.resolve(IdentityContext);
|
|
37
|
+
const result = resolveTarget(payload);
|
|
38
|
+
if (!result) {
|
|
39
|
+
debuggerService.discard();
|
|
40
|
+
return payload;
|
|
41
|
+
}
|
|
42
|
+
if (identityContext.getIdentity().isAnonymous()) {
|
|
43
|
+
debuggerService.discard();
|
|
44
|
+
return payload;
|
|
45
|
+
}
|
|
46
|
+
const isAuthorized = identityContext.isAuthorizationEnabled() && await canCaptureDebugData(identityContext);
|
|
47
|
+
if (!isAuthorized) {
|
|
48
|
+
debuggerService.discard();
|
|
49
|
+
return payload;
|
|
50
|
+
}
|
|
51
|
+
let operations = [];
|
|
52
|
+
let requestId = "";
|
|
53
|
+
let url = "";
|
|
54
|
+
try {
|
|
55
|
+
const request = context.container.resolve(Request);
|
|
56
|
+
operations = readOperationNames(request.body);
|
|
57
|
+
requestId = context.container.resolve(RequestId).value;
|
|
58
|
+
url = request.url || "";
|
|
59
|
+
} catch {}
|
|
60
|
+
const target = {
|
|
61
|
+
type: GRAPHQL_TARGET,
|
|
62
|
+
result,
|
|
63
|
+
requestId,
|
|
64
|
+
operations,
|
|
65
|
+
url
|
|
66
|
+
};
|
|
67
|
+
await debuggerService.flush(target);
|
|
68
|
+
} catch (ex) {
|
|
69
|
+
console.error("Debugger failed to flush.", ex instanceof Error ? `${ex.name}: ${ex.message}` : "Unknown error.");
|
|
70
|
+
try {
|
|
71
|
+
context.container.resolve(Debugger).discard();
|
|
72
|
+
} catch {}
|
|
73
|
+
}
|
|
74
|
+
return payload;
|
|
75
|
+
});
|
|
76
|
+
const createDebuggerPlugins = ()=>[
|
|
77
|
+
createStartPlugin(),
|
|
78
|
+
createFlushPlugin()
|
|
79
|
+
];
|
|
80
|
+
export { DEBUG_HEADER, DEBUG_PERMISSION, createDebuggerPlugins };
|
|
81
|
+
|
|
82
|
+
//# sourceMappingURL=plugins.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/debugger/plugins.js","sources":["../../../src/features/debugger/plugins.ts"],"sourcesContent":["import {\n createBeforeHandlerPlugin,\n createHandlerResultPlugin,\n Request,\n RequestId\n} from \"@webiny/handler\";\nimport type { PluginCollection } from \"@webiny/plugins/types.js\";\nimport type { Context } from \"@webiny/handler/types.js\";\nimport { Debugger } from \"./abstractions.js\";\nimport { IdentityContext } from \"~/features/security/IdentityContext/index.js\";\nimport { parseNamespaceHeader } from \"./matchNamespace.js\";\nimport { canCaptureDebugData } from \"./permissions.js\";\nimport { GRAPHQL_TARGET, type IGraphQLDeliveryTarget } from \"./GraphQLDebuggerTransport.js\";\n\nexport const DEBUG_HEADER = \"x-webiny-debug\";\nexport const DEBUG_PERMISSION = \"dev-tools.debug\";\n\nconst isPlainObject = (value: unknown): value is Record<string, any> => {\n return typeof value === \"object\" && value !== null && !Array.isArray(value);\n};\n\n/**\n * A GraphQL result carries `data` or `errors`. Anything else - asset delivery, a plain string body -\n * is not ours to write to.\n */\nconst isGraphQLResult = (value: unknown): value is Record<string, any> => {\n return isPlainObject(value) && (\"data\" in value || \"errors\" in value);\n};\n\n/**\n * Batched requests produce an array of results. The session spans the whole HTTP request, so it is\n * attached once, to the last element - duplicating it per operation would multiply it against the\n * byte budget.\n */\nconst resolveTarget = (payload: unknown): Record<string, any> | undefined => {\n const candidate = Array.isArray(payload) ? payload.at(-1) : payload;\n return isGraphQLResult(candidate) ? candidate : undefined;\n};\n\nconst readOperationNames = (body: unknown): string[] => {\n const operations = Array.isArray(body) ? body : [body];\n return operations\n .map(operation => {\n return isPlainObject(operation) && typeof operation.operationName === \"string\"\n ? operation.operationName\n : undefined;\n })\n .filter((name): name is string => Boolean(name));\n};\n\n/**\n * Opens the capture session.\n *\n * Runs as a `BeforeHandlerPlugin`, so it is in place before the route handler and therefore before\n * any resolver. Note that `ContextPlugin`s run earlier still, so anything logged from one of those\n * is not captured.\n */\nconst createStartPlugin = () => {\n return createBeforeHandlerPlugin<Context>(async context => {\n /**\n * Must never throw: `ProcessBeforeHandlerPlugins` rethrows, which would turn a malformed\n * header into a 500 for the whole request.\n */\n try {\n const request = context.container.resolve(Request);\n\n /**\n * A session starts on every request, and the permission check at flush decides whether\n * anything is delivered or the whole thing is discarded.\n *\n * Nothing is required from the client. Asking for a request header would mean putting it\n * on the CORS allow-list, which is cached in preflight responses at CloudFront and in\n * the browser for a day - so rolling one out breaks requests intermittently until every\n * cache entry expires.\n *\n * The header is still honoured when present, so a direct API client can narrow what it\n * captures.\n */\n const requested = parseNamespaceHeader(request.headers[DEBUG_HEADER]);\n const namespaces = requested.length > 0 ? requested : [\"*\"];\n\n context.container.resolve(Debugger).start({ namespaces });\n } catch {\n // A debugger that cannot start is not a reason to fail the request.\n }\n });\n};\n\n/**\n * Performs the permission gate and hands the session to the transports.\n *\n * This runs once per HTTP response, in the `preSerialization` hook - deliberately not in\n * `graphql-after-query`, which also fires for the nested executions that CMS resolvers perform\n * internally and would attach the session to a result that is then discarded.\n */\nconst createFlushPlugin = () => {\n return createHandlerResultPlugin<Context>(async (context, payload) => {\n /**\n * The whole body is guarded: `preSerialization` rethrows whatever a `HandlerResultPlugin`\n * throws, so an error here would turn a working response into a 500 - and only for requests\n * that asked for debugging.\n */\n try {\n const debuggerService = context.container.resolve(Debugger);\n const identityContext = context.container.resolve(IdentityContext);\n\n const result = resolveTarget(payload);\n if (!result) {\n debuggerService.discard();\n return payload;\n }\n\n /**\n * Anonymous callers are rejected before permissions are resolved: `getPermission` runs\n * every registered Authorizer, some of which hit the database.\n */\n if (identityContext.getIdentity().isAnonymous()) {\n debuggerService.discard();\n return payload;\n }\n\n /**\n * `getPermission` returns a wildcard permission whenever authorization is disabled, so a\n * flush occurring inside a `withoutAuthorization` scope would pass for anyone.\n */\n const isAuthorized =\n identityContext.isAuthorizationEnabled() &&\n (await canCaptureDebugData(identityContext));\n\n if (!isAuthorized) {\n /**\n * Nothing is written. Reporting the denial would tell every unauthorized caller that\n * the feature exists, and the Admin app already knows from the identity's own\n * permissions whether capture is available to it.\n */\n debuggerService.discard();\n return payload;\n }\n\n let operations: string[] = [];\n let requestId = \"\";\n let url = \"\";\n try {\n const request = context.container.resolve(Request);\n operations = readOperationNames(request.body);\n requestId = context.container.resolve(RequestId).value;\n /**\n * Which endpoint this was. An Admin session talks to several - the main API and the\n * CMS manage, read and preview endpoints - and knowing which one produced an entry\n * is most of the context when reading a report.\n */\n url = request.url || \"\";\n } catch {\n // All three are conveniences; their absence must not stop the flush.\n }\n\n const target: IGraphQLDeliveryTarget = {\n type: GRAPHQL_TARGET,\n result,\n requestId,\n operations,\n url\n };\n\n await debuggerService.flush(target);\n } catch (ex) {\n /**\n * Only the message - never the session or the payload.\n */\n console.error(\n \"Debugger failed to flush.\",\n ex instanceof Error ? `${ex.name}: ${ex.message}` : \"Unknown error.\"\n );\n try {\n context.container.resolve(Debugger).discard();\n } catch {\n // Nothing further to do.\n }\n }\n\n /**\n * `preSerialization` ignores this return value and serializes the original payload object,\n * so every write above mutates `result` in place.\n */\n return payload;\n });\n};\n\nexport const createDebuggerPlugins = (): PluginCollection => {\n return [createStartPlugin(), createFlushPlugin()];\n};\n"],"names":["DEBUG_HEADER","DEBUG_PERMISSION","isPlainObject","value","Array","isGraphQLResult","resolveTarget","payload","candidate","undefined","readOperationNames","body","operations","operation","name","Boolean","createStartPlugin","createBeforeHandlerPlugin","context","request","Request","requested","parseNamespaceHeader","namespaces","Debugger","createFlushPlugin","createHandlerResultPlugin","debuggerService","identityContext","IdentityContext","result","isAuthorized","canCaptureDebugData","requestId","url","RequestId","target","GRAPHQL_TARGET","ex","console","Error","createDebuggerPlugins"],"mappings":";;;;;;AAcO,MAAMA,eAAe;AACrB,MAAMC,mBAAmB;AAEhC,MAAMC,gBAAgB,CAACC,QACZ,AAAiB,YAAjB,OAAOA,SAAsBA,AAAU,SAAVA,SAAkB,CAACC,MAAM,OAAO,CAACD;AAOzE,MAAME,kBAAkB,CAACF,QACdD,cAAcC,UAAW,WAAUA,SAAS,YAAYA,KAAI;AAQvE,MAAMG,gBAAgB,CAACC;IACnB,MAAMC,YAAYJ,MAAM,OAAO,CAACG,WAAWA,QAAQ,EAAE,CAAC,MAAMA;IAC5D,OAAOF,gBAAgBG,aAAaA,YAAYC;AACpD;AAEA,MAAMC,qBAAqB,CAACC;IACxB,MAAMC,aAAaR,MAAM,OAAO,CAACO,QAAQA,OAAO;QAACA;KAAK;IACtD,OAAOC,WACF,GAAG,CAACC,CAAAA,YACMX,cAAcW,cAAc,AAAmC,YAAnC,OAAOA,UAAU,aAAa,GAC3DA,UAAU,aAAa,GACvBJ,QAET,MAAM,CAAC,CAACK,OAAyBC,QAAQD;AAClD;AASA,MAAME,oBAAoB,IACfC,0BAAmC,OAAMC;QAK5C,IAAI;YACA,MAAMC,UAAUD,QAAQ,SAAS,CAAC,OAAO,CAACE;YAc1C,MAAMC,YAAYC,qBAAqBH,QAAQ,OAAO,CAACnB,aAAa;YACpE,MAAMuB,aAAaF,UAAU,MAAM,GAAG,IAAIA,YAAY;gBAAC;aAAI;YAE3DH,QAAQ,SAAS,CAAC,OAAO,CAACM,UAAU,KAAK,CAAC;gBAAED;YAAW;QAC3D,EAAE,OAAM,CAER;IACJ;AAUJ,MAAME,oBAAoB,IACfC,0BAAmC,OAAOR,SAASX;QAMtD,IAAI;YACA,MAAMoB,kBAAkBT,QAAQ,SAAS,CAAC,OAAO,CAACM;YAClD,MAAMI,kBAAkBV,QAAQ,SAAS,CAAC,OAAO,CAACW;YAElD,MAAMC,SAASxB,cAAcC;YAC7B,IAAI,CAACuB,QAAQ;gBACTH,gBAAgB,OAAO;gBACvB,OAAOpB;YACX;YAMA,IAAIqB,gBAAgB,WAAW,GAAG,WAAW,IAAI;gBAC7CD,gBAAgB,OAAO;gBACvB,OAAOpB;YACX;YAMA,MAAMwB,eACFH,gBAAgB,sBAAsB,MACrC,MAAMI,oBAAoBJ;YAE/B,IAAI,CAACG,cAAc;gBAMfJ,gBAAgB,OAAO;gBACvB,OAAOpB;YACX;YAEA,IAAIK,aAAuB,EAAE;YAC7B,IAAIqB,YAAY;YAChB,IAAIC,MAAM;YACV,IAAI;gBACA,MAAMf,UAAUD,QAAQ,SAAS,CAAC,OAAO,CAACE;gBAC1CR,aAAaF,mBAAmBS,QAAQ,IAAI;gBAC5Cc,YAAYf,QAAQ,SAAS,CAAC,OAAO,CAACiB,WAAW,KAAK;gBAMtDD,MAAMf,QAAQ,GAAG,IAAI;YACzB,EAAE,OAAM,CAER;YAEA,MAAMiB,SAAiC;gBACnC,MAAMC;gBACNP;gBACAG;gBACArB;gBACAsB;YACJ;YAEA,MAAMP,gBAAgB,KAAK,CAACS;QAChC,EAAE,OAAOE,IAAI;YAITC,QAAQ,KAAK,CACT,6BACAD,cAAcE,QAAQ,GAAGF,GAAG,IAAI,CAAC,EAAE,EAAEA,GAAG,OAAO,EAAE,GAAG;YAExD,IAAI;gBACApB,QAAQ,SAAS,CAAC,OAAO,CAACM,UAAU,OAAO;YAC/C,EAAE,OAAM,CAER;QACJ;QAMA,OAAOjB;IACX;AAGG,MAAMkC,wBAAwB,IAC1B;QAACzB;QAAqBS;KAAoB"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { IDebuggerLimits } from "./limits.js";
|
|
2
|
+
export interface ISerializeResult {
|
|
3
|
+
json: string;
|
|
4
|
+
bytes: number;
|
|
5
|
+
/** True if the value was replaced by the per-entry cap marker. */
|
|
6
|
+
truncated: boolean;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Serializes a captured payload to JSON, never throwing and always producing valid JSON.
|
|
10
|
+
*/
|
|
11
|
+
export declare const serialize: (value: unknown, limits: IDebuggerLimits) => ISerializeResult;
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
const UNDEFINED = "#[Undefined value]";
|
|
2
|
+
const CIRCULAR = "#[Circular]";
|
|
3
|
+
const DEPTH_LIMIT = "#[Depth limit]";
|
|
4
|
+
const isPlainValue = (value)=>{
|
|
5
|
+
const type = typeof value;
|
|
6
|
+
return "string" === type || "number" === type || "boolean" === type || null === value;
|
|
7
|
+
};
|
|
8
|
+
const describeBinary = (value)=>{
|
|
9
|
+
const name = value.constructor?.name || "TypedArray";
|
|
10
|
+
return `#[${name} ${value.byteLength} bytes]`;
|
|
11
|
+
};
|
|
12
|
+
const walk = (value, limits, seen, depth)=>{
|
|
13
|
+
if (void 0 === value) return UNDEFINED;
|
|
14
|
+
if (isPlainValue(value)) {
|
|
15
|
+
if ("string" == typeof value && value.length > limits.stringLength) {
|
|
16
|
+
const kept = value.slice(0, limits.stringLength);
|
|
17
|
+
return `${kept}…(${value.length - limits.stringLength} more)`;
|
|
18
|
+
}
|
|
19
|
+
if ("number" == typeof value && !Number.isFinite(value)) return String(value);
|
|
20
|
+
return value;
|
|
21
|
+
}
|
|
22
|
+
if ("bigint" == typeof value) return `${value.toString()}n`;
|
|
23
|
+
if ("symbol" == typeof value) return String(value);
|
|
24
|
+
if ("function" == typeof value) return `#[Function: ${value.name || "anonymous"}]`;
|
|
25
|
+
if (value instanceof Error) return {
|
|
26
|
+
name: value.name,
|
|
27
|
+
message: value.message,
|
|
28
|
+
stack: value.stack
|
|
29
|
+
};
|
|
30
|
+
if (value instanceof Date) return value.toISOString();
|
|
31
|
+
if (value instanceof RegExp) return value.toString();
|
|
32
|
+
if (ArrayBuffer.isView(value)) return describeBinary(value);
|
|
33
|
+
if (value instanceof ArrayBuffer) return `#[ArrayBuffer ${value.byteLength} bytes]`;
|
|
34
|
+
if (depth >= limits.depth) return DEPTH_LIMIT;
|
|
35
|
+
if ("object" != typeof value || null === value) return String(value);
|
|
36
|
+
if (seen.has(value)) return CIRCULAR;
|
|
37
|
+
seen.add(value);
|
|
38
|
+
try {
|
|
39
|
+
if (Array.isArray(value)) {
|
|
40
|
+
const kept = value.slice(0, limits.arrayLength).map((item)=>walk(item, limits, seen, depth + 1));
|
|
41
|
+
if (value.length > limits.arrayLength) kept.push(`…(${value.length - limits.arrayLength} more items)`);
|
|
42
|
+
return kept;
|
|
43
|
+
}
|
|
44
|
+
if (value instanceof Map) {
|
|
45
|
+
const entries = [];
|
|
46
|
+
for (const [key, item] of value.entries()){
|
|
47
|
+
if (entries.length >= limits.arrayLength) {
|
|
48
|
+
entries.push(`…(${value.size - limits.arrayLength} more entries)`);
|
|
49
|
+
break;
|
|
50
|
+
}
|
|
51
|
+
entries.push([
|
|
52
|
+
walk(key, limits, seen, depth + 1),
|
|
53
|
+
walk(item, limits, seen, depth + 1)
|
|
54
|
+
]);
|
|
55
|
+
}
|
|
56
|
+
return {
|
|
57
|
+
__type: "Map",
|
|
58
|
+
entries
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
if (value instanceof Set) {
|
|
62
|
+
const items = [];
|
|
63
|
+
for (const item of value.values()){
|
|
64
|
+
if (items.length >= limits.arrayLength) {
|
|
65
|
+
items.push(`…(${value.size - limits.arrayLength} more items)`);
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
items.push(walk(item, limits, seen, depth + 1));
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
__type: "Set",
|
|
72
|
+
items
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const result = {};
|
|
76
|
+
for (const key of Object.keys(value)){
|
|
77
|
+
let item;
|
|
78
|
+
try {
|
|
79
|
+
item = value[key];
|
|
80
|
+
} catch (ex) {
|
|
81
|
+
result[key] = `#[Throws: ${ex instanceof Error ? ex.message : String(ex)}]`;
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (void 0 !== item) result[key] = walk(item, limits, seen, depth + 1);
|
|
85
|
+
}
|
|
86
|
+
return result;
|
|
87
|
+
} finally{
|
|
88
|
+
seen.delete(value);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
const serialize = (value, limits)=>{
|
|
92
|
+
let json;
|
|
93
|
+
try {
|
|
94
|
+
const walked = walk(value, limits, new WeakSet(), 0);
|
|
95
|
+
json = JSON.stringify(walked) ?? JSON.stringify(UNDEFINED);
|
|
96
|
+
} catch (ex) {
|
|
97
|
+
const message = ex instanceof Error ? ex.message : String(ex);
|
|
98
|
+
json = JSON.stringify({
|
|
99
|
+
__serializationFailed: true,
|
|
100
|
+
message
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
let bytes = Buffer.byteLength(json, "utf8");
|
|
104
|
+
let truncated = false;
|
|
105
|
+
if (bytes > limits.entryBytes) {
|
|
106
|
+
json = JSON.stringify({
|
|
107
|
+
truncated: true,
|
|
108
|
+
originalBytes: bytes
|
|
109
|
+
});
|
|
110
|
+
bytes = Buffer.byteLength(json, "utf8");
|
|
111
|
+
truncated = true;
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
json,
|
|
115
|
+
bytes,
|
|
116
|
+
truncated
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
export { serialize };
|
|
120
|
+
|
|
121
|
+
//# sourceMappingURL=serialize.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/debugger/serialize.js","sources":["../../../src/features/debugger/serialize.ts"],"sourcesContent":["import type { IDebuggerLimits } from \"./limits.js\";\n\nexport interface ISerializeResult {\n json: string;\n bytes: number;\n /** True if the value was replaced by the per-entry cap marker. */\n truncated: boolean;\n}\n\n/**\n * Markers stand in for values that cannot be represented as JSON.\n *\n * They are prefixed with `#` so a reader can tell at a glance that a value was substituted by the\n * serializer rather than being what the code actually held - the previous `[undefined]` read as an\n * array literal in documents that are full of real arrays.\n */\nconst UNDEFINED = \"#[Undefined value]\";\nconst CIRCULAR = \"#[Circular]\";\nconst DEPTH_LIMIT = \"#[Depth limit]\";\n\nconst isPlainValue = (value: unknown): boolean => {\n const type = typeof value;\n return type === \"string\" || type === \"number\" || type === \"boolean\" || value === null;\n};\n\nconst describeBinary = (value: ArrayBufferView): string => {\n const name = value.constructor?.name || \"TypedArray\";\n return `#[${name} ${value.byteLength} bytes]`;\n};\n\n/**\n * Walks a value into a JSON-safe structure, applying depth, string and array caps as it goes.\n *\n * Caps are applied during the walk rather than by slicing the finished JSON string, so the output is\n * always parseable.\n *\n * A custom `toJSON` is never invoked - own properties are enumerated instead. That means an object\n * is reported as its internals rather than its intended representation, which is what you want when\n * debugging, and it removes a whole class of failure where a third-party `toJSON` throws.\n */\nconst walk = (\n value: unknown,\n limits: IDebuggerLimits,\n seen: WeakSet<object>,\n depth: number\n): unknown => {\n if (value === undefined) {\n /**\n * Only reached for array elements and for a top-level payload. Object properties holding\n * `undefined` are dropped before they get here, matching `JSON.stringify`.\n *\n * Inside an array the element cannot be dropped without shifting every later index, and\n * `JSON.stringify` would turn it into `null` - which is a meaningful value in its own right.\n */\n return UNDEFINED;\n }\n\n if (isPlainValue(value)) {\n if (typeof value === \"string\" && value.length > limits.stringLength) {\n const kept = value.slice(0, limits.stringLength);\n return `${kept}…(${value.length - limits.stringLength} more)`;\n }\n /**\n * Infinity and NaN serialize to `null`, which silently loses the distinction. Keep them\n * readable instead.\n */\n if (typeof value === \"number\" && !Number.isFinite(value)) {\n return String(value);\n }\n return value;\n }\n\n if (typeof value === \"bigint\") {\n return `${value.toString()}n`;\n }\n\n if (typeof value === \"symbol\") {\n return String(value);\n }\n\n if (typeof value === \"function\") {\n return `#[Function: ${value.name || \"anonymous\"}]`;\n }\n\n if (value instanceof Error) {\n return {\n name: value.name,\n message: value.message,\n stack: value.stack\n };\n }\n\n if (value instanceof Date) {\n return value.toISOString();\n }\n\n if (value instanceof RegExp) {\n return value.toString();\n }\n\n if (ArrayBuffer.isView(value)) {\n return describeBinary(value);\n }\n\n if (value instanceof ArrayBuffer) {\n return `#[ArrayBuffer ${value.byteLength} bytes]`;\n }\n\n if (depth >= limits.depth) {\n return DEPTH_LIMIT;\n }\n\n if (typeof value !== \"object\" || value === null) {\n return String(value);\n }\n\n if (seen.has(value)) {\n return CIRCULAR;\n }\n seen.add(value);\n\n try {\n if (Array.isArray(value)) {\n const kept = value.slice(0, limits.arrayLength).map(item => {\n return walk(item, limits, seen, depth + 1);\n });\n if (value.length > limits.arrayLength) {\n kept.push(`…(${value.length - limits.arrayLength} more items)`);\n }\n return kept;\n }\n\n if (value instanceof Map) {\n const entries: unknown[] = [];\n for (const [key, item] of value.entries()) {\n if (entries.length >= limits.arrayLength) {\n entries.push(`…(${value.size - limits.arrayLength} more entries)`);\n break;\n }\n entries.push([\n walk(key, limits, seen, depth + 1),\n walk(item, limits, seen, depth + 1)\n ]);\n }\n return { __type: \"Map\", entries };\n }\n\n if (value instanceof Set) {\n const items: unknown[] = [];\n for (const item of value.values()) {\n if (items.length >= limits.arrayLength) {\n items.push(`…(${value.size - limits.arrayLength} more items)`);\n break;\n }\n items.push(walk(item, limits, seen, depth + 1));\n }\n return { __type: \"Set\", items };\n }\n\n const result: Record<string, unknown> = {};\n for (const key of Object.keys(value)) {\n /**\n * A getter can throw. One bad property must not take down the whole entry, let alone the\n * request that is being observed.\n */\n let item: unknown;\n try {\n item = (value as Record<string, unknown>)[key];\n } catch (ex) {\n result[key] = `#[Throws: ${ex instanceof Error ? ex.message : String(ex)}]`;\n continue;\n }\n\n /**\n * Drop the key, as `JSON.stringify` does.\n *\n * Captured payloads are frequently copies of something that went over the wire, and the\n * wire never carried these keys - keeping them would describe a request that was never\n * made.\n */\n if (item === undefined) {\n continue;\n }\n\n result[key] = walk(item, limits, seen, depth + 1);\n }\n return result;\n } finally {\n /**\n * Only guard against cycles along the current branch. Without this, a value referenced twice\n * as siblings would report the second one as circular.\n */\n seen.delete(value);\n }\n};\n\n/**\n * Serializes a captured payload to JSON, never throwing and always producing valid JSON.\n */\nexport const serialize = (value: unknown, limits: IDebuggerLimits): ISerializeResult => {\n let json: string;\n\n try {\n const walked = walk(value, limits, new WeakSet(), 0);\n json = JSON.stringify(walked) ?? JSON.stringify(UNDEFINED);\n } catch (ex) {\n const message = ex instanceof Error ? ex.message : String(ex);\n json = JSON.stringify({ __serializationFailed: true, message });\n }\n\n let bytes = Buffer.byteLength(json, \"utf8\");\n let truncated = false;\n\n if (bytes > limits.entryBytes) {\n /**\n * Slicing the string would produce invalid JSON, so the payload is replaced wholesale.\n */\n json = JSON.stringify({ truncated: true, originalBytes: bytes });\n bytes = Buffer.byteLength(json, \"utf8\");\n truncated = true;\n }\n\n return { json, bytes, truncated };\n};\n"],"names":["UNDEFINED","CIRCULAR","DEPTH_LIMIT","isPlainValue","value","type","describeBinary","name","walk","limits","seen","depth","undefined","kept","Number","String","Error","Date","RegExp","ArrayBuffer","Array","item","Map","entries","key","Set","items","result","Object","ex","serialize","json","walked","WeakSet","JSON","message","bytes","Buffer","truncated"],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { Result } from "@webiny/feature/api";
|
|
2
|
+
import { HttpClient as HttpClientAbstraction } from "./abstractions.js";
|
|
3
|
+
declare class HttpClientImpl implements HttpClientAbstraction.Interface {
|
|
4
|
+
requestJson(request: HttpClientAbstraction.Request): Promise<Result<unknown, HttpClientAbstraction.Error>>;
|
|
5
|
+
}
|
|
6
|
+
export declare const HttpClient: import("@webiny/di").Implementation<typeof HttpClientImpl>;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { Result, createImplementation } from "@webiny/feature/api";
|
|
2
|
+
import { HttpClient } from "./abstractions.js";
|
|
3
|
+
import { HttpRequestError } from "./HttpRequestError.js";
|
|
4
|
+
import { HttpStatusError } from "./HttpStatusError.js";
|
|
5
|
+
const DEFAULT_TIMEOUT_MS = 10000;
|
|
6
|
+
function createRequestInit(request) {
|
|
7
|
+
const timeoutMs = request.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
8
|
+
const headers = new Headers(request.headers);
|
|
9
|
+
const init = {
|
|
10
|
+
method: request.method ?? "GET",
|
|
11
|
+
headers,
|
|
12
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
13
|
+
};
|
|
14
|
+
if (void 0 !== request.body) {
|
|
15
|
+
if (!headers.has("content-type")) headers.set("content-type", "application/json");
|
|
16
|
+
init.body = JSON.stringify(request.body);
|
|
17
|
+
}
|
|
18
|
+
return init;
|
|
19
|
+
}
|
|
20
|
+
function isTimeout(error) {
|
|
21
|
+
return error instanceof Error && "TimeoutError" === error.name;
|
|
22
|
+
}
|
|
23
|
+
function parseJson(text) {
|
|
24
|
+
if ("" === text) return Result.ok(void 0);
|
|
25
|
+
try {
|
|
26
|
+
const value = JSON.parse(text);
|
|
27
|
+
return Result.ok(value);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
return Result.fail(error);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
class HttpClientImpl {
|
|
33
|
+
async requestJson(request) {
|
|
34
|
+
const { url } = request;
|
|
35
|
+
const init = createRequestInit(request);
|
|
36
|
+
let response;
|
|
37
|
+
let text;
|
|
38
|
+
try {
|
|
39
|
+
response = await fetch(url, init);
|
|
40
|
+
text = await response.text();
|
|
41
|
+
} catch (error) {
|
|
42
|
+
const reason = isTimeout(error) ? "timeout" : "network";
|
|
43
|
+
return Result.fail(new HttpRequestError({
|
|
44
|
+
url,
|
|
45
|
+
reason
|
|
46
|
+
}, error));
|
|
47
|
+
}
|
|
48
|
+
const body = parseJson(text);
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
const statusError = new HttpStatusError({
|
|
51
|
+
url,
|
|
52
|
+
status: response.status,
|
|
53
|
+
statusText: response.statusText,
|
|
54
|
+
body: body.isOk() ? body.value : void 0
|
|
55
|
+
});
|
|
56
|
+
return Result.fail(statusError);
|
|
57
|
+
}
|
|
58
|
+
if (body.isFail()) return Result.fail(new HttpRequestError({
|
|
59
|
+
url,
|
|
60
|
+
reason: "invalidJson"
|
|
61
|
+
}, body.error));
|
|
62
|
+
return Result.ok(body.value);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const HttpClient_HttpClient = createImplementation({
|
|
66
|
+
abstraction: HttpClient,
|
|
67
|
+
implementation: HttpClientImpl,
|
|
68
|
+
dependencies: []
|
|
69
|
+
});
|
|
70
|
+
export { HttpClient_HttpClient as HttpClient };
|
|
71
|
+
|
|
72
|
+
//# sourceMappingURL=HttpClient.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/httpClient/HttpClient.js","sources":["../../../src/features/httpClient/HttpClient.ts"],"sourcesContent":["import { createImplementation } from \"@webiny/feature/api\";\nimport { Result } from \"@webiny/feature/api\";\nimport { HttpClient as HttpClientAbstraction } from \"./abstractions.js\";\nimport { HttpRequestError } from \"./HttpRequestError.js\";\nimport { HttpStatusError } from \"./HttpStatusError.js\";\n\nconst DEFAULT_TIMEOUT_MS = 10_000;\n\nfunction createRequestInit(request: HttpClientAbstraction.Request): RequestInit {\n const timeoutMs = request.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const headers = new Headers(request.headers);\n const init: RequestInit = {\n method: request.method ?? \"GET\",\n headers,\n signal: AbortSignal.timeout(timeoutMs)\n };\n\n if (request.body !== undefined) {\n if (!headers.has(\"content-type\")) {\n headers.set(\"content-type\", \"application/json\");\n }\n init.body = JSON.stringify(request.body);\n }\n\n return init;\n}\n\nfunction isTimeout(error: unknown): boolean {\n return error instanceof Error && error.name === \"TimeoutError\";\n}\n\n// An empty body parses to `undefined`, so a 204 is not an invalid response.\nfunction parseJson(text: string): Result<unknown, unknown> {\n if (text === \"\") {\n return Result.ok(undefined);\n }\n try {\n const value: unknown = JSON.parse(text);\n return Result.ok(value);\n } catch (error) {\n return Result.fail(error);\n }\n}\n\nclass HttpClientImpl implements HttpClientAbstraction.Interface {\n async requestJson(\n request: HttpClientAbstraction.Request\n ): Promise<Result<unknown, HttpClientAbstraction.Error>> {\n const { url } = request;\n const init = createRequestInit(request);\n\n let response: Response;\n let text: string;\n try {\n response = await fetch(url, init);\n text = await response.text();\n } catch (error) {\n const reason = isTimeout(error) ? \"timeout\" : \"network\";\n return Result.fail(new HttpRequestError({ url, reason }, error));\n }\n\n const body = parseJson(text);\n\n if (!response.ok) {\n const statusError = new HttpStatusError({\n url,\n status: response.status,\n statusText: response.statusText,\n body: body.isOk() ? body.value : undefined\n });\n return Result.fail(statusError);\n }\n\n if (body.isFail()) {\n return Result.fail(new HttpRequestError({ url, reason: \"invalidJson\" }, body.error));\n }\n\n return Result.ok(body.value);\n }\n}\n\nexport const HttpClient = createImplementation({\n abstraction: HttpClientAbstraction,\n implementation: HttpClientImpl,\n dependencies: []\n});\n"],"names":["DEFAULT_TIMEOUT_MS","createRequestInit","request","timeoutMs","headers","Headers","init","AbortSignal","undefined","JSON","isTimeout","error","Error","parseJson","text","Result","value","HttpClientImpl","url","response","fetch","reason","HttpRequestError","body","statusError","HttpStatusError","HttpClient","createImplementation","HttpClientAbstraction"],"mappings":";;;;AAMA,MAAMA,qBAAqB;AAE3B,SAASC,kBAAkBC,OAAsC;IAC7D,MAAMC,YAAYD,QAAQ,SAAS,IAAIF;IACvC,MAAMI,UAAU,IAAIC,QAAQH,QAAQ,OAAO;IAC3C,MAAMI,OAAoB;QACtB,QAAQJ,QAAQ,MAAM,IAAI;QAC1BE;QACA,QAAQG,YAAY,OAAO,CAACJ;IAChC;IAEA,IAAID,AAAiBM,WAAjBN,QAAQ,IAAI,EAAgB;QAC5B,IAAI,CAACE,QAAQ,GAAG,CAAC,iBACbA,QAAQ,GAAG,CAAC,gBAAgB;QAEhCE,KAAK,IAAI,GAAGG,KAAK,SAAS,CAACP,QAAQ,IAAI;IAC3C;IAEA,OAAOI;AACX;AAEA,SAASI,UAAUC,KAAc;IAC7B,OAAOA,iBAAiBC,SAASD,AAAe,mBAAfA,MAAM,IAAI;AAC/C;AAGA,SAASE,UAAUC,IAAY;IAC3B,IAAIA,AAAS,OAATA,MACA,OAAOC,OAAO,EAAE,CAACP;IAErB,IAAI;QACA,MAAMQ,QAAiBP,KAAK,KAAK,CAACK;QAClC,OAAOC,OAAO,EAAE,CAACC;IACrB,EAAE,OAAOL,OAAO;QACZ,OAAOI,OAAO,IAAI,CAACJ;IACvB;AACJ;AAEA,MAAMM;IACF,MAAM,YACFf,OAAsC,EACe;QACrD,MAAM,EAAEgB,GAAG,EAAE,GAAGhB;QAChB,MAAMI,OAAOL,kBAAkBC;QAE/B,IAAIiB;QACJ,IAAIL;QACJ,IAAI;YACAK,WAAW,MAAMC,MAAMF,KAAKZ;YAC5BQ,OAAO,MAAMK,SAAS,IAAI;QAC9B,EAAE,OAAOR,OAAO;YACZ,MAAMU,SAASX,UAAUC,SAAS,YAAY;YAC9C,OAAOI,OAAO,IAAI,CAAC,IAAIO,iBAAiB;gBAAEJ;gBAAKG;YAAO,GAAGV;QAC7D;QAEA,MAAMY,OAAOV,UAAUC;QAEvB,IAAI,CAACK,SAAS,EAAE,EAAE;YACd,MAAMK,cAAc,IAAIC,gBAAgB;gBACpCP;gBACA,QAAQC,SAAS,MAAM;gBACvB,YAAYA,SAAS,UAAU;gBAC/B,MAAMI,KAAK,IAAI,KAAKA,KAAK,KAAK,GAAGf;YACrC;YACA,OAAOO,OAAO,IAAI,CAACS;QACvB;QAEA,IAAID,KAAK,MAAM,IACX,OAAOR,OAAO,IAAI,CAAC,IAAIO,iBAAiB;YAAEJ;YAAK,QAAQ;QAAc,GAAGK,KAAK,KAAK;QAGtF,OAAOR,OAAO,EAAE,CAACQ,KAAK,KAAK;IAC/B;AACJ;AAEO,MAAMG,wBAAaC,qBAAqB;IAC3C,aAAaC;IACb,gBAAgBX;IAChB,cAAc,EAAE;AACpB"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { BaseError } from "@webiny/feature/api";
|
|
2
|
+
export interface HttpRequestErrorData {
|
|
3
|
+
url: string;
|
|
4
|
+
reason: "network" | "timeout" | "invalidJson";
|
|
5
|
+
}
|
|
6
|
+
/** The request didn't produce a usable response: it failed, timed out, or the body wasn't JSON. */
|
|
7
|
+
export declare class HttpRequestError extends BaseError<HttpRequestErrorData> {
|
|
8
|
+
readonly code: "HttpClient/Request";
|
|
9
|
+
constructor(data: HttpRequestErrorData, cause: unknown);
|
|
10
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { BaseError } from "@webiny/feature/api";
|
|
2
|
+
class HttpRequestError extends BaseError {
|
|
3
|
+
constructor(data, cause){
|
|
4
|
+
super({
|
|
5
|
+
message: `Request to "${data.url}" failed (${data.reason}).`,
|
|
6
|
+
data
|
|
7
|
+
}), this.code = "HttpClient/Request";
|
|
8
|
+
this.cause = cause;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export { HttpRequestError };
|
|
12
|
+
|
|
13
|
+
//# sourceMappingURL=HttpRequestError.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/httpClient/HttpRequestError.js","sources":["../../../src/features/httpClient/HttpRequestError.ts"],"sourcesContent":["import { BaseError } from \"@webiny/feature/api\";\n\nexport interface HttpRequestErrorData {\n url: string;\n reason: \"network\" | \"timeout\" | \"invalidJson\";\n}\n\n/** The request didn't produce a usable response: it failed, timed out, or the body wasn't JSON. */\nexport class HttpRequestError extends BaseError<HttpRequestErrorData> {\n override readonly code = \"HttpClient/Request\" as const;\n\n constructor(data: HttpRequestErrorData, cause: unknown) {\n super({\n message: `Request to \"${data.url}\" failed (${data.reason}).`,\n data\n });\n this.cause = cause;\n }\n}\n"],"names":["HttpRequestError","BaseError","data","cause"],"mappings":";AAQO,MAAMA,yBAAyBC;IAGlC,YAAYC,IAA0B,EAAEC,KAAc,CAAE;QACpD,KAAK,CAAC;YACF,SAAS,CAAC,YAAY,EAAED,KAAK,GAAG,CAAC,UAAU,EAAEA,KAAK,MAAM,CAAC,EAAE,CAAC;YAC5DA;QACJ,SANc,IAAI,GAAG;QAOrB,IAAI,CAAC,KAAK,GAAGC;IACjB;AACJ"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BaseError } from "@webiny/feature/api";
|
|
2
|
+
export interface HttpStatusErrorData {
|
|
3
|
+
url: string;
|
|
4
|
+
status: number;
|
|
5
|
+
statusText: string;
|
|
6
|
+
body: unknown;
|
|
7
|
+
}
|
|
8
|
+
/** The server answered with a non-2xx status. */
|
|
9
|
+
export declare class HttpStatusError extends BaseError<HttpStatusErrorData> {
|
|
10
|
+
readonly code: "HttpClient/Status";
|
|
11
|
+
constructor(data: HttpStatusErrorData);
|
|
12
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { BaseError } from "@webiny/feature/api";
|
|
2
|
+
class HttpStatusError extends BaseError {
|
|
3
|
+
constructor(data){
|
|
4
|
+
super({
|
|
5
|
+
message: `Request to "${data.url}" failed with status ${data.status}.`,
|
|
6
|
+
data
|
|
7
|
+
}), this.code = "HttpClient/Status";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export { HttpStatusError };
|
|
11
|
+
|
|
12
|
+
//# sourceMappingURL=HttpStatusError.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/httpClient/HttpStatusError.js","sources":["../../../src/features/httpClient/HttpStatusError.ts"],"sourcesContent":["import { BaseError } from \"@webiny/feature/api\";\n\nexport interface HttpStatusErrorData {\n url: string;\n status: number;\n statusText: string;\n body: unknown; // the parsed JSON body, or `undefined` when there was none\n}\n\n/** The server answered with a non-2xx status. */\nexport class HttpStatusError extends BaseError<HttpStatusErrorData> {\n override readonly code = \"HttpClient/Status\" as const;\n\n constructor(data: HttpStatusErrorData) {\n super({\n message: `Request to \"${data.url}\" failed with status ${data.status}.`,\n data\n });\n }\n}\n"],"names":["HttpStatusError","BaseError","data"],"mappings":";AAUO,MAAMA,wBAAwBC;IAGjC,YAAYC,IAAyB,CAAE;QACnC,KAAK,CAAC;YACF,SAAS,CAAC,YAAY,EAAEA,KAAK,GAAG,CAAC,qBAAqB,EAAEA,KAAK,MAAM,CAAC,CAAC,CAAC;YACtEA;QACJ,SANc,IAAI,GAAG;IAOzB;AACJ"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Result } from "@webiny/feature/api";
|
|
2
|
+
import type { HttpRequestError } from "./HttpRequestError.js";
|
|
3
|
+
import type { HttpStatusError } from "./HttpStatusError.js";
|
|
4
|
+
export type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
|
5
|
+
export interface IHttpClientRequest {
|
|
6
|
+
url: string;
|
|
7
|
+
method?: HttpMethod;
|
|
8
|
+
headers?: Record<string, string>;
|
|
9
|
+
body?: unknown;
|
|
10
|
+
timeoutMs?: number;
|
|
11
|
+
}
|
|
12
|
+
export interface IHttpClientErrors {
|
|
13
|
+
request: HttpRequestError;
|
|
14
|
+
status: HttpStatusError;
|
|
15
|
+
}
|
|
16
|
+
type HttpClientError = IHttpClientErrors[keyof IHttpClientErrors];
|
|
17
|
+
export interface IHttpClient {
|
|
18
|
+
/**
|
|
19
|
+
* Sends a request and parses the response body as JSON. The body is `unknown` on purpose:
|
|
20
|
+
* validate it before use.
|
|
21
|
+
*/
|
|
22
|
+
requestJson(request: IHttpClientRequest): Promise<Result<unknown, HttpClientError>>;
|
|
23
|
+
}
|
|
24
|
+
/** Outbound HTTP requests with a timeout, a status check and JSON parsing. */
|
|
25
|
+
export declare const HttpClient: import("@webiny/di").Abstraction<IHttpClient>;
|
|
26
|
+
export declare namespace HttpClient {
|
|
27
|
+
type Interface = IHttpClient;
|
|
28
|
+
type Request = IHttpClientRequest;
|
|
29
|
+
type Method = HttpMethod;
|
|
30
|
+
type Error = HttpClientError;
|
|
31
|
+
}
|
|
32
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/httpClient/abstractions.js","sources":["../../../src/features/httpClient/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/api\";\nimport type { Result } from \"@webiny/feature/api\";\nimport type { HttpRequestError } from \"./HttpRequestError.js\";\nimport type { HttpStatusError } from \"./HttpStatusError.js\";\n\nexport type HttpMethod = \"GET\" | \"POST\" | \"PUT\" | \"PATCH\" | \"DELETE\";\n\nexport interface IHttpClientRequest {\n url: string;\n method?: HttpMethod; // defaults to \"GET\"\n headers?: Record<string, string>;\n body?: unknown; // sent as JSON\n timeoutMs?: number; // defaults to 10 seconds\n}\n\nexport interface IHttpClientErrors {\n request: HttpRequestError;\n status: HttpStatusError;\n}\n\ntype HttpClientError = IHttpClientErrors[keyof IHttpClientErrors];\n\nexport interface IHttpClient {\n /**\n * Sends a request and parses the response body as JSON. The body is `unknown` on purpose:\n * validate it before use.\n */\n requestJson(request: IHttpClientRequest): Promise<Result<unknown, HttpClientError>>;\n}\n\n/** Outbound HTTP requests with a timeout, a status check and JSON parsing. */\nexport const HttpClient = createAbstraction<IHttpClient>(\"HttpClient\");\n\nexport namespace HttpClient {\n export type Interface = IHttpClient;\n export type Request = IHttpClientRequest;\n export type Method = HttpMethod;\n export type Error = HttpClientError;\n}\n"],"names":["HttpClient","createAbstraction"],"mappings":";AA+BO,MAAMA,aAAaC,kBAA+B"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createFeature } from "@webiny/feature/api";
|
|
2
|
+
import { HttpClient } from "./HttpClient.js";
|
|
3
|
+
const HttpClientFeature = createFeature({
|
|
4
|
+
name: "HttpClientFeature",
|
|
5
|
+
register (container) {
|
|
6
|
+
container.register(HttpClient).inSingletonScope();
|
|
7
|
+
}
|
|
8
|
+
});
|
|
9
|
+
export { HttpClientFeature };
|
|
10
|
+
|
|
11
|
+
//# sourceMappingURL=feature.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"features/httpClient/feature.js","sources":["../../../src/features/httpClient/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport { HttpClient } from \"./HttpClient.js\";\n\nexport const HttpClientFeature = createFeature({\n name: \"HttpClientFeature\",\n register(container) {\n container.register(HttpClient).inSingletonScope();\n }\n});\n"],"names":["HttpClientFeature","createFeature","container","HttpClient"],"mappings":";;AAGO,MAAMA,oBAAoBC,cAAc;IAC3C,MAAM;IACN,UAASC,SAAS;QACdA,UAAU,QAAQ,CAACC,YAAY,gBAAgB;IACnD;AACJ"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"features/users/shared/loaders.js","sources":["../../../../src/features/users/shared/loaders.ts"],"sourcesContent":["import DataLoader from \"dataloader\";\nimport WebinyError from \"@webiny/error\";\nimport flatten from \"lodash/flatten.js\";\nimport type { AdminUsersStorageOperations } from \"~/types/users.js\";\nimport type { AdminUser } from \"./types.js\";\n\ninterface Config {\n getTenant(): string;\n storageOperations: AdminUsersStorageOperations;\n}\n\ninterface GetUserLoaderKey {\n id: string;\n tenant: string;\n}\n\nexport const createUserLoaders = ({ storageOperations }: Config) => {\n const loaders = new Map<string, DataLoader<any, any>>();\n\n async function getUserLoader(ids: readonly GetUserLoaderKey[]) {\n if (ids.length === 0) {\n return [];\n }\n\n // Group by tenant\n const byTenant = ids.reduce(\n (acc, item) => {\n acc[item.tenant] = [...(acc[item.tenant] || []), item.id];\n return acc;\n },\n {} as Record<string, string[]>\n );\n\n try {\n const results = await Promise.all(\n Object.keys(byTenant).map(tenant => {\n return storageOperations.listUsers({\n where: {\n tenant,\n id_in: byTenant[tenant]\n }\n });\n })\n ).then(res => flatten(res));\n\n return ids.map(({ tenant, id }) => {\n const user = results.find(item => item.id === id && item.tenant === tenant);\n /**\n * Casting because DataLoader expect error. But we do not.\n */\n return user || (null as unknown as Error);\n });\n } catch (err) {\n throw WebinyError.from(err, {\n message: \"Could not execute batch read in getUser.\",\n code: \"BATCH_READ_ERROR\",\n data: { ids }\n });\n }\n }\n\n return {\n get getUser() {\n let userLoader = loaders.get(\"getUser\");\n if (userLoader) {\n return userLoader;\n }\n userLoader = new DataLoader<GetUserLoaderKey, AdminUser, string>(getUserLoader, {\n cacheKeyFn(key) {\n return `${key.tenant}:${key.id}`;\n }\n });\n loaders.set(\"getUser\", userLoader);\n return userLoader;\n },\n\n clearLoadersCache(ids: GetUserLoaderKey[]) {\n for (const id of ids) {\n this.getUser.clear(id);\n }\n },\n\n async updateDataLoaderUserCache(id: GetUserLoaderKey, data: Partial<AdminUser>) {\n const user = await this.getUser.load(id);\n this.getUser.clear(id).prime(id, Object.assign({}, user, data));\n }\n };\n};\n"],"names":["createUserLoaders","storageOperations","loaders","Map","getUserLoader","ids","byTenant","acc","item","results","Promise","Object","tenant","res","flatten","id","user","err","WebinyError","userLoader","DataLoader","key","data"],"mappings":";;;AAgBO,MAAMA,oBAAoB,CAAC,EAAEC,iBAAiB,EAAU;IAC3D,MAAMC,UAAU,IAAIC;IAEpB,eAAeC,cAAcC,GAAgC;QACzD,IAAIA,AAAe,MAAfA,IAAI,MAAM,EACV,OAAO,EAAE;QAIb,MAAMC,WAAWD,IAAI,MAAM,CACvB,CAACE,KAAKC;YACFD,GAAG,CAACC,KAAK,MAAM,CAAC,GAAG;mBAAKD,GAAG,CAACC,KAAK,MAAM,CAAC,IAAI,EAAE;gBAAGA,KAAK,EAAE;aAAC;YACzD,OAAOD;QACX,GACA,CAAC;QAGL,IAAI;YACA,MAAME,UAAU,MAAMC,QAAQ,GAAG,CAC7BC,OAAO,IAAI,CAACL,UAAU,GAAG,CAACM,CAAAA,SACfX,kBAAkB,SAAS,CAAC;oBAC/B,OAAO;wBACHW;wBACA,OAAON,QAAQ,CAACM,OAAO;oBAC3B;gBACJ,KAEN,IAAI,CAACC,CAAAA,MAAOC,QAAQD;YAEtB,OAAOR,IAAI,GAAG,CAAC,CAAC,EAAEO,MAAM,EAAEG,EAAE,EAAE;gBAC1B,MAAMC,OAAOP,QAAQ,IAAI,CAACD,CAAAA,OAAQA,KAAK,EAAE,KAAKO,MAAMP,KAAK,MAAM,KAAKI;gBAIpE,OAAOI,QAAS;YACpB;QACJ,EAAE,OAAOC,KAAK;YACV,MAAMC,MAAAA,IAAgB,CAACD,KAAK;gBACxB,SAAS;gBACT,MAAM;gBACN,MAAM;oBAAEZ;gBAAI;YAChB;QACJ;IACJ;IAEA,OAAO;QACH,IAAI
|
|
1
|
+
{"version":3,"file":"features/users/shared/loaders.js","sources":["../../../../src/features/users/shared/loaders.ts"],"sourcesContent":["import DataLoader from \"dataloader\";\nimport WebinyError from \"@webiny/error\";\nimport flatten from \"lodash/flatten.js\";\nimport type { AdminUsersStorageOperations } from \"~/types/users.js\";\nimport type { AdminUser } from \"./types.js\";\n\ninterface Config {\n getTenant(): string;\n storageOperations: AdminUsersStorageOperations;\n}\n\ninterface GetUserLoaderKey {\n id: string;\n tenant: string;\n}\n\nexport const createUserLoaders = ({ storageOperations }: Config) => {\n const loaders = new Map<string, DataLoader<any, any>>();\n\n async function getUserLoader(ids: readonly GetUserLoaderKey[]) {\n if (ids.length === 0) {\n return [];\n }\n\n // Group by tenant\n const byTenant = ids.reduce(\n (acc, item) => {\n acc[item.tenant] = [...(acc[item.tenant] || []), item.id];\n return acc;\n },\n {} as Record<string, string[]>\n );\n\n try {\n const results = await Promise.all(\n Object.keys(byTenant).map(tenant => {\n return storageOperations.listUsers({\n where: {\n tenant,\n id_in: byTenant[tenant]\n }\n });\n })\n ).then(res => flatten(res));\n\n return ids.map(({ tenant, id }) => {\n const user = results.find(item => item.id === id && item.tenant === tenant);\n /**\n * Casting because DataLoader expect error. But we do not.\n */\n return user || (null as unknown as Error);\n });\n } catch (err) {\n throw WebinyError.from(err, {\n message: \"Could not execute batch read in getUser.\",\n code: \"BATCH_READ_ERROR\",\n data: { ids }\n });\n }\n }\n\n return {\n get getUser() {\n let userLoader = loaders.get(\"getUser\");\n if (userLoader) {\n return userLoader;\n }\n userLoader = new DataLoader<GetUserLoaderKey, AdminUser, string>(getUserLoader, {\n cacheKeyFn(key) {\n return `${key.tenant}:${key.id}`;\n }\n });\n loaders.set(\"getUser\", userLoader);\n return userLoader;\n },\n\n clearLoadersCache(ids: GetUserLoaderKey[]) {\n for (const id of ids) {\n this.getUser.clear(id);\n }\n },\n\n async updateDataLoaderUserCache(id: GetUserLoaderKey, data: Partial<AdminUser>) {\n const user = await this.getUser.load(id);\n this.getUser.clear(id).prime(id, Object.assign({}, user, data));\n }\n };\n};\n"],"names":["createUserLoaders","storageOperations","loaders","Map","getUserLoader","ids","byTenant","acc","item","results","Promise","Object","tenant","res","flatten","id","user","err","WebinyError","userLoader","DataLoader","key","data"],"mappings":";;;AAgBO,MAAMA,oBAAoB,CAAC,EAAEC,iBAAiB,EAAU;IAC3D,MAAMC,UAAU,IAAIC;IAEpB,eAAeC,cAAcC,GAAgC;QACzD,IAAIA,AAAe,MAAfA,IAAI,MAAM,EACV,OAAO,EAAE;QAIb,MAAMC,WAAWD,IAAI,MAAM,CACvB,CAACE,KAAKC;YACFD,GAAG,CAACC,KAAK,MAAM,CAAC,GAAG;mBAAKD,GAAG,CAACC,KAAK,MAAM,CAAC,IAAI,EAAE;gBAAGA,KAAK,EAAE;aAAC;YACzD,OAAOD;QACX,GACA,CAAC;QAGL,IAAI;YACA,MAAME,UAAU,MAAMC,QAAQ,GAAG,CAC7BC,OAAO,IAAI,CAACL,UAAU,GAAG,CAACM,CAAAA,SACfX,kBAAkB,SAAS,CAAC;oBAC/B,OAAO;wBACHW;wBACA,OAAON,QAAQ,CAACM,OAAO;oBAC3B;gBACJ,KAEN,IAAI,CAACC,CAAAA,MAAOC,QAAQD;YAEtB,OAAOR,IAAI,GAAG,CAAC,CAAC,EAAEO,MAAM,EAAEG,EAAE,EAAE;gBAC1B,MAAMC,OAAOP,QAAQ,IAAI,CAACD,CAAAA,OAAQA,KAAK,EAAE,KAAKO,MAAMP,KAAK,MAAM,KAAKI;gBAIpE,OAAOI,QAAS;YACpB;QACJ,EAAE,OAAOC,KAAK;YACV,MAAMC,MAAAA,IAAgB,CAACD,KAAK;gBACxB,SAAS;gBACT,MAAM;gBACN,MAAM;oBAAEZ;gBAAI;YAChB;QACJ;IACJ;IAEA,OAAO;QACH,IAAI;YACA,IAAIc,aAAajB,QAAQ,GAAG,CAAC;YAC7B,IAAIiB,YACA,OAAOA;YAEXA,aAAa,IAAIC,WAAgDhB,eAAe;gBAC5E,YAAWiB,GAAG;oBACV,OAAO,GAAGA,IAAI,MAAM,CAAC,CAAC,EAAEA,IAAI,EAAE,EAAE;gBACpC;YACJ;YACAnB,QAAQ,GAAG,CAAC,WAAWiB;YACvB,OAAOA;QACX;QAEA,mBAAkBd,GAAuB;YACrC,KAAK,MAAMU,MAAMV,IACb,IAAI,CAAC,OAAO,CAAC,KAAK,CAACU;QAE3B;QAEA,MAAM,2BAA0BA,EAAoB,EAAEO,IAAwB;YAC1E,MAAMN,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC,IAAI,CAACD;YACrC,IAAI,CAAC,OAAO,CAAC,KAAK,CAACA,IAAI,KAAK,CAACA,IAAIJ,OAAO,MAAM,CAAC,CAAC,GAAGK,MAAMM;QAC7D;IACJ;AACJ"}
|
package/graphql/wcp/graphql.js
CHANGED
|
@@ -18,6 +18,8 @@ const createWcpGraphQL = ()=>new GraphQLSchemaPlugin({
|
|
|
18
18
|
recordLocking: WcpProjectPackageFeaturesFeature
|
|
19
19
|
fileManager: WcpProjectPackageFeaturesFeature
|
|
20
20
|
aiPowerups: WcpProjectPackageFeaturesFeature
|
|
21
|
+
abTesting: WcpProjectPackageFeaturesFeature
|
|
22
|
+
collaboration: WcpProjectPackageFeaturesFeature
|
|
21
23
|
}
|
|
22
24
|
|
|
23
25
|
type WcpProjectPackage {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graphql/wcp/graphql.js","sources":["../../../src/graphql/wcp/graphql.ts"],"sourcesContent":["import { GraphQLSchemaPlugin } from \"@webiny/handler-graphql/plugins/index.js\";\nimport { ErrorResponse, Response } from \"@webiny/handler-graphql/responses.js\";\nimport { WcpContext } from \"~/features/wcp/WcpContext/index.js\";\n\nconst emptyResolver = () => ({});\n\nexport const createWcpGraphQL = () => {\n return new GraphQLSchemaPlugin({\n typeDefs: /* GraphQL */ `\n type WcpProjectPackageFeaturesFeature {\n enabled: Boolean\n options: JSON\n }\n\n type WcpProjectPackageFeatures {\n seats: WcpProjectPackageFeaturesFeature\n multiTenancy: WcpProjectPackageFeaturesFeature\n advancedPublishingWorkflow: WcpProjectPackageFeaturesFeature\n advancedAccessControlLayer: WcpProjectPackageFeaturesFeature\n auditLogs: WcpProjectPackageFeaturesFeature\n recordLocking: WcpProjectPackageFeaturesFeature\n fileManager: WcpProjectPackageFeaturesFeature\n aiPowerups: WcpProjectPackageFeaturesFeature\n }\n\n type WcpProjectPackage {\n features: WcpProjectPackageFeatures\n }\n\n type WcpProject {\n orgId: ID\n projectId: ID\n package: WcpProjectPackage\n }\n\n type WcpError {\n code: String\n message: String\n data: JSON\n stack: String\n }\n\n type WcpProjectResponse {\n data: WcpProject\n error: WcpError\n }\n\n type WcpQuery {\n getProject: WcpProjectResponse\n }\n\n type WcpMutation {\n updateProject: WcpProjectResponse\n }\n\n extend type Query {\n wcp: WcpQuery\n }\n\n extend type Mutation {\n wcp: WcpMutation\n }\n `,\n resolvers: {\n Query: {\n wcp: emptyResolver\n },\n WcpQuery: {\n getProject: async (_, __, context) => {\n try {\n const wcpContext = context.container.resolve(WcpContext);\n const project = wcpContext.getProjectWithFeatureFlags();\n\n if (!project) {\n throw Error(`Could not get project!`);\n }\n\n return new Response(project);\n } catch (e) {\n return new ErrorResponse({\n code: \"COULD_NOT_GET_PROJECT\",\n message: e.message,\n data: null,\n stack: e.stack\n });\n }\n }\n }\n }\n });\n};\n"],"names":["emptyResolver","createWcpGraphQL","GraphQLSchemaPlugin","_","__","context","wcpContext","WcpContext","project","Error","Response","e","ErrorResponse"],"mappings":";;;AAIA,MAAMA,gBAAgB,IAAO,EAAC;AAEvB,MAAMC,mBAAmB,IACrB,IAAIC,oBAAoB;QAC3B,UAAwB,CAAC
|
|
1
|
+
{"version":3,"file":"graphql/wcp/graphql.js","sources":["../../../src/graphql/wcp/graphql.ts"],"sourcesContent":["import { GraphQLSchemaPlugin } from \"@webiny/handler-graphql/plugins/index.js\";\nimport { ErrorResponse, Response } from \"@webiny/handler-graphql/responses.js\";\nimport { WcpContext } from \"~/features/wcp/WcpContext/index.js\";\n\nconst emptyResolver = () => ({});\n\nexport const createWcpGraphQL = () => {\n return new GraphQLSchemaPlugin({\n typeDefs: /* GraphQL */ `\n type WcpProjectPackageFeaturesFeature {\n enabled: Boolean\n options: JSON\n }\n\n type WcpProjectPackageFeatures {\n seats: WcpProjectPackageFeaturesFeature\n multiTenancy: WcpProjectPackageFeaturesFeature\n advancedPublishingWorkflow: WcpProjectPackageFeaturesFeature\n advancedAccessControlLayer: WcpProjectPackageFeaturesFeature\n auditLogs: WcpProjectPackageFeaturesFeature\n recordLocking: WcpProjectPackageFeaturesFeature\n fileManager: WcpProjectPackageFeaturesFeature\n aiPowerups: WcpProjectPackageFeaturesFeature\n abTesting: WcpProjectPackageFeaturesFeature\n collaboration: WcpProjectPackageFeaturesFeature\n }\n\n type WcpProjectPackage {\n features: WcpProjectPackageFeatures\n }\n\n type WcpProject {\n orgId: ID\n projectId: ID\n package: WcpProjectPackage\n }\n\n type WcpError {\n code: String\n message: String\n data: JSON\n stack: String\n }\n\n type WcpProjectResponse {\n data: WcpProject\n error: WcpError\n }\n\n type WcpQuery {\n getProject: WcpProjectResponse\n }\n\n type WcpMutation {\n updateProject: WcpProjectResponse\n }\n\n extend type Query {\n wcp: WcpQuery\n }\n\n extend type Mutation {\n wcp: WcpMutation\n }\n `,\n resolvers: {\n Query: {\n wcp: emptyResolver\n },\n WcpQuery: {\n getProject: async (_, __, context) => {\n try {\n const wcpContext = context.container.resolve(WcpContext);\n const project = wcpContext.getProjectWithFeatureFlags();\n\n if (!project) {\n throw Error(`Could not get project!`);\n }\n\n return new Response(project);\n } catch (e) {\n return new ErrorResponse({\n code: \"COULD_NOT_GET_PROJECT\",\n message: e.message,\n data: null,\n stack: e.stack\n });\n }\n }\n }\n }\n });\n};\n"],"names":["emptyResolver","createWcpGraphQL","GraphQLSchemaPlugin","_","__","context","wcpContext","WcpContext","project","Error","Response","e","ErrorResponse"],"mappings":";;;AAIA,MAAMA,gBAAgB,IAAO,EAAC;AAEvB,MAAMC,mBAAmB,IACrB,IAAIC,oBAAoB;QAC3B,UAAwB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QAwDzB,CAAC;QACD,WAAW;YACP,OAAO;gBACH,KAAKF;YACT;YACA,UAAU;gBACN,YAAY,OAAOG,GAAGC,IAAIC;oBACtB,IAAI;wBACA,MAAMC,aAAaD,QAAQ,SAAS,CAAC,OAAO,CAACE;wBAC7C,MAAMC,UAAUF,WAAW,0BAA0B;wBAErD,IAAI,CAACE,SACD,MAAMC,MAAM;wBAGhB,OAAO,IAAIC,SAASF;oBACxB,EAAE,OAAOG,GAAG;wBACR,OAAO,IAAIC,cAAc;4BACrB,MAAM;4BACN,SAASD,EAAE,OAAO;4BAClB,MAAM;4BACN,OAAOA,EAAE,KAAK;wBAClB;oBACJ;gBACJ;YACJ;QACJ;IACJ"}
|
package/idp/JwksCache.d.ts
CHANGED
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
import type { Jwk } from "../features/security/utils/verifyJwtUsingJwk.js";
|
|
2
|
+
import { HttpClient } from "../features/httpClient/index.js";
|
|
2
3
|
import { JwkCache } from "./abstractions.js";
|
|
3
|
-
|
|
4
|
+
declare class JwksCacheImpl implements JwkCache.Interface {
|
|
5
|
+
private readonly httpClient;
|
|
4
6
|
private cache;
|
|
7
|
+
constructor(httpClient: HttpClient.Interface);
|
|
5
8
|
getKeys(issuer: string): Promise<Jwk[]>;
|
|
6
|
-
|
|
9
|
+
private fetchJson;
|
|
7
10
|
}
|
|
11
|
+
export declare const JwksCache: import("@webiny/di").Implementation<typeof JwksCacheImpl>;
|
|
12
|
+
export {};
|