@webiny/api-core 6.6.0-alpha.2 → 6.6.0-alpha.4

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.
@@ -1,2 +1,3 @@
1
1
  export { TaskService } from "../../features/task/TaskService/index.js";
2
2
  export { TaskDefinition } from "../../features/task/TaskDefinition/index.js";
3
+ export { TaskHandler } from "../../features/task/TaskDefinition/index.js";
@@ -1,2 +1,2 @@
1
1
  export { TaskService } from "../../features/task/TaskService/index.js";
2
- export { TaskDefinition } from "../../features/task/TaskDefinition/index.js";
2
+ export { TaskDefinition, TaskHandler } from "../../features/task/TaskDefinition/index.js";
package/exports/api.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { AiSdk, AiSdkFactory, AiSdkTool, AiConnectionFactory, Ai } from "../features/ai/index.js";
1
+ export { AiSdk, AiSdkFactory, AiSdkToolDefinition, AiSdkToolHandler, AiConnectionFactory, Ai } from "../features/ai/index.js";
2
2
  export type { IAiConnection, IAiConnectionInline } from "../features/ai/index.js";
3
3
  export { Logger } from "../features/logger/index.js";
4
4
  export { Encryption } from "../features/encryption/index.js";
package/exports/api.js CHANGED
@@ -1,4 +1,4 @@
1
- export { Ai, AiConnectionFactory, AiSdk, AiSdkFactory, AiSdkTool } from "../features/ai/index.js";
1
+ export { Ai, AiConnectionFactory, AiSdk, AiSdkFactory, AiSdkToolDefinition, AiSdkToolHandler } from "../features/ai/index.js";
2
2
  export { Logger } from "../features/logger/index.js";
3
3
  export { Encryption } from "../features/encryption/index.js";
4
4
  export { Hasher } from "../features/hashing/index.js";
@@ -0,0 +1,11 @@
1
+ import type { Container, Constructor } from "@webiny/di";
2
+ import { AiSdkToolHandler, AiSdkToolHandlerResolver as Abstraction } from "./abstractions.js";
3
+ declare class ContainerAiSdkToolHandlerResolver implements Abstraction.Interface {
4
+ private readonly container;
5
+ constructor(container: Container);
6
+ resolve<TInput>(handler: Constructor<AiSdkToolHandler.Interface<TInput>>): AiSdkToolHandler.Interface<TInput>;
7
+ }
8
+ export declare const AiSdkToolHandlerResolver: typeof ContainerAiSdkToolHandlerResolver & {
9
+ __abstraction: import("@webiny/di").Abstraction<import("./abstractions.js").IAiSdkToolHandlerResolver>;
10
+ };
11
+ export {};
@@ -0,0 +1,19 @@
1
+ import { RequestContainer } from "@webiny/event-handler-core/features/events/RequestContainer.js";
2
+ import { AiSdkToolHandlerResolver } from "./abstractions.js";
3
+ class ContainerAiSdkToolHandlerResolver {
4
+ constructor(container){
5
+ this.container = container;
6
+ }
7
+ resolve(handler) {
8
+ return this.container.resolveImplementation(handler);
9
+ }
10
+ }
11
+ const AiSdkToolHandlerResolver_AiSdkToolHandlerResolver = AiSdkToolHandlerResolver.createImplementation({
12
+ implementation: ContainerAiSdkToolHandlerResolver,
13
+ dependencies: [
14
+ RequestContainer
15
+ ]
16
+ });
17
+ export { AiSdkToolHandlerResolver_AiSdkToolHandlerResolver as AiSdkToolHandlerResolver };
18
+
19
+ //# sourceMappingURL=AiSdkToolHandlerResolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"features/ai/AiSdkToolHandlerResolver.js","sources":["../../../src/features/ai/AiSdkToolHandlerResolver.ts"],"sourcesContent":["import type { Container, Constructor } from \"@webiny/di\";\nimport { RequestContainer } from \"@webiny/event-handler-core/features/events/RequestContainer.js\";\nimport { AiSdkToolHandler, AiSdkToolHandlerResolver as Abstraction } from \"./abstractions.js\";\n\nclass ContainerAiSdkToolHandlerResolver implements Abstraction.Interface {\n constructor(private readonly container: Container) {}\n\n resolve<TInput>(\n handler: Constructor<AiSdkToolHandler.Interface<TInput>>\n ): AiSdkToolHandler.Interface<TInput> {\n /*\n * `resolveImplementation` builds the class from its own dependency metadata and runs it\n * through the normal resolution path, so decorators registered against the handler still\n * apply. It deliberately ignores registrations, which is what we want here: the tool names\n * the class directly and nothing registers handlers against an abstraction.\n */\n return this.container.resolveImplementation(handler);\n }\n}\n\nexport const AiSdkToolHandlerResolver = Abstraction.createImplementation({\n implementation: ContainerAiSdkToolHandlerResolver,\n dependencies: [RequestContainer]\n});\n"],"names":["ContainerAiSdkToolHandlerResolver","container","handler","AiSdkToolHandlerResolver","Abstraction","RequestContainer"],"mappings":";;AAIA,MAAMA;IACF,YAA6BC,SAAoB,CAAE;aAAtBA,SAAS,GAATA;IAAuB;IAEpD,QACIC,OAAwD,EACtB;QAOlC,OAAO,IAAI,CAAC,SAAS,CAAC,qBAAqB,CAACA;IAChD;AACJ;AAEO,MAAMC,oDAA2BC,yBAAAA,oBAAgC,CAAC;IACrE,gBAAgBJ;IAChB,cAAc;QAACK;KAAiB;AACpC"}
@@ -1,8 +1,9 @@
1
- import { AiSdkTool, AiSdkTools as AiSdkToolsAbstraction } from "./abstractions.js";
1
+ import { AiSdkToolDefinition, AiSdkToolHandlerResolver, AiSdkTools as AiSdkToolsAbstraction } from "./abstractions.js";
2
2
  import type { ToolSet } from "ai";
3
3
  declare class AiSdkToolsImpl implements AiSdkToolsAbstraction.Interface {
4
- private tools;
5
- constructor(tools: AiSdkTool.Interface[]);
4
+ private definitions;
5
+ private resolver;
6
+ constructor(definitions: AiSdkToolDefinition.Interface[], resolver: AiSdkToolHandlerResolver.Interface);
6
7
  getToolSet(): ToolSet;
7
8
  }
8
9
  export declare const AiSdkTools: typeof AiSdkToolsImpl & {
@@ -1,14 +1,15 @@
1
- import { AiSdkTool, AiSdkTools } from "./abstractions.js";
1
+ import { AiSdkToolDefinition, AiSdkToolHandlerResolver, AiSdkTools } from "./abstractions.js";
2
2
  class AiSdkToolsImpl {
3
- constructor(tools){
4
- this.tools = tools;
3
+ constructor(definitions, resolver){
4
+ this.definitions = definitions;
5
+ this.resolver = resolver;
5
6
  }
6
7
  getToolSet() {
7
8
  const map = {};
8
- for (const t of this.tools)map[t.name] = {
9
- description: t.description,
10
- inputSchema: t.inputSchema,
11
- execute: (input)=>t.execute(input)
9
+ for (const definition of this.definitions)map[definition.name] = {
10
+ description: definition.description,
11
+ inputSchema: definition.inputSchema,
12
+ execute: (input)=>this.resolver.resolve(definition.handler).execute(input)
12
13
  };
13
14
  return map;
14
15
  }
@@ -17,11 +18,12 @@ const AiSdkTools_AiSdkTools = AiSdkTools.createImplementation({
17
18
  implementation: AiSdkToolsImpl,
18
19
  dependencies: [
19
20
  [
20
- AiSdkTool,
21
+ AiSdkToolDefinition,
21
22
  {
22
23
  multiple: true
23
24
  }
24
- ]
25
+ ],
26
+ AiSdkToolHandlerResolver
25
27
  ]
26
28
  });
27
29
  export { AiSdkTools_AiSdkTools as AiSdkTools };
@@ -1 +1 @@
1
- {"version":3,"file":"features/ai/AiSdkTools.js","sources":["../../../src/features/ai/AiSdkTools.ts"],"sourcesContent":["import { AiSdkTool, AiSdkTools as AiSdkToolsAbstraction } from \"./abstractions.js\";\nimport type { ToolSet } from \"ai\";\n\nclass AiSdkToolsImpl implements AiSdkToolsAbstraction.Interface {\n constructor(private tools: AiSdkTool.Interface[]) {}\n\n getToolSet(): ToolSet {\n const map: ToolSet = {};\n for (const t of this.tools) {\n map[t.name] = {\n description: t.description,\n inputSchema: t.inputSchema,\n execute: (input: unknown) => t.execute(input)\n };\n }\n return map;\n }\n}\n\nexport const AiSdkTools = AiSdkToolsAbstraction.createImplementation({\n implementation: AiSdkToolsImpl,\n dependencies: [[AiSdkTool, { multiple: true }]]\n});\n"],"names":["AiSdkToolsImpl","tools","map","t","input","AiSdkTools","AiSdkToolsAbstraction","AiSdkTool"],"mappings":";AAGA,MAAMA;IACF,YAAoBC,KAA4B,CAAE;aAA9BA,KAAK,GAALA;IAA+B;IAEnD,aAAsB;QAClB,MAAMC,MAAe,CAAC;QACtB,KAAK,MAAMC,KAAK,IAAI,CAAC,KAAK,CACtBD,GAAG,CAACC,EAAE,IAAI,CAAC,GAAG;YACV,aAAaA,EAAE,WAAW;YAC1B,aAAaA,EAAE,WAAW;YAC1B,SAAS,CAACC,QAAmBD,EAAE,OAAO,CAACC;QAC3C;QAEJ,OAAOF;IACX;AACJ;AAEO,MAAMG,wBAAaC,WAAAA,oBAA0C,CAAC;IACjE,gBAAgBN;IAChB,cAAc;QAAC;YAACO;YAAW;gBAAE,UAAU;YAAK;SAAE;KAAC;AACnD"}
1
+ {"version":3,"file":"features/ai/AiSdkTools.js","sources":["../../../src/features/ai/AiSdkTools.ts"],"sourcesContent":["import {\n AiSdkToolDefinition,\n AiSdkToolHandlerResolver,\n AiSdkTools as AiSdkToolsAbstraction\n} from \"./abstractions.js\";\nimport type { ToolSet } from \"ai\";\n\nclass AiSdkToolsImpl implements AiSdkToolsAbstraction.Interface {\n constructor(\n private definitions: AiSdkToolDefinition.Interface[],\n private resolver: AiSdkToolHandlerResolver.Interface\n ) {}\n\n getToolSet(): ToolSet {\n const map: ToolSet = {};\n for (const definition of this.definitions) {\n map[definition.name] = {\n description: definition.description,\n inputSchema: definition.inputSchema,\n /*\n * Deliberately resolved inside the call, not here. The model is offered every\n * tool and calls a few, which is what keeps the rest of their use cases unbuilt.\n *\n * `resolve` IS `container.resolveImplementation`, one layer down. It lives behind\n * an abstraction so this class does not inject a container. Note it builds the\n * class the definition names; resolving the `AiSdkToolHandler` abstraction would\n * find nothing, since handlers are declared through it but never registered.\n */\n execute: (input: unknown) =>\n this.resolver.resolve(definition.handler).execute(input)\n };\n }\n return map;\n }\n}\n\nexport const AiSdkTools = AiSdkToolsAbstraction.createImplementation({\n implementation: AiSdkToolsImpl,\n dependencies: [[AiSdkToolDefinition, { multiple: true }], AiSdkToolHandlerResolver]\n});\n"],"names":["AiSdkToolsImpl","definitions","resolver","map","definition","input","AiSdkTools","AiSdkToolsAbstraction","AiSdkToolDefinition","AiSdkToolHandlerResolver"],"mappings":";AAOA,MAAMA;IACF,YACYC,WAA4C,EAC5CC,QAA4C,CACtD;aAFUD,WAAW,GAAXA;aACAC,QAAQ,GAARA;IACT;IAEH,aAAsB;QAClB,MAAMC,MAAe,CAAC;QACtB,KAAK,MAAMC,cAAc,IAAI,CAAC,WAAW,CACrCD,GAAG,CAACC,WAAW,IAAI,CAAC,GAAG;YACnB,aAAaA,WAAW,WAAW;YACnC,aAAaA,WAAW,WAAW;YAUnC,SAAS,CAACC,QACN,IAAI,CAAC,QAAQ,CAAC,OAAO,CAACD,WAAW,OAAO,EAAE,OAAO,CAACC;QAC1D;QAEJ,OAAOF;IACX;AACJ;AAEO,MAAMG,wBAAaC,WAAAA,oBAA0C,CAAC;IACjE,gBAAgBP;IAChB,cAAc;QAAC;YAACQ;YAAqB;gBAAE,UAAU;YAAK;SAAE;QAAEC;KAAyB;AACvF"}
@@ -1,3 +1,4 @@
1
+ import type { Constructor } from "@webiny/di";
1
2
  import type { generateText } from "ai";
2
3
  import type { streamText } from "ai";
3
4
  import type { LanguageModel } from "ai";
@@ -85,21 +86,59 @@ export interface IAiSdkToolAnnotations {
85
86
  /** Tool interacts with entities outside its own closed world. */
86
87
  openWorldHint?: boolean;
87
88
  }
88
- export interface IAiSdkTool<TInput = any> {
89
+ /**
90
+ * What a tool DOES. This is the half that injects use cases, so it is built only for a tool the
91
+ * model actually calls, and only at the moment it calls it.
92
+ */
93
+ export interface IAiSdkToolHandler<TInput = any> {
94
+ execute(input: TInput): Promise<unknown>;
95
+ }
96
+ /**
97
+ * What a tool IS: everything the model is told about it, plus the class that runs it.
98
+ *
99
+ * Carries no behaviour and no dependencies, which is what makes the registry cheap to read.
100
+ * `AiSdkTools` builds every definition to assemble the tool set, and builds a handler only for a
101
+ * tool the model actually calls.
102
+ */
103
+ export interface IAiSdkToolDefinition<TInput = any> {
89
104
  readonly name: string;
90
105
  readonly description: string;
91
106
  readonly inputSchema: FlexibleSchema<TInput>;
92
107
  /** Human-friendly display name. Falls back to `name` when omitted. */
93
108
  readonly title?: string;
94
109
  readonly annotations?: IAiSdkToolAnnotations;
95
- execute(input: TInput): Promise<unknown>;
110
+ readonly handler: Constructor<IAiSdkToolHandler<TInput>>;
96
111
  }
97
- /** A single tool that can be provided to AI generateText/streamText calls. */
98
- export declare const AiSdkTool: import("@webiny/di").Abstraction<IAiSdkTool<any>>;
99
- export declare namespace AiSdkTool {
100
- type Interface = IAiSdkTool;
112
+ export declare const AiSdkToolDefinition: import("@webiny/di").Abstraction<IAiSdkToolDefinition<any>>;
113
+ /**
114
+ * The behaviour half of a tool, built on demand through {@link AiSdkToolHandlerResolver}.
115
+ *
116
+ * Implementations are NOT registered against this abstraction. The definition points at the class
117
+ * directly, exactly as `HttpRouteDefinition` points at its `HttpRouteHandler`. Declaring them
118
+ * through it is what attaches their dependency metadata.
119
+ */
120
+ export declare const AiSdkToolHandler: import("@webiny/di").Abstraction<IAiSdkToolHandler<any>>;
121
+ export declare namespace AiSdkToolDefinition {
122
+ type Interface<TInput = any> = IAiSdkToolDefinition<TInput>;
101
123
  type Annotations = IAiSdkToolAnnotations;
102
124
  }
125
+ export declare namespace AiSdkToolHandler {
126
+ type Interface<TInput = any> = IAiSdkToolHandler<TInput>;
127
+ }
128
+ export interface IAiSdkToolHandlerResolver {
129
+ resolve<TInput>(handler: Constructor<IAiSdkToolHandler<TInput>>): IAiSdkToolHandler<TInput>;
130
+ }
131
+ /**
132
+ * Builds the behaviour half of a tool on demand.
133
+ *
134
+ * Something has to turn `tool.handler` (a class) into an instance, and that needs the container.
135
+ * Rather than injecting the container into the tool registry, it lives behind this one narrow
136
+ * abstraction, so everything else depends on `resolve(handler)` and stays testable with a stub.
137
+ */
138
+ export declare const AiSdkToolHandlerResolver: import("@webiny/di").Abstraction<IAiSdkToolHandlerResolver>;
139
+ export declare namespace AiSdkToolHandlerResolver {
140
+ type Interface = IAiSdkToolHandlerResolver;
141
+ }
103
142
  export interface IAiSdkTools {
104
143
  getToolSet(): ToolSet;
105
144
  }
@@ -3,8 +3,10 @@ const AiSdk = createAbstraction("AiSdk");
3
3
  const AiSdkFactory = createAbstraction("AiSdkFactory");
4
4
  const AiConnectionFactory = createAbstraction("AiConnectionFactory");
5
5
  const Ai = createAbstraction("Ai");
6
- const AiSdkTool = createAbstraction("AiSdkTool");
6
+ const AiSdkToolDefinition = createAbstraction("AiSdkToolDefinition");
7
+ const AiSdkToolHandler = createAbstraction("AiSdkToolHandler");
8
+ const AiSdkToolHandlerResolver = createAbstraction("AiSdkToolHandlerResolver");
7
9
  const AiSdkTools = createAbstraction("AiSdkTools");
8
- export { Ai, AiConnectionFactory, AiSdk, AiSdkFactory, AiSdkTool, AiSdkTools };
10
+ export { Ai, AiConnectionFactory, AiSdk, AiSdkFactory, AiSdkToolDefinition, AiSdkToolHandler, AiSdkToolHandlerResolver, AiSdkTools };
9
11
 
10
12
  //# sourceMappingURL=abstractions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"features/ai/abstractions.js","sources":["../../../src/features/ai/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/api\";\nimport type { generateText } from \"ai\";\nimport type { streamText } from \"ai\";\nimport type { LanguageModel } from \"ai\";\nimport type { FlexibleSchema, ToolSet } from \"ai\";\n\n// AiSdk\n\nexport interface IAiSdkModel {\n id: string; // raw model id, e.g. \"claude-sonnet-4-5\"\n name: string; // human-friendly name, e.g. \"Claude Sonnet 4.5\"\n}\n\nexport interface IAiSdk {\n languageModel(modelId: string): LanguageModel;\n}\n\n/** A single AI SDK instance (e.g. OpenAI, Anthropic) that resolves model instances. */\nexport const AiSdk = createAbstraction<IAiSdk>(\"AiSdk\");\n\nexport namespace AiSdk {\n export type Interface = IAiSdk;\n}\n\n// AiSdkFactory\n\nexport interface IAiSdkFactory {\n readonly id: string; // machine id, e.g. \"anthropic\"\n readonly name: string; // human-friendly name, e.g. \"Anthropic\"\n readonly models: readonly IAiSdkModel[]; // static model list, no API call needed\n execute(apiKey?: string): Promise<IAiSdk>;\n}\n\n/** Factory that asynchronously initialises an AI SDK. Register one per provider namespace. */\nexport const AiSdkFactory = createAbstraction<IAiSdkFactory>(\"AiSdkFactory\");\n\nexport namespace AiSdkFactory {\n export type Interface = IAiSdkFactory;\n}\n\n// AiConnection\n\nexport interface IAiConnectionInline {\n readonly sdkName: string;\n readonly apiKey?: string;\n}\n\nexport interface IAiConnection extends IAiConnectionInline {\n readonly id: string;\n}\n\n// AiConnectionFactory\n\nexport interface IAiConnectionFactory {\n execute(): Promise<IAiConnection>;\n}\n\n/** Factory that asynchronously produces an AiConnection. */\nexport const AiConnectionFactory = createAbstraction<IAiConnectionFactory>(\"AiConnectionFactory\");\n\nexport namespace AiConnectionFactory {\n export type Interface = IAiConnectionFactory;\n}\n\n// Ai\n\ntype SDKGenerateTextParams = Parameters<typeof generateText>[0];\ntype SDKStreamTextParams = Parameters<typeof streamText>[0];\n\nexport type AiGenerateTextParams = Omit<SDKGenerateTextParams, \"model\"> & {\n model: string;\n connection?: string | IAiConnectionInline;\n};\nexport type AiStreamTextParams = Omit<SDKStreamTextParams, \"model\"> & {\n model: string;\n connection?: string | IAiConnectionInline;\n};\n\nexport interface AiModel {\n providerId: string; // e.g. \"anthropic\"\n providerName: string; // e.g. \"Anthropic\"\n modelId: string; // e.g. \"claude-sonnet-4-5\"\n modelName: string; // e.g. \"Claude Sonnet 4.5\"\n}\n\nexport interface IAi {\n generateText(params: AiGenerateTextParams): ReturnType<typeof generateText>;\n streamText(params: AiStreamTextParams): Promise<ReturnType<typeof streamText>>;\n listModels(): Promise<AiModel[]>;\n listModelsByConnections(): Promise<AiModel[]>;\n listModelsByConnection(connection: string | IAiConnectionInline): Promise<AiModel[]>;\n}\n\n/** Interact with AI language models using registered providers. */\nexport const Ai = createAbstraction<IAi>(\"Ai\");\n\nexport namespace Ai {\n export type Interface = IAi;\n export type GenerateTextParams = AiGenerateTextParams;\n export type StreamTextParams = AiStreamTextParams;\n}\n\n// AiSdkTool\n\n/**\n * Behavioural hints about a tool. Purely advisory — they never replace a permission check.\n * Names mirror the MCP tool annotations so they can be forwarded verbatim to MCP clients,\n * which use them to decide what to auto-approve and what to confirm with the user.\n */\nexport interface IAiSdkToolAnnotations {\n /** Tool does not modify state. */\n readOnlyHint?: boolean;\n /** Tool may perform destructive updates (only meaningful when not read-only). */\n destructiveHint?: boolean;\n /** Repeated calls with the same arguments have no additional effect. */\n idempotentHint?: boolean;\n /** Tool interacts with entities outside its own closed world. */\n openWorldHint?: boolean;\n}\n\nexport interface IAiSdkTool<TInput = any> {\n readonly name: string;\n readonly description: string;\n readonly inputSchema: FlexibleSchema<TInput>;\n /** Human-friendly display name. Falls back to `name` when omitted. */\n readonly title?: string;\n readonly annotations?: IAiSdkToolAnnotations;\n execute(input: TInput): Promise<unknown>;\n}\n\n/** A single tool that can be provided to AI generateText/streamText calls. */\nexport const AiSdkTool = createAbstraction<IAiSdkTool>(\"AiSdkTool\");\n\nexport namespace AiSdkTool {\n export type Interface = IAiSdkTool;\n export type Annotations = IAiSdkToolAnnotations;\n}\n\n// AiSdkTools\n\nexport interface IAiSdkTools {\n getToolSet(): ToolSet;\n}\n\n/** Collection of AI SDK tools. Returns a ready-to-use ToolSet for generateText/streamText. */\nexport const AiSdkTools = createAbstraction<IAiSdkTools>(\"AiSdkTools\");\n\nexport namespace AiSdkTools {\n export type Interface = IAiSdkTools;\n}\n"],"names":["AiSdk","createAbstraction","AiSdkFactory","AiConnectionFactory","Ai","AiSdkTool","AiSdkTools"],"mappings":";AAkBO,MAAMA,QAAQC,kBAA0B;AAgBxC,MAAMC,eAAeD,kBAAiC;AAwBtD,MAAME,sBAAsBF,kBAAwC;AAoCpE,MAAMG,KAAKH,kBAAuB;AAqClC,MAAMI,YAAYJ,kBAA8B;AAchD,MAAMK,aAAaL,kBAA+B"}
1
+ {"version":3,"file":"features/ai/abstractions.js","sources":["../../../src/features/ai/abstractions.ts"],"sourcesContent":["import { createAbstraction } from \"@webiny/feature/api\";\nimport type { Constructor } from \"@webiny/di\";\nimport type { generateText } from \"ai\";\nimport type { streamText } from \"ai\";\nimport type { LanguageModel } from \"ai\";\nimport type { FlexibleSchema, ToolSet } from \"ai\";\n\n// AiSdk\n\nexport interface IAiSdkModel {\n id: string; // raw model id, e.g. \"claude-sonnet-4-5\"\n name: string; // human-friendly name, e.g. \"Claude Sonnet 4.5\"\n}\n\nexport interface IAiSdk {\n languageModel(modelId: string): LanguageModel;\n}\n\n/** A single AI SDK instance (e.g. OpenAI, Anthropic) that resolves model instances. */\nexport const AiSdk = createAbstraction<IAiSdk>(\"AiSdk\");\n\nexport namespace AiSdk {\n export type Interface = IAiSdk;\n}\n\n// AiSdkFactory\n\nexport interface IAiSdkFactory {\n readonly id: string; // machine id, e.g. \"anthropic\"\n readonly name: string; // human-friendly name, e.g. \"Anthropic\"\n readonly models: readonly IAiSdkModel[]; // static model list, no API call needed\n execute(apiKey?: string): Promise<IAiSdk>;\n}\n\n/** Factory that asynchronously initialises an AI SDK. Register one per provider namespace. */\nexport const AiSdkFactory = createAbstraction<IAiSdkFactory>(\"AiSdkFactory\");\n\nexport namespace AiSdkFactory {\n export type Interface = IAiSdkFactory;\n}\n\n// AiConnection\n\nexport interface IAiConnectionInline {\n readonly sdkName: string;\n readonly apiKey?: string;\n}\n\nexport interface IAiConnection extends IAiConnectionInline {\n readonly id: string;\n}\n\n// AiConnectionFactory\n\nexport interface IAiConnectionFactory {\n execute(): Promise<IAiConnection>;\n}\n\n/** Factory that asynchronously produces an AiConnection. */\nexport const AiConnectionFactory = createAbstraction<IAiConnectionFactory>(\"AiConnectionFactory\");\n\nexport namespace AiConnectionFactory {\n export type Interface = IAiConnectionFactory;\n}\n\n// Ai\n\ntype SDKGenerateTextParams = Parameters<typeof generateText>[0];\ntype SDKStreamTextParams = Parameters<typeof streamText>[0];\n\nexport type AiGenerateTextParams = Omit<SDKGenerateTextParams, \"model\"> & {\n model: string;\n connection?: string | IAiConnectionInline;\n};\nexport type AiStreamTextParams = Omit<SDKStreamTextParams, \"model\"> & {\n model: string;\n connection?: string | IAiConnectionInline;\n};\n\nexport interface AiModel {\n providerId: string; // e.g. \"anthropic\"\n providerName: string; // e.g. \"Anthropic\"\n modelId: string; // e.g. \"claude-sonnet-4-5\"\n modelName: string; // e.g. \"Claude Sonnet 4.5\"\n}\n\nexport interface IAi {\n generateText(params: AiGenerateTextParams): ReturnType<typeof generateText>;\n streamText(params: AiStreamTextParams): Promise<ReturnType<typeof streamText>>;\n listModels(): Promise<AiModel[]>;\n listModelsByConnections(): Promise<AiModel[]>;\n listModelsByConnection(connection: string | IAiConnectionInline): Promise<AiModel[]>;\n}\n\n/** Interact with AI language models using registered providers. */\nexport const Ai = createAbstraction<IAi>(\"Ai\");\n\nexport namespace Ai {\n export type Interface = IAi;\n export type GenerateTextParams = AiGenerateTextParams;\n export type StreamTextParams = AiStreamTextParams;\n}\n\n// AiSdkToolDefinition\n\n/**\n * Behavioural hints about a tool. Purely advisory — they never replace a permission check.\n * Names mirror the MCP tool annotations so they can be forwarded verbatim to MCP clients,\n * which use them to decide what to auto-approve and what to confirm with the user.\n */\nexport interface IAiSdkToolAnnotations {\n /** Tool does not modify state. */\n readOnlyHint?: boolean;\n /** Tool may perform destructive updates (only meaningful when not read-only). */\n destructiveHint?: boolean;\n /** Repeated calls with the same arguments have no additional effect. */\n idempotentHint?: boolean;\n /** Tool interacts with entities outside its own closed world. */\n openWorldHint?: boolean;\n}\n\n/**\n * What a tool DOES. This is the half that injects use cases, so it is built only for a tool the\n * model actually calls, and only at the moment it calls it.\n */\nexport interface IAiSdkToolHandler<TInput = any> {\n execute(input: TInput): Promise<unknown>;\n}\n\n/**\n * What a tool IS: everything the model is told about it, plus the class that runs it.\n *\n * Carries no behaviour and no dependencies, which is what makes the registry cheap to read.\n * `AiSdkTools` builds every definition to assemble the tool set, and builds a handler only for a\n * tool the model actually calls.\n */\nexport interface IAiSdkToolDefinition<TInput = any> {\n readonly name: string;\n readonly description: string;\n readonly inputSchema: FlexibleSchema<TInput>;\n /** Human-friendly display name. Falls back to `name` when omitted. */\n readonly title?: string;\n readonly annotations?: IAiSdkToolAnnotations;\n readonly handler: Constructor<IAiSdkToolHandler<TInput>>;\n}\n\nexport const AiSdkToolDefinition = createAbstraction<IAiSdkToolDefinition>(\"AiSdkToolDefinition\");\n\n/**\n * The behaviour half of a tool, built on demand through {@link AiSdkToolHandlerResolver}.\n *\n * Implementations are NOT registered against this abstraction. The definition points at the class\n * directly, exactly as `HttpRouteDefinition` points at its `HttpRouteHandler`. Declaring them\n * through it is what attaches their dependency metadata.\n */\nexport const AiSdkToolHandler = createAbstraction<IAiSdkToolHandler>(\"AiSdkToolHandler\");\n\nexport namespace AiSdkToolDefinition {\n export type Interface<TInput = any> = IAiSdkToolDefinition<TInput>;\n export type Annotations = IAiSdkToolAnnotations;\n}\n\nexport namespace AiSdkToolHandler {\n export type Interface<TInput = any> = IAiSdkToolHandler<TInput>;\n}\n\n// AiSdkToolHandlerResolver\n\nexport interface IAiSdkToolHandlerResolver {\n resolve<TInput>(handler: Constructor<IAiSdkToolHandler<TInput>>): IAiSdkToolHandler<TInput>;\n}\n\n/**\n * Builds the behaviour half of a tool on demand.\n *\n * Something has to turn `tool.handler` (a class) into an instance, and that needs the container.\n * Rather than injecting the container into the tool registry, it lives behind this one narrow\n * abstraction, so everything else depends on `resolve(handler)` and stays testable with a stub.\n */\nexport const AiSdkToolHandlerResolver = createAbstraction<IAiSdkToolHandlerResolver>(\n \"AiSdkToolHandlerResolver\"\n);\n\nexport namespace AiSdkToolHandlerResolver {\n export type Interface = IAiSdkToolHandlerResolver;\n}\n\n// AiSdkTools\n\nexport interface IAiSdkTools {\n getToolSet(): ToolSet;\n}\n\n/** Collection of AI SDK tools. Returns a ready-to-use ToolSet for generateText/streamText. */\nexport const AiSdkTools = createAbstraction<IAiSdkTools>(\"AiSdkTools\");\n\nexport namespace AiSdkTools {\n export type Interface = IAiSdkTools;\n}\n"],"names":["AiSdk","createAbstraction","AiSdkFactory","AiConnectionFactory","Ai","AiSdkToolDefinition","AiSdkToolHandler","AiSdkToolHandlerResolver","AiSdkTools"],"mappings":";AAmBO,MAAMA,QAAQC,kBAA0B;AAgBxC,MAAMC,eAAeD,kBAAiC;AAwBtD,MAAME,sBAAsBF,kBAAwC;AAoCpE,MAAMG,KAAKH,kBAAuB;AAmDlC,MAAMI,sBAAsBJ,kBAAwC;AASpE,MAAMK,mBAAmBL,kBAAqC;AAwB9D,MAAMM,2BAA2BN,kBACpC;AAcG,MAAMO,aAAaP,kBAA+B"}
@@ -3,6 +3,7 @@ import { OpenAiSdkFactory } from "./OpenAiSdkFactory.js";
3
3
  import { AnthropicSdkFactory } from "./AnthropicSdkFactory.js";
4
4
  import { Ai } from "./Ai.js";
5
5
  import { AiSdkTools } from "./AiSdkTools.js";
6
+ import { AiSdkToolHandlerResolver } from "./AiSdkToolHandlerResolver.js";
6
7
  import { AiOutputToolRegistry } from "./toolPipeline/AiOutputToolRegistry.js";
7
8
  import { AiToolPipelineRunner } from "./toolPipeline/AiToolPipelineRunner.js";
8
9
  import { DefaultTextExtractor } from "./TextExtractor/DefaultTextExtractor.js";
@@ -13,6 +14,7 @@ const AiFeature = createFeature({
13
14
  container.register(AnthropicSdkFactory);
14
15
  container.register(Ai);
15
16
  container.register(AiSdkTools);
17
+ container.register(AiSdkToolHandlerResolver);
16
18
  container.register(AiOutputToolRegistry);
17
19
  container.register(AiToolPipelineRunner);
18
20
  container.register(DefaultTextExtractor);
@@ -1 +1 @@
1
- {"version":3,"file":"features/ai/feature.js","sources":["../../../src/features/ai/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport { OpenAiSdkFactory } from \"./OpenAiSdkFactory.js\";\nimport { AnthropicSdkFactory } from \"./AnthropicSdkFactory.js\";\nimport { Ai } from \"./Ai.js\";\nimport { AiSdkTools } from \"./AiSdkTools.js\";\nimport { AiOutputToolRegistry } from \"./toolPipeline/AiOutputToolRegistry.js\";\nimport { AiToolPipelineRunner } from \"./toolPipeline/AiToolPipelineRunner.js\";\nimport { DefaultTextExtractor } from \"./TextExtractor/DefaultTextExtractor.js\";\n\nexport const AiFeature = createFeature({\n name: \"AiFeature\",\n register(container) {\n container.register(OpenAiSdkFactory);\n container.register(AnthropicSdkFactory);\n container.register(Ai);\n container.register(AiSdkTools);\n container.register(AiOutputToolRegistry);\n container.register(AiToolPipelineRunner);\n container.register(DefaultTextExtractor);\n }\n});\n"],"names":["AiFeature","createFeature","container","OpenAiSdkFactory","AnthropicSdkFactory","Ai","AiSdkTools","AiOutputToolRegistry","AiToolPipelineRunner","DefaultTextExtractor"],"mappings":";;;;;;;;AASO,MAAMA,YAAYC,cAAc;IACnC,MAAM;IACN,UAASC,SAAS;QACdA,UAAU,QAAQ,CAACC;QACnBD,UAAU,QAAQ,CAACE;QACnBF,UAAU,QAAQ,CAACG;QACnBH,UAAU,QAAQ,CAACI;QACnBJ,UAAU,QAAQ,CAACK;QACnBL,UAAU,QAAQ,CAACM;QACnBN,UAAU,QAAQ,CAACO;IACvB;AACJ"}
1
+ {"version":3,"file":"features/ai/feature.js","sources":["../../../src/features/ai/feature.ts"],"sourcesContent":["import { createFeature } from \"@webiny/feature/api\";\nimport { OpenAiSdkFactory } from \"./OpenAiSdkFactory.js\";\nimport { AnthropicSdkFactory } from \"./AnthropicSdkFactory.js\";\nimport { Ai } from \"./Ai.js\";\nimport { AiSdkTools } from \"./AiSdkTools.js\";\nimport { AiSdkToolHandlerResolver } from \"./AiSdkToolHandlerResolver.js\";\nimport { AiOutputToolRegistry } from \"./toolPipeline/AiOutputToolRegistry.js\";\nimport { AiToolPipelineRunner } from \"./toolPipeline/AiToolPipelineRunner.js\";\nimport { DefaultTextExtractor } from \"./TextExtractor/DefaultTextExtractor.js\";\n\nexport const AiFeature = createFeature({\n name: \"AiFeature\",\n register(container) {\n container.register(OpenAiSdkFactory);\n container.register(AnthropicSdkFactory);\n container.register(Ai);\n container.register(AiSdkTools);\n container.register(AiSdkToolHandlerResolver);\n container.register(AiOutputToolRegistry);\n container.register(AiToolPipelineRunner);\n container.register(DefaultTextExtractor);\n }\n});\n"],"names":["AiFeature","createFeature","container","OpenAiSdkFactory","AnthropicSdkFactory","Ai","AiSdkTools","AiSdkToolHandlerResolver","AiOutputToolRegistry","AiToolPipelineRunner","DefaultTextExtractor"],"mappings":";;;;;;;;;AAUO,MAAMA,YAAYC,cAAc;IACnC,MAAM;IACN,UAASC,SAAS;QACdA,UAAU,QAAQ,CAACC;QACnBD,UAAU,QAAQ,CAACE;QACnBF,UAAU,QAAQ,CAACG;QACnBH,UAAU,QAAQ,CAACI;QACnBJ,UAAU,QAAQ,CAACK;QACnBL,UAAU,QAAQ,CAACM;QACnBN,UAAU,QAAQ,CAACO;QACnBP,UAAU,QAAQ,CAACQ;IACvB;AACJ"}
@@ -1,5 +1,5 @@
1
- export { AiSdk, AiSdkFactory, AiConnectionFactory, Ai, AiSdkTool, AiSdkTools } from "./abstractions.js";
2
- export type { IAiConnection, IAiConnectionInline, AiModel, IAiSdkModel, IAiSdkTool, IAiSdkToolAnnotations, IAiSdkTools } from "./abstractions.js";
1
+ export { AiSdk, AiSdkFactory, AiConnectionFactory, Ai, AiSdkToolDefinition, AiSdkToolHandler, AiSdkToolHandlerResolver, AiSdkTools } from "./abstractions.js";
2
+ export type { IAiConnection, IAiConnectionInline, AiModel, IAiSdkModel } from "./abstractions.js";
3
3
  export { AiOutputTool, AiOutputToolRegistry, AiToolPipelineRunner } from "./toolPipeline/index.js";
4
4
  export type { IAiOutputTool, IAiOutputToolRegistry, IAiToolPipelineRunner } from "./toolPipeline/index.js";
5
5
  export { TextExtractor, DefaultTextExtractor } from "./TextExtractor/index.js";
@@ -1,4 +1,4 @@
1
- export { Ai, AiConnectionFactory, AiSdk, AiSdkFactory, AiSdkTool, AiSdkTools } from "./abstractions.js";
1
+ export { Ai, AiConnectionFactory, AiSdk, AiSdkFactory, AiSdkToolDefinition, AiSdkToolHandler, AiSdkToolHandlerResolver, AiSdkTools } from "./abstractions.js";
2
2
  export { AiOutputTool, AiOutputToolRegistry, AiToolPipelineRunner } from "./toolPipeline/index.js";
3
3
  export { DefaultTextExtractor, TextExtractor } from "./TextExtractor/index.js";
4
4
  export { AiAfterGenerateTextEvent, AiAfterGenerateTextEventHandler, AiBeforeGenerateTextEvent, AiBeforeGenerateTextEventHandler, AiBeforeStreamTextEvent, AiBeforeStreamTextEventHandler, AiGenerateTextErrorEvent, AiGenerateTextErrorEventHandler } from "./events.js";
@@ -22,13 +22,14 @@ export interface ITaskOutput {
22
22
  * wraps every task; without this it has nothing to branch on but a hardcoded list of ids, which
23
23
  * means editing the decorator every time a task is added.
24
24
  *
25
- * This is {@link ITaskMetadata}: identity and policy, no behaviour. The object passed at runtime is
26
- * the resolved task, so `run` is physically present; naming the metadata type here stops the obvious
27
- * mistake of a handler calling `params.definition.run(...)` and recursing forever.
25
+ * This is {@link ITaskMetadata}: identity and policy, no behaviour and no handler pointer. The
26
+ * object passed at runtime is the resolved task, so `run` is physically present; naming the metadata
27
+ * type here stops the obvious mistake of a handler calling `params.definition.run(...)` and
28
+ * recursing forever.
28
29
  *
29
30
  * Only the fields declared here arrive. A field a project adds to its own definition class is
30
- * currently dropped, because `RunnableTaskDecorator` and `SelfCleaningTaskDecorator` are fixed
31
- * pass-throughs that expose a known set of getters and nothing else. Making them forward unknown
31
+ * currently dropped, because `TaskDefinitionDefaultsDecorator` is a fixed pass-through that
32
+ * exposes a known set of getters and nothing else. Making them forward unknown
32
33
  * properties would turn a definition into a place to declare policy (a rate limit, a set of tags)
33
34
  * that a decorator acts on; `taskDefinitionInParams.test.ts` pins the current behaviour so that
34
35
  * change announces itself.
@@ -99,7 +100,7 @@ export type ITaskLifecycleHook<I extends ITaskInput = ITaskInput, O extends ITas
99
100
  * What a task IS: identity and runtime policy, with no behaviour and no dependencies.
100
101
  *
101
102
  * Listing tasks, resolving one by id, and applying defaults all need only this. Keeping it free of
102
- * dependencies is the whole point — `GetTaskDefinitionUseCase` builds every registered definition
103
+ * dependencies is the whole point — `GetRunnableTaskDefinitionUseCase` builds every registered definition
103
104
  * to find one by id, so anything expensive here is paid 24 times per lookup.
104
105
  */
105
106
  export interface ITaskMetadata {
@@ -140,22 +141,14 @@ export interface ITaskHandler<I extends ITaskInput = ITaskInput, O extends ITask
140
141
  createInputValidation?(params: ITaskCreateInputValidationParams): GenericRecord<keyof I, zod.Schema> | zod.Schema;
141
142
  }
142
143
  /**
143
- * Core TaskDefinition - minimal interface
144
+ * What you register: a task's {@link ITaskMetadata} plus the class that does the work.
144
145
  *
145
- * TRANSITIONAL SHAPE. A definition supplies its behaviour in one of two ways:
146
- *
147
- * - the new way: `handler` names a {@link ITaskHandler} class, which the runner builds only for the
148
- * task it is about to run;
149
- * - the old way: the definition implements `run()` and the hooks itself, which forces every
150
- * definition (and every dependency it injects) to be built just to look one up by id.
151
- *
152
- * `run` is optional ONLY to let both shapes coexist while packages migrate. Once every definition
153
- * carries a `handler`, this interface becomes `ITaskMetadata & { handler }` and the optionality
154
- * goes away. Exactly one of `run` or `handler` must be present; `GetTaskDefinitionUseCase` fails
155
- * with {@link TaskDefinitionNotRunnableError} if neither is.
146
+ * The definition takes no dependencies of its own, so `GetRunnableTaskDefinitionUseCase` can build every
147
+ * registered one to find a task by id without constructing anything expensive. Only the winner's
148
+ * `handler` gets built.
156
149
  */
157
- export interface ITaskDefinition<I extends ITaskInput = ITaskInput, O extends ITaskOutput = ITaskOutput> extends ITaskMetadata, Partial<ITaskHandler<I, O>> {
158
- handler?: Constructor<ITaskHandler<I, O>>;
150
+ export interface ITaskDefinition<I extends ITaskInput = ITaskInput, O extends ITaskOutput = ITaskOutput> extends ITaskMetadata {
151
+ handler: Constructor<ITaskHandler<I, O>>;
159
152
  }
160
153
  export interface ITaskCreateInputValidationParams {
161
154
  validator: typeof zod;
@@ -1 +1 @@
1
- {"version":3,"file":"features/task/TaskDefinition/abstractions.js","sources":["../../../../src/features/task/TaskDefinition/abstractions.ts"],"sourcesContent":["import zod from \"zod\";\nimport type { Constructor } from \"@webiny/di\";\nimport { createAbstraction } from \"@webiny/feature/api\";\nimport type { GenericRecord } from \"@webiny/api/types\";\nimport type { ITask } from \"~/features/task/TaskService/index.js\";\nimport { TaskController } from \"~/features/task/TaskController/index.js\";\n\nexport type ITaskInput = GenericRecord;\n\nexport interface IResponseError {\n message: string;\n code?: string | null;\n data?: GenericRecord | null;\n stack?: string;\n}\n\nexport interface ITaskOutput {\n error?: IResponseError;\n [key: string]: any;\n}\n\n/**\n * The task's own definition, minus its behaviour.\n *\n * Handed to `run()` and to every lifecycle hook so a decorator can tell which task it is wrapping\n * and act on the definition's own fields. A decorator is registered against the abstraction, so it\n * wraps every task; without this it has nothing to branch on but a hardcoded list of ids, which\n * means editing the decorator every time a task is added.\n *\n * This is {@link ITaskMetadata}: identity and policy, no behaviour. The object passed at runtime is\n * the resolved task, so `run` is physically present; naming the metadata type here stops the obvious\n * mistake of a handler calling `params.definition.run(...)` and recursing forever.\n *\n * Only the fields declared here arrive. A field a project adds to its own definition class is\n * currently dropped, because `RunnableTaskDecorator` and `SelfCleaningTaskDecorator` are fixed\n * pass-throughs that expose a known set of getters and nothing else. Making them forward unknown\n * properties would turn a definition into a place to declare policy (a rate limit, a set of tags)\n * that a decorator acts on; `taskDefinitionInParams.test.ts` pins the current behaviour so that\n * change announces itself.\n */\nexport type ITaskDefinitionInfo = ITaskMetadata;\n\n/**\n * Task run params - the input data, plus the definition being run\n * All runtime dependencies come from TaskController (injected separately)\n */\nexport interface ITaskRunParams<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n> {\n input: I;\n controller: TaskController.Interface<I, O>;\n definition: ITaskDefinitionInfo;\n}\n\n/**\n * Data for creating a new task\n */\nexport interface ITaskCreateData<I = ITaskInput> {\n definitionId: string;\n name: string;\n input: I;\n parentId?: string;\n}\n\n/**\n * Parameters for onBeforeTrigger lifecycle hook\n */\nexport interface ITaskBeforeTriggerParams<I = ITaskInput> {\n data: ITaskCreateData<I>;\n definition: ITaskDefinitionInfo;\n}\n\n/**\n * Task result status enum\n */\nexport enum TaskResultStatus {\n DONE = \"done\",\n CONTINUE = \"continue\",\n ERROR = \"error\",\n ABORTED = \"aborted\"\n}\n\n/**\n * Specific result types\n */\nexport interface ITaskResultDone<O extends ITaskOutput = ITaskOutput> {\n status: TaskResultStatus.DONE;\n message?: string;\n output?: O;\n}\n\nexport interface ITaskResultContinue<I = ITaskInput> {\n status: TaskResultStatus.CONTINUE;\n input: I;\n wait?: number; // seconds to wait before next iteration\n}\n\nexport interface ITaskResultError {\n status: TaskResultStatus.ERROR;\n error: IResponseError;\n}\n\nexport interface ITaskResultAborted {\n status: TaskResultStatus.ABORTED;\n}\n\nexport type ITaskResult<I = ITaskInput, O extends ITaskOutput = ITaskOutput> =\n | ITaskResultDone<O>\n | ITaskResultContinue<I>\n | ITaskResultError\n | ITaskResultAborted;\n\nexport type ISelfCleanupEvent = \"onSuccess\" | \"onError\" | \"onAbort\";\n\nexport type ISelfCleanup = \"always\" | \"never\" | ISelfCleanupEvent | ISelfCleanupEvent[];\n\nexport type ITaskLifecycleHook<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n> = {\n task: ITask<I, O>;\n definition: ITaskDefinitionInfo;\n};\n\n/**\n * What a task IS: identity and runtime policy, with no behaviour and no dependencies.\n *\n * Listing tasks, resolving one by id, and applying defaults all need only this. Keeping it free of\n * dependencies is the whole point — `GetTaskDefinitionUseCase` builds every registered definition\n * to find one by id, so anything expensive here is paid 24 times per lookup.\n */\nexport interface ITaskMetadata {\n id: string;\n title: string;\n description?: string;\n maxIterations?: number;\n databaseLogs?: boolean;\n isPrivate?: boolean;\n selfCleanup?: ISelfCleanup;\n}\n\n/**\n * What a task DOES. This is the half that carries dependencies (a CMS context, an OpenSearch\n * client), so it is built only for the task actually being run.\n *\n * The lifecycle hooks live here rather than on {@link ITaskMetadata} because they need the same\n * dependencies `run()` does — see `MockDataManagerTask`, whose `onError` and `onAbort` both use the\n * OpenSearch client injected for `run()`.\n */\nexport interface ITaskHandler<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n> {\n /**\n * Core run method - receives ONLY input params\n * All runtime dependencies (logging, state management, etc.) come from TaskController\n */\n run(params: ITaskRunParams<I, O>): Promise<ITaskResult<I, O>>;\n\n /**\n * Optional lifecycle hooks - receive task data, no context.\n */\n onBeforeTrigger?(params: ITaskBeforeTriggerParams<I>): Promise<void>;\n onDone?(params: ITaskLifecycleHook<I, O>): Promise<void>;\n onError?(params: ITaskLifecycleHook<I, O>): Promise<void>;\n onAbort?(params: ITaskLifecycleHook<I, O>): Promise<void>;\n onMaxIterations?(params: ITaskLifecycleHook<I, O>): Promise<void>;\n /**\n * Create a validation schema for the task input.\n * This will be used to validate the input before the task is triggered.\n */\n createInputValidation?(\n params: ITaskCreateInputValidationParams\n ): GenericRecord<keyof I, zod.Schema> | zod.Schema;\n}\n\n/**\n * Core TaskDefinition - minimal interface\n *\n * TRANSITIONAL SHAPE. A definition supplies its behaviour in one of two ways:\n *\n * - the new way: `handler` names a {@link ITaskHandler} class, which the runner builds only for the\n * task it is about to run;\n * - the old way: the definition implements `run()` and the hooks itself, which forces every\n * definition (and every dependency it injects) to be built just to look one up by id.\n *\n * `run` is optional ONLY to let both shapes coexist while packages migrate. Once every definition\n * carries a `handler`, this interface becomes `ITaskMetadata & { handler }` and the optionality\n * goes away. Exactly one of `run` or `handler` must be present; `GetTaskDefinitionUseCase` fails\n * with {@link TaskDefinitionNotRunnableError} if neither is.\n */\nexport interface ITaskDefinition<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n>\n extends ITaskMetadata, Partial<ITaskHandler<I, O>> {\n handler?: Constructor<ITaskHandler<I, O>>;\n}\n\nexport interface ITaskCreateInputValidationParams {\n validator: typeof zod;\n}\n\n/** Define a long-running background task with lifecycle hooks. */\nexport const TaskDefinition = createAbstraction<ITaskDefinition>(\"TaskDefinition\");\n\n/**\n * The behaviour half of a task, resolved on demand by the runner via\n * `container.resolveImplementation(definition.handler)`. Registered implementations are NOT\n * registered against this abstraction — the definition points at the class directly, exactly as\n * `HttpRouteDefinition` points at its `HttpRouteHandler`.\n */\nexport const TaskHandler = createAbstraction<ITaskHandler>(\"TaskHandler\");\n\n/**\n * IRunnableTaskDefinition represents a TaskDefinition after decoration/processing.\n * All optional runtime properties are guaranteed to have values (with defaults applied).\n * Used internally by the task runner to ensure consistent behavior.\n */\nexport interface IRunnableTaskDefinition<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n>\n extends ITaskMetadata, ITaskHandler<I, O> {\n // Override optional properties to be required with guaranteed values\n isPrivate: boolean;\n databaseLogs: boolean;\n maxIterations: number;\n}\n\nexport namespace TaskHandler {\n export type Interface<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n > = ITaskHandler<I, O>;\n\n export type RunParams<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n > = ITaskRunParams<I, O>;\n\n export type Result<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n > = ITaskResult<I, O>;\n\n export type LifecycleHookParams<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n > = ITaskLifecycleHook<I, O>;\n}\n\nexport namespace TaskDefinition {\n export type Interface<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n > = ITaskDefinition<I, O>;\n\n export type Metadata = ITaskMetadata;\n\n export type Handler<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n > = ITaskHandler<I, O>;\n\n export type TaskInput = ITaskInput;\n\n export type TaskOutput = ITaskOutput;\n\n export type Runnable<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n > = IRunnableTaskDefinition<I, O>;\n\n export type RunParams<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n > = ITaskRunParams<I, O>;\n\n export type Result<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n > = ITaskResult<I, O>;\n\n export type Task<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n > = ITask<I, O>;\n\n export type ResultDone<O extends ITaskOutput = ITaskOutput> = ITaskResultDone<O>;\n export type ResultContinue<I = ITaskInput> = ITaskResultContinue<I>;\n export type ResultError = ITaskResultError;\n export type ResultAborted = ITaskResultAborted;\n export type CreateInputValidationParams = ITaskCreateInputValidationParams;\n export type TaskCreateData<I = ITaskInput> = ITaskCreateData<I>;\n export type BeforeTriggerParams<I = ITaskInput> = ITaskBeforeTriggerParams<I>;\n export type LifecycleHookParams<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n > = ITaskLifecycleHook<I, O>;\n\n export type Info = ITaskDefinitionInfo;\n\n export type SelfCleanupEvent = ISelfCleanupEvent;\n export type SelfCleanup = ISelfCleanup;\n}\n"],"names":["TaskResultStatus","TaskDefinition","createAbstraction","TaskHandler"],"mappings":";AA4EO,IAAKA,gCAAgBA,WAAAA,GAAAA,SAAhBA,gBAAgB;;;;;WAAhBA;;AAiIL,MAAMC,iBAAiBC,kBAAmC;AAQ1D,MAAMC,cAAcD,kBAAgC"}
1
+ {"version":3,"file":"features/task/TaskDefinition/abstractions.js","sources":["../../../../src/features/task/TaskDefinition/abstractions.ts"],"sourcesContent":["import zod from \"zod\";\nimport type { Constructor } from \"@webiny/di\";\nimport { createAbstraction } from \"@webiny/feature/api\";\nimport type { GenericRecord } from \"@webiny/api/types\";\nimport type { ITask } from \"~/features/task/TaskService/index.js\";\nimport { TaskController } from \"~/features/task/TaskController/index.js\";\n\nexport type ITaskInput = GenericRecord;\n\nexport interface IResponseError {\n message: string;\n code?: string | null;\n data?: GenericRecord | null;\n stack?: string;\n}\n\nexport interface ITaskOutput {\n error?: IResponseError;\n [key: string]: any;\n}\n\n/**\n * The task's own definition, minus its behaviour.\n *\n * Handed to `run()` and to every lifecycle hook so a decorator can tell which task it is wrapping\n * and act on the definition's own fields. A decorator is registered against the abstraction, so it\n * wraps every task; without this it has nothing to branch on but a hardcoded list of ids, which\n * means editing the decorator every time a task is added.\n *\n * This is {@link ITaskMetadata}: identity and policy, no behaviour and no handler pointer. The\n * object passed at runtime is the resolved task, so `run` is physically present; naming the metadata\n * type here stops the obvious mistake of a handler calling `params.definition.run(...)` and\n * recursing forever.\n *\n * Only the fields declared here arrive. A field a project adds to its own definition class is\n * currently dropped, because `TaskDefinitionDefaultsDecorator` is a fixed pass-through that\n * exposes a known set of getters and nothing else. Making them forward unknown\n * properties would turn a definition into a place to declare policy (a rate limit, a set of tags)\n * that a decorator acts on; `taskDefinitionInParams.test.ts` pins the current behaviour so that\n * change announces itself.\n */\nexport type ITaskDefinitionInfo = ITaskMetadata;\n\n/**\n * Task run params - the input data, plus the definition being run\n * All runtime dependencies come from TaskController (injected separately)\n */\nexport interface ITaskRunParams<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n> {\n input: I;\n controller: TaskController.Interface<I, O>;\n definition: ITaskDefinitionInfo;\n}\n\n/**\n * Data for creating a new task\n */\nexport interface ITaskCreateData<I = ITaskInput> {\n definitionId: string;\n name: string;\n input: I;\n parentId?: string;\n}\n\n/**\n * Parameters for onBeforeTrigger lifecycle hook\n */\nexport interface ITaskBeforeTriggerParams<I = ITaskInput> {\n data: ITaskCreateData<I>;\n definition: ITaskDefinitionInfo;\n}\n\n/**\n * Task result status enum\n */\nexport enum TaskResultStatus {\n DONE = \"done\",\n CONTINUE = \"continue\",\n ERROR = \"error\",\n ABORTED = \"aborted\"\n}\n\n/**\n * Specific result types\n */\nexport interface ITaskResultDone<O extends ITaskOutput = ITaskOutput> {\n status: TaskResultStatus.DONE;\n message?: string;\n output?: O;\n}\n\nexport interface ITaskResultContinue<I = ITaskInput> {\n status: TaskResultStatus.CONTINUE;\n input: I;\n wait?: number; // seconds to wait before next iteration\n}\n\nexport interface ITaskResultError {\n status: TaskResultStatus.ERROR;\n error: IResponseError;\n}\n\nexport interface ITaskResultAborted {\n status: TaskResultStatus.ABORTED;\n}\n\nexport type ITaskResult<I = ITaskInput, O extends ITaskOutput = ITaskOutput> =\n | ITaskResultDone<O>\n | ITaskResultContinue<I>\n | ITaskResultError\n | ITaskResultAborted;\n\nexport type ISelfCleanupEvent = \"onSuccess\" | \"onError\" | \"onAbort\";\n\nexport type ISelfCleanup = \"always\" | \"never\" | ISelfCleanupEvent | ISelfCleanupEvent[];\n\nexport type ITaskLifecycleHook<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n> = {\n task: ITask<I, O>;\n definition: ITaskDefinitionInfo;\n};\n\n/**\n * What a task IS: identity and runtime policy, with no behaviour and no dependencies.\n *\n * Listing tasks, resolving one by id, and applying defaults all need only this. Keeping it free of\n * dependencies is the whole point — `GetRunnableTaskDefinitionUseCase` builds every registered definition\n * to find one by id, so anything expensive here is paid 24 times per lookup.\n */\nexport interface ITaskMetadata {\n id: string;\n title: string;\n description?: string;\n maxIterations?: number;\n databaseLogs?: boolean;\n isPrivate?: boolean;\n selfCleanup?: ISelfCleanup;\n}\n\n/**\n * What a task DOES. This is the half that carries dependencies (a CMS context, an OpenSearch\n * client), so it is built only for the task actually being run.\n *\n * The lifecycle hooks live here rather than on {@link ITaskMetadata} because they need the same\n * dependencies `run()` does — see `MockDataManagerTask`, whose `onError` and `onAbort` both use the\n * OpenSearch client injected for `run()`.\n */\nexport interface ITaskHandler<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n> {\n /**\n * Core run method - receives ONLY input params\n * All runtime dependencies (logging, state management, etc.) come from TaskController\n */\n run(params: ITaskRunParams<I, O>): Promise<ITaskResult<I, O>>;\n\n /**\n * Optional lifecycle hooks - receive task data, no context.\n */\n onBeforeTrigger?(params: ITaskBeforeTriggerParams<I>): Promise<void>;\n onDone?(params: ITaskLifecycleHook<I, O>): Promise<void>;\n onError?(params: ITaskLifecycleHook<I, O>): Promise<void>;\n onAbort?(params: ITaskLifecycleHook<I, O>): Promise<void>;\n onMaxIterations?(params: ITaskLifecycleHook<I, O>): Promise<void>;\n /**\n * Create a validation schema for the task input.\n * This will be used to validate the input before the task is triggered.\n */\n createInputValidation?(\n params: ITaskCreateInputValidationParams\n ): GenericRecord<keyof I, zod.Schema> | zod.Schema;\n}\n\n/**\n * What you register: a task's {@link ITaskMetadata} plus the class that does the work.\n *\n * The definition takes no dependencies of its own, so `GetRunnableTaskDefinitionUseCase` can build every\n * registered one to find a task by id without constructing anything expensive. Only the winner's\n * `handler` gets built.\n */\nexport interface ITaskDefinition<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n> extends ITaskMetadata {\n handler: Constructor<ITaskHandler<I, O>>;\n}\n\nexport interface ITaskCreateInputValidationParams {\n validator: typeof zod;\n}\n\n/** Define a long-running background task with lifecycle hooks. */\nexport const TaskDefinition = createAbstraction<ITaskDefinition>(\"TaskDefinition\");\n\n/**\n * The behaviour half of a task, resolved on demand by the runner via\n * `container.resolveImplementation(definition.handler)`. Registered implementations are NOT\n * registered against this abstraction — the definition points at the class directly, exactly as\n * `HttpRouteDefinition` points at its `HttpRouteHandler`.\n */\nexport const TaskHandler = createAbstraction<ITaskHandler>(\"TaskHandler\");\n\n/**\n * IRunnableTaskDefinition represents a TaskDefinition after decoration/processing.\n * All optional runtime properties are guaranteed to have values (with defaults applied).\n * Used internally by the task runner to ensure consistent behavior.\n */\nexport interface IRunnableTaskDefinition<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n>\n extends ITaskMetadata, ITaskHandler<I, O> {\n // Override optional properties to be required with guaranteed values\n isPrivate: boolean;\n databaseLogs: boolean;\n maxIterations: number;\n}\n\nexport namespace TaskHandler {\n export type Interface<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n > = ITaskHandler<I, O>;\n\n export type RunParams<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n > = ITaskRunParams<I, O>;\n\n export type Result<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n > = ITaskResult<I, O>;\n\n export type LifecycleHookParams<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n > = ITaskLifecycleHook<I, O>;\n}\n\nexport namespace TaskDefinition {\n export type Interface<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n > = ITaskDefinition<I, O>;\n\n export type Metadata = ITaskMetadata;\n\n export type Handler<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n > = ITaskHandler<I, O>;\n\n export type TaskInput = ITaskInput;\n\n export type TaskOutput = ITaskOutput;\n\n export type Runnable<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n > = IRunnableTaskDefinition<I, O>;\n\n export type RunParams<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n > = ITaskRunParams<I, O>;\n\n export type Result<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n > = ITaskResult<I, O>;\n\n export type Task<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n > = ITask<I, O>;\n\n export type ResultDone<O extends ITaskOutput = ITaskOutput> = ITaskResultDone<O>;\n export type ResultContinue<I = ITaskInput> = ITaskResultContinue<I>;\n export type ResultError = ITaskResultError;\n export type ResultAborted = ITaskResultAborted;\n export type CreateInputValidationParams = ITaskCreateInputValidationParams;\n export type TaskCreateData<I = ITaskInput> = ITaskCreateData<I>;\n export type BeforeTriggerParams<I = ITaskInput> = ITaskBeforeTriggerParams<I>;\n export type LifecycleHookParams<\n I extends ITaskInput = ITaskInput,\n O extends ITaskOutput = ITaskOutput\n > = ITaskLifecycleHook<I, O>;\n\n export type Info = ITaskDefinitionInfo;\n\n export type SelfCleanupEvent = ISelfCleanupEvent;\n export type SelfCleanup = ISelfCleanup;\n}\n"],"names":["TaskResultStatus","TaskDefinition","createAbstraction","TaskHandler"],"mappings":";AA6EO,IAAKA,gCAAgBA,WAAAA,GAAAA,SAAhBA,gBAAgB;;;;;WAAhBA;;AAwHL,MAAMC,iBAAiBC,kBAAmC;AAQ1D,MAAMC,cAAcD,kBAAgC"}
@@ -1 +1 @@
1
- {"version":3,"file":"features/wcp/WcpLicenseLoader.js","sources":["../../../src/features/wcp/WcpLicenseLoader.ts"],"sourcesContent":["import { getWcpProjectLicense, NullLicense, License } from \"@webiny/wcp\";\nimport type { DecryptedWcpProjectLicense, ILicense } from \"@webiny/wcp/types.js\";\nimport type { CachedWcpProjectLicense } from \"~/features/wcp/WcpContext/types.js\";\nimport { getWcpProjectLicenseCacheKey } from \"~/features/wcp/WcpContext/utils.js\";\nimport { resolveWcpProjectEnvironment } from \"~/features/wcp/resolveWcpProjectEnvironment.js\";\n\n/**\n * Loads and process-caches the WCP project license. Static because the cache is process-global (one\n * license per deployment): the pre-register refresh in `registerApiRequestStack` calls `load()` to\n * populate it, and `WcpLicenseProvider` / register-time feature-flag gates read it synchronously via\n * `getCached()`. Not DI — it must be usable before any container exists (before register).\n */\nexport class WcpLicenseLoader {\n private static readonly wcpProjectEnvironment = resolveWcpProjectEnvironment();\n\n private static cached: CachedWcpProjectLicense = {\n cacheKey: null,\n project: null,\n license: new NullLicense()\n };\n\n // In-flight fetch, memoized so concurrent cache-miss callers coalesce onto a single WCP request\n // (self-hosted runs many requests in one process; we never want two license fetches at once).\n private static inflight: Promise<ILicense> | null = null;\n\n /**\n * Synchronously read the currently cached license. `load()` must have been awaited at least once\n * this request (the pre-register refresh does this), so this reflects the live license — letting\n * register()-time feature-flag checks read it synchronously.\n */\n static getCached(): ILicense {\n return WcpLicenseLoader.cached.license;\n }\n\n /**\n * Seed the cache with an already-resolved license (tests pass a pre-built one). Marks it fresh so\n * a subsequent `load()` returns it without a fetch. This is how `WcpLicenseProviderImpl` installs\n * an initial license.\n */\n static seed(license: ILicense): void {\n WcpLicenseLoader.cached.license = license;\n WcpLicenseLoader.cached.cacheKey = getWcpProjectLicenseCacheKey();\n }\n\n static async load(testProjectLicense?: DecryptedWcpProjectLicense): Promise<ILicense> {\n const cached = WcpLicenseLoader.cached;\n\n if (testProjectLicense) {\n cached.license = License.fromLicenseDto(testProjectLicense);\n return cached.license;\n }\n\n const env = WcpLicenseLoader.wcpProjectEnvironment;\n if (!env) {\n return cached.license;\n }\n\n const currentCacheKey = getWcpProjectLicenseCacheKey();\n if (cached.cacheKey === currentCacheKey) {\n return cached.license;\n }\n\n // Cache miss (first load or the ~5-min key rotated). Coalesce concurrent callers onto one fetch.\n if (WcpLicenseLoader.inflight) {\n return WcpLicenseLoader.inflight;\n }\n\n WcpLicenseLoader.inflight = (async () => {\n // Pull the project license from the WCP API.\n const decryptedLicenseDto = await getWcpProjectLicense({\n orgId: env.org.id,\n projectId: env.project.id,\n projectEnvironmentApiKey: env.apiKey\n });\n\n if (decryptedLicenseDto) {\n cached.project = {\n orgId: decryptedLicenseDto.orgId,\n projectId: decryptedLicenseDto.projectId,\n package: decryptedLicenseDto.package\n };\n }\n\n cached.license = License.fromLicenseDto(decryptedLicenseDto);\n // Mark fresh only after a successful fetch — a throw leaves the key unset so the next call retries.\n cached.cacheKey = currentCacheKey;\n return cached.license;\n })().finally(() => {\n WcpLicenseLoader.inflight = null;\n });\n\n return WcpLicenseLoader.inflight;\n }\n}\n"],"names":["WcpLicenseLoader","resolveWcpProjectEnvironment","NullLicense","license","getWcpProjectLicenseCacheKey","testProjectLicense","cached","License","env","currentCacheKey","decryptedLicenseDto","getWcpProjectLicense"],"mappings":";;;AAYO,MAAMA;;aACe,qBAAqB,GAAGC;;;aAEjC,MAAM,GAA4B;YAC7C,UAAU;YACV,SAAS;YACT,SAAS,IAAIC;QACjB;;;aAIe,QAAQ,GAA6B;;IAOpD,OAAO,YAAsB;QACzB,OAAOF,iBAAiB,MAAM,CAAC,OAAO;IAC1C;IAOA,OAAO,KAAKG,OAAiB,EAAQ;QACjCH,iBAAiB,MAAM,CAAC,OAAO,GAAGG;QAClCH,iBAAiB,MAAM,CAAC,QAAQ,GAAGI;IACvC;IAEA,aAAa,KAAKC,kBAA+C,EAAqB;QAClF,MAAMC,SAASN,iBAAiB,MAAM;QAEtC,IAAIK,oBAAoB;YACpBC,OAAO,OAAO,GAAGC,QAAQ,cAAc,CAACF;YACxC,OAAOC,OAAO,OAAO;QACzB;QAEA,MAAME,MAAMR,iBAAiB,qBAAqB;QAClD,IAAI,CAACQ,KACD,OAAOF,OAAO,OAAO;QAGzB,MAAMG,kBAAkBL;QACxB,IAAIE,OAAO,QAAQ,KAAKG,iBACpB,OAAOH,OAAO,OAAO;QAIzB,IAAIN,iBAAiB,QAAQ,EACzB,OAAOA,iBAAiB,QAAQ;QAGpCA,iBAAiB,QAAQ,GAAI;YAEzB,MAAMU,sBAAsB,MAAMC,qBAAqB;gBACnD,OAAOH,IAAI,GAAG,CAAC,EAAE;gBACjB,WAAWA,IAAI,OAAO,CAAC,EAAE;gBACzB,0BAA0BA,IAAI,MAAM;YACxC;YAEA,IAAIE,qBACAJ,OAAO,OAAO,GAAG;gBACb,OAAOI,oBAAoB,KAAK;gBAChC,WAAWA,oBAAoB,SAAS;gBACxC,SAASA,oBAAoB,OAAO;YACxC;YAGJJ,OAAO,OAAO,GAAGC,QAAQ,cAAc,CAACG;YAExCJ,OAAO,QAAQ,GAAGG;YAClB,OAAOH,OAAO,OAAO;QACzB,KAAK,OAAO,CAAC;YACTN,iBAAiB,QAAQ,GAAG;QAChC;QAEA,OAAOA,iBAAiB,QAAQ;IACpC;AACJ"}
1
+ {"version":3,"file":"features/wcp/WcpLicenseLoader.js","sources":["../../../src/features/wcp/WcpLicenseLoader.ts"],"sourcesContent":["import { getWcpProjectLicense, NullLicense, License } from \"@webiny/wcp\";\nimport type { DecryptedWcpProjectLicense, ILicense } from \"@webiny/wcp/types.js\";\nimport type { CachedWcpProjectLicense } from \"~/features/wcp/WcpContext/types.js\";\nimport { getWcpProjectLicenseCacheKey } from \"~/features/wcp/WcpContext/utils.js\";\nimport { resolveWcpProjectEnvironment } from \"~/features/wcp/resolveWcpProjectEnvironment.js\";\n\n/**\n * Loads and process-caches the WCP project license. Static because the cache is process-global (one\n * license per deployment): the pre-register refresh in `registerApiRequestStack` calls `load()` to\n * populate it, and `WcpLicenseProvider` / register-time feature-flag gates read it synchronously via\n * `getCached()`. Not DI — it must be usable before any container exists (before register).\n */\nexport class WcpLicenseLoader {\n private static readonly wcpProjectEnvironment = resolveWcpProjectEnvironment();\n\n private static cached: CachedWcpProjectLicense = {\n cacheKey: null,\n project: null,\n license: new NullLicense()\n };\n\n // In-flight fetch, memoized so concurrent cache-miss callers coalesce onto a single WCP request\n // (standalone runs many requests in one process; we never want two license fetches at once).\n private static inflight: Promise<ILicense> | null = null;\n\n /**\n * Synchronously read the currently cached license. `load()` must have been awaited at least once\n * this request (the pre-register refresh does this), so this reflects the live license — letting\n * register()-time feature-flag checks read it synchronously.\n */\n static getCached(): ILicense {\n return WcpLicenseLoader.cached.license;\n }\n\n /**\n * Seed the cache with an already-resolved license (tests pass a pre-built one). Marks it fresh so\n * a subsequent `load()` returns it without a fetch. This is how `WcpLicenseProviderImpl` installs\n * an initial license.\n */\n static seed(license: ILicense): void {\n WcpLicenseLoader.cached.license = license;\n WcpLicenseLoader.cached.cacheKey = getWcpProjectLicenseCacheKey();\n }\n\n static async load(testProjectLicense?: DecryptedWcpProjectLicense): Promise<ILicense> {\n const cached = WcpLicenseLoader.cached;\n\n if (testProjectLicense) {\n cached.license = License.fromLicenseDto(testProjectLicense);\n return cached.license;\n }\n\n const env = WcpLicenseLoader.wcpProjectEnvironment;\n if (!env) {\n return cached.license;\n }\n\n const currentCacheKey = getWcpProjectLicenseCacheKey();\n if (cached.cacheKey === currentCacheKey) {\n return cached.license;\n }\n\n // Cache miss (first load or the ~5-min key rotated). Coalesce concurrent callers onto one fetch.\n if (WcpLicenseLoader.inflight) {\n return WcpLicenseLoader.inflight;\n }\n\n WcpLicenseLoader.inflight = (async () => {\n // Pull the project license from the WCP API.\n const decryptedLicenseDto = await getWcpProjectLicense({\n orgId: env.org.id,\n projectId: env.project.id,\n projectEnvironmentApiKey: env.apiKey\n });\n\n if (decryptedLicenseDto) {\n cached.project = {\n orgId: decryptedLicenseDto.orgId,\n projectId: decryptedLicenseDto.projectId,\n package: decryptedLicenseDto.package\n };\n }\n\n cached.license = License.fromLicenseDto(decryptedLicenseDto);\n // Mark fresh only after a successful fetch — a throw leaves the key unset so the next call retries.\n cached.cacheKey = currentCacheKey;\n return cached.license;\n })().finally(() => {\n WcpLicenseLoader.inflight = null;\n });\n\n return WcpLicenseLoader.inflight;\n }\n}\n"],"names":["WcpLicenseLoader","resolveWcpProjectEnvironment","NullLicense","license","getWcpProjectLicenseCacheKey","testProjectLicense","cached","License","env","currentCacheKey","decryptedLicenseDto","getWcpProjectLicense"],"mappings":";;;AAYO,MAAMA;;aACe,qBAAqB,GAAGC;;;aAEjC,MAAM,GAA4B;YAC7C,UAAU;YACV,SAAS;YACT,SAAS,IAAIC;QACjB;;;aAIe,QAAQ,GAA6B;;IAOpD,OAAO,YAAsB;QACzB,OAAOF,iBAAiB,MAAM,CAAC,OAAO;IAC1C;IAOA,OAAO,KAAKG,OAAiB,EAAQ;QACjCH,iBAAiB,MAAM,CAAC,OAAO,GAAGG;QAClCH,iBAAiB,MAAM,CAAC,QAAQ,GAAGI;IACvC;IAEA,aAAa,KAAKC,kBAA+C,EAAqB;QAClF,MAAMC,SAASN,iBAAiB,MAAM;QAEtC,IAAIK,oBAAoB;YACpBC,OAAO,OAAO,GAAGC,QAAQ,cAAc,CAACF;YACxC,OAAOC,OAAO,OAAO;QACzB;QAEA,MAAME,MAAMR,iBAAiB,qBAAqB;QAClD,IAAI,CAACQ,KACD,OAAOF,OAAO,OAAO;QAGzB,MAAMG,kBAAkBL;QACxB,IAAIE,OAAO,QAAQ,KAAKG,iBACpB,OAAOH,OAAO,OAAO;QAIzB,IAAIN,iBAAiB,QAAQ,EACzB,OAAOA,iBAAiB,QAAQ;QAGpCA,iBAAiB,QAAQ,GAAI;YAEzB,MAAMU,sBAAsB,MAAMC,qBAAqB;gBACnD,OAAOH,IAAI,GAAG,CAAC,EAAE;gBACjB,WAAWA,IAAI,OAAO,CAAC,EAAE;gBACzB,0BAA0BA,IAAI,MAAM;YACxC;YAEA,IAAIE,qBACAJ,OAAO,OAAO,GAAG;gBACb,OAAOI,oBAAoB,KAAK;gBAChC,WAAWA,oBAAoB,SAAS;gBACxC,SAASA,oBAAoB,OAAO;YACxC;YAGJJ,OAAO,OAAO,GAAGC,QAAQ,cAAc,CAACG;YAExCJ,OAAO,QAAQ,GAAGG;YAClB,OAAOH,OAAO,OAAO;QACzB,KAAK,OAAO,CAAC;YACTN,iBAAiB,QAAQ,GAAG;QAChC;QAEA,OAAOA,iBAAiB,QAAQ;IACpC;AACJ"}
@@ -2,7 +2,7 @@ import type { WcpProjectEnvironment } from "@webiny/wcp/types.js";
2
2
  /**
3
3
  * Resolve the WCP project environment used to fetch the license. Prefer the CLI-computed
4
4
  * WCP_PROJECT_ENVIRONMENT blob (set by `applyWcpEnvVars` during `webiny serve` and the AWS deploy).
5
- * When it's absent — a CLI-less self-hosted deploy that boots the built handler directly (e.g.
5
+ * When it's absent — a CLI-less standalone deploy that boots the built handler directly (e.g.
6
6
  * `node start.mjs`) — derive the same org, project and api key straight from the runtime env, so a
7
7
  * fresh license can still be fetched.
8
8
  */
@@ -1 +1 @@
1
- {"version":3,"file":"features/wcp/resolveWcpProjectEnvironment.js","sources":["../../../src/features/wcp/resolveWcpProjectEnvironment.ts"],"sourcesContent":["import { getWcpProjectEnvironment } from \"@webiny/wcp\";\nimport type { WcpProjectEnvironment } from \"@webiny/wcp/types.js\";\n\n/**\n * Resolve the WCP project environment used to fetch the license. Prefer the CLI-computed\n * WCP_PROJECT_ENVIRONMENT blob (set by `applyWcpEnvVars` during `webiny serve` and the AWS deploy).\n * When it's absent — a CLI-less self-hosted deploy that boots the built handler directly (e.g.\n * `node start.mjs`) — derive the same org, project and api key straight from the runtime env, so a\n * fresh license can still be fetched.\n */\nexport function resolveWcpProjectEnvironment(): WcpProjectEnvironment | null {\n const fromBlob = getWcpProjectEnvironment();\n if (fromBlob) {\n return fromBlob;\n }\n\n const projectId = process.env.WEBINY_PROJECT_ID || process.env.WCP_PROJECT_ID;\n const apiKey =\n process.env.WEBINY_PROJECT_API_KEY || process.env.WCP_PROJECT_ENVIRONMENT_API_KEY;\n if (!projectId || !apiKey) {\n return null;\n }\n\n // WEBINY_PROJECT_ID has an \"orgId/projectId\" shape.\n const [orgId, project] = projectId.split(\"/\");\n if (!orgId || !project) {\n return null;\n }\n\n // `id` is intentionally empty. The WCP backend identifies the specific project environment\n // from the api key (sent as the request's authorization header); the license fetch only uses\n // org/project (URL path) + apiKey (auth), never the environment id. So there's nothing to fill.\n return { id: \"\", apiKey, org: { id: orgId }, project: { id: project } };\n}\n"],"names":["resolveWcpProjectEnvironment","fromBlob","getWcpProjectEnvironment","projectId","process","apiKey","orgId","project"],"mappings":";AAUO,SAASA;IACZ,MAAMC,WAAWC;IACjB,IAAID,UACA,OAAOA;IAGX,MAAME,YAAYC,QAAQ,GAAG,CAAC,iBAAiB,IAAIA,QAAQ,GAAG,CAAC,cAAc;IAC7E,MAAMC,SACFD,QAAQ,GAAG,CAAC,sBAAsB,IAAIA,QAAQ,GAAG,CAAC,+BAA+B;IACrF,IAAI,CAACD,aAAa,CAACE,QACf,OAAO;IAIX,MAAM,CAACC,OAAOC,QAAQ,GAAGJ,UAAU,KAAK,CAAC;IACzC,IAAI,CAACG,SAAS,CAACC,SACX,OAAO;IAMX,OAAO;QAAE,IAAI;QAAIF;QAAQ,KAAK;YAAE,IAAIC;QAAM;QAAG,SAAS;YAAE,IAAIC;QAAQ;IAAE;AAC1E"}
1
+ {"version":3,"file":"features/wcp/resolveWcpProjectEnvironment.js","sources":["../../../src/features/wcp/resolveWcpProjectEnvironment.ts"],"sourcesContent":["import { getWcpProjectEnvironment } from \"@webiny/wcp\";\nimport type { WcpProjectEnvironment } from \"@webiny/wcp/types.js\";\n\n/**\n * Resolve the WCP project environment used to fetch the license. Prefer the CLI-computed\n * WCP_PROJECT_ENVIRONMENT blob (set by `applyWcpEnvVars` during `webiny serve` and the AWS deploy).\n * When it's absent — a CLI-less standalone deploy that boots the built handler directly (e.g.\n * `node start.mjs`) — derive the same org, project and api key straight from the runtime env, so a\n * fresh license can still be fetched.\n */\nexport function resolveWcpProjectEnvironment(): WcpProjectEnvironment | null {\n const fromBlob = getWcpProjectEnvironment();\n if (fromBlob) {\n return fromBlob;\n }\n\n const projectId = process.env.WEBINY_PROJECT_ID || process.env.WCP_PROJECT_ID;\n const apiKey =\n process.env.WEBINY_PROJECT_API_KEY || process.env.WCP_PROJECT_ENVIRONMENT_API_KEY;\n if (!projectId || !apiKey) {\n return null;\n }\n\n // WEBINY_PROJECT_ID has an \"orgId/projectId\" shape.\n const [orgId, project] = projectId.split(\"/\");\n if (!orgId || !project) {\n return null;\n }\n\n // `id` is intentionally empty. The WCP backend identifies the specific project environment\n // from the api key (sent as the request's authorization header); the license fetch only uses\n // org/project (URL path) + apiKey (auth), never the environment id. So there's nothing to fill.\n return { id: \"\", apiKey, org: { id: orgId }, project: { id: project } };\n}\n"],"names":["resolveWcpProjectEnvironment","fromBlob","getWcpProjectEnvironment","projectId","process","apiKey","orgId","project"],"mappings":";AAUO,SAASA;IACZ,MAAMC,WAAWC;IACjB,IAAID,UACA,OAAOA;IAGX,MAAME,YAAYC,QAAQ,GAAG,CAAC,iBAAiB,IAAIA,QAAQ,GAAG,CAAC,cAAc;IAC7E,MAAMC,SACFD,QAAQ,GAAG,CAAC,sBAAsB,IAAIA,QAAQ,GAAG,CAAC,+BAA+B;IACrF,IAAI,CAACD,aAAa,CAACE,QACf,OAAO;IAIX,MAAM,CAACC,OAAOC,QAAQ,GAAGJ,UAAU,KAAK,CAAC;IACzC,IAAI,CAACG,SAAS,CAACC,SACX,OAAO;IAMX,OAAO;QAAE,IAAI;QAAIF;QAAQ,KAAK;YAAE,IAAIC;QAAM;QAAG,SAAS;YAAE,IAAIC;QAAQ;IAAE;AAC1E"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webiny/api-core",
3
- "version": "6.6.0-alpha.2",
3
+ "version": "6.6.0-alpha.4",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -16,15 +16,16 @@
16
16
  "dependencies": {
17
17
  "@ai-sdk/anthropic": "4.0.40",
18
18
  "@ai-sdk/openai": "4.0.45",
19
- "@webiny/api": "6.6.0-alpha.2",
20
- "@webiny/api-graphql": "6.6.0-alpha.2",
19
+ "@webiny/api": "6.6.0-alpha.4",
20
+ "@webiny/api-graphql": "6.6.0-alpha.4",
21
21
  "@webiny/di": "1.1.0",
22
- "@webiny/error": "6.6.0-alpha.2",
23
- "@webiny/feature": "6.6.0-alpha.2",
24
- "@webiny/feature-flags": "6.6.0-alpha.2",
25
- "@webiny/handler": "6.6.0-alpha.2",
26
- "@webiny/utils": "6.6.0-alpha.2",
27
- "@webiny/wcp": "6.6.0-alpha.2",
22
+ "@webiny/error": "6.6.0-alpha.4",
23
+ "@webiny/event-handler-core": "6.6.0-alpha.4",
24
+ "@webiny/feature": "6.6.0-alpha.4",
25
+ "@webiny/feature-flags": "6.6.0-alpha.4",
26
+ "@webiny/handler": "6.6.0-alpha.4",
27
+ "@webiny/utils": "6.6.0-alpha.4",
28
+ "@webiny/wcp": "6.6.0-alpha.4",
28
29
  "ai": "7.0.73",
29
30
  "dataloader": "2.2.3",
30
31
  "graphql": "16.14.2",
@@ -39,8 +40,7 @@
39
40
  },
40
41
  "devDependencies": {
41
42
  "@types/jsonwebtoken": "9.0.10",
42
- "@webiny/build-tools": "6.6.0-alpha.2",
43
- "@webiny/event-handler-core": "6.6.0-alpha.2",
43
+ "@webiny/build-tools": "6.6.0-alpha.4",
44
44
  "rimraf": "6.1.3",
45
45
  "typescript": "7.0.2",
46
46
  "vitest": "4.1.11"