@webpieces/cloudtasks-client 0.4.893 → 0.4.895

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.
Files changed (41) hide show
  1. package/README.md +74 -13
  2. package/package.json +10 -5
  3. package/src/ClientCloudTasksFactory.d.ts +1 -1
  4. package/src/ClientCloudTasksFactory.js +1 -1
  5. package/src/ClientCloudTasksFactory.js.map +1 -1
  6. package/src/CloudTaskScheduler.d.ts +37 -13
  7. package/src/CloudTaskScheduler.js +38 -14
  8. package/src/CloudTaskScheduler.js.map +1 -1
  9. package/src/CloudTasksClientProvider.d.ts +29 -0
  10. package/src/CloudTasksClientProvider.js +41 -0
  11. package/src/CloudTasksClientProvider.js.map +1 -0
  12. package/src/GcpTaskInvoker.d.ts +27 -4
  13. package/src/GcpTaskInvoker.js +61 -19
  14. package/src/GcpTaskInvoker.js.map +1 -1
  15. package/src/GcpTaskRegistry.d.ts +31 -0
  16. package/src/GcpTaskRegistry.js +79 -0
  17. package/src/GcpTaskRegistry.js.map +1 -0
  18. package/src/GrpcErrorMapper.d.ts +27 -0
  19. package/src/GrpcErrorMapper.js +52 -0
  20. package/src/GrpcErrorMapper.js.map +1 -0
  21. package/src/InMemoryTaskInvoker.d.ts +9 -1
  22. package/src/InMemoryTaskInvoker.js +16 -3
  23. package/src/InMemoryTaskInvoker.js.map +1 -1
  24. package/src/TaskAlreadyExistsError.d.ts +18 -0
  25. package/src/TaskAlreadyExistsError.js +26 -0
  26. package/src/TaskAlreadyExistsError.js.map +1 -0
  27. package/src/TaskName.d.ts +33 -0
  28. package/src/TaskName.js +71 -0
  29. package/src/TaskName.js.map +1 -0
  30. package/src/TaskProxyClient.d.ts +32 -1
  31. package/src/TaskProxyClient.js +77 -27
  32. package/src/TaskProxyClient.js.map +1 -1
  33. package/src/TaskTypes.d.ts +110 -23
  34. package/src/TaskTypes.js +72 -22
  35. package/src/TaskTypes.js.map +1 -1
  36. package/src/__tests__/FakeCloudTasksServer.d.ts +51 -0
  37. package/src/__tests__/FakeCloudTasksServer.js +121 -0
  38. package/src/__tests__/FakeCloudTasksServer.js.map +1 -0
  39. package/src/index.d.ts +7 -1
  40. package/src/index.js +13 -1
  41. package/src/index.js.map +1 -1
package/src/TaskTypes.js CHANGED
@@ -1,59 +1,104 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.TaskInvoker = exports.TaskRequest = exports.JobReference = exports.ScheduleInfo = void 0;
3
+ exports.TaskInvoker = exports.TaskRequest = exports.EnqueuePlan = exports.JobReference = exports.ScheduleInfo = void 0;
4
4
  /**
5
5
  * Scheduling options for an enqueued task. Travels out-of-band (via the scheduler
6
6
  * frame in RequestContext) so the shared API method signature stays `foo(req)` on
7
7
  * both the client and the controller.
8
8
  */
9
9
  class ScheduleInfo {
10
- /** Absolute epoch-millis to run at; omitted = run as soon as possible. */
10
+ taskName;
11
11
  epochMsToRunAt;
12
- /** Per-task dispatch deadline in seconds. */
13
12
  taskTimeoutSeconds;
14
- /**
15
- * Deterministic dedup name → the Cloud Task resource name. A second enqueue with
16
- * the same name is rejected ALREADY_EXISTS (treated as success = idempotent).
17
- */
18
- dedupName;
19
- constructor(epochMsToRunAt, taskTimeoutSeconds, dedupName) {
13
+ customizeTask;
14
+ gax;
15
+ constructor(
16
+ /** How the task is named; resolved ONCE per logical enqueue by `TaskProxyClient`. */
17
+ taskName,
18
+ /** Absolute epoch-millis to run at; omitted = run as soon as possible. */
19
+ epochMsToRunAt,
20
+ /** Per-task dispatch deadline in seconds (`task.dispatchDeadline`). */
21
+ taskTimeoutSeconds,
22
+ /** The call site's {@link TaskCustomizer}; wins over `GcpTaskRegistry.setCustomizer`. */
23
+ customizeTask,
24
+ /** The call site's gax `CallOptions`; set ⇒ the caller owns timing for this enqueue. */
25
+ gax) {
26
+ this.taskName = taskName;
20
27
  this.epochMsToRunAt = epochMsToRunAt;
21
28
  this.taskTimeoutSeconds = taskTimeoutSeconds;
22
- this.dedupName = dedupName;
29
+ this.customizeTask = customizeTask;
30
+ this.gax = gax;
23
31
  }
24
32
  }
25
33
  exports.ScheduleInfo = ScheduleInfo;
26
- /** Handle to an enqueued task (its Cloud Tasks id), returned by the scheduler. */
34
+ /** Handle to an enqueued task (its Cloud Tasks name), returned by the scheduler. */
27
35
  class JobReference {
28
36
  taskId;
29
- constructor(taskId) {
37
+ alreadyExisted;
38
+ constructor(
39
+ /** The task's full resource name (`projects/.../tasks/<id>`), or the in-memory task id. */
40
+ taskId,
41
+ /**
42
+ * True when the task already existed (ALREADY_EXISTS): a retry whose earlier attempt landed,
43
+ * or a `TaskName.dedup` collision. Reported as success, never as an error.
44
+ */
45
+ alreadyExisted) {
30
46
  this.taskId = taskId;
47
+ this.alreadyExisted = alreadyExisted;
31
48
  }
32
49
  }
33
50
  exports.JobReference = JobReference;
51
+ /**
52
+ * What `TaskProxyClient` resolved ONCE for a logical enqueue, before any attempt: the task id every
53
+ * attempt carries, and the EFFECTIVE customizer and gax options (call site, else `GcpTaskRegistry`
54
+ * method -> API -> ALL).
55
+ */
56
+ class EnqueuePlan {
57
+ taskId;
58
+ customizeTask;
59
+ gax;
60
+ constructor(
61
+ /** The task id (not the full path); undefined = let Cloud Tasks assign one. */
62
+ taskId, customizeTask,
63
+ /** Set ⇒ the caller owns timing: one attempt, `createTask(request, gax)` as given. */
64
+ gax) {
65
+ this.taskId = taskId;
66
+ this.customizeTask = customizeTask;
67
+ this.gax = gax;
68
+ }
69
+ }
70
+ exports.EnqueuePlan = EnqueuePlan;
34
71
  /**
35
72
  * Everything an invoker needs to enqueue one task. Built by the enqueue proxy from
36
73
  * the shared @PubSub contract's decorators + the ambient RequestContext.
37
74
  */
38
75
  class TaskRequest {
39
- /** Callee base URL (e.g. https://email-svc-123.us-central1.run.app). */
40
76
  targetUrl;
41
- /** Endpoint path the task is delivered to (basePath + endpoint, e.g. /email/send). */
42
77
  path;
43
- /** Cloud Tasks queue name (getQueueName: @Queue override or `${Api}-${method}`). */
44
78
  queueName;
45
- /** The request DTO (serialized to the POST body on delivery). */
46
- // webpieces-disable no-any-unknown -- request DTO type is erased at the task boundary
47
79
  body;
48
- /** Context headers to propagate (txId/requestId/tenant…), already resolved. */
49
80
  contextHeaders;
50
- /** The endpoint's auth mode — how the invoker authenticates delivery. */
51
81
  authMode;
52
- /** Scheduling options (dedup name, run-at, timeout). */
53
82
  scheduleInfo;
54
- constructor(targetUrl, path, queueName,
83
+ plan;
84
+ constructor(
85
+ /** Callee base URL (e.g. https://email-svc-123.us-central1.run.app). */
86
+ targetUrl,
87
+ /** Endpoint path the task is delivered to (basePath + endpoint, e.g. /email/send). */
88
+ path,
89
+ /** Cloud Tasks queue name (getQueueName: @Queue override or `${Api}-${method}`). */
90
+ queueName,
91
+ /** The request DTO (serialized to the POST body on delivery). */
55
92
  // webpieces-disable no-any-unknown -- request DTO type is erased at the task boundary
56
- body, contextHeaders, authMode, scheduleInfo) {
93
+ body,
94
+ /** Context headers to propagate (txId/requestId/tenant…), already resolved. */
95
+ contextHeaders,
96
+ /** The endpoint's auth mode — how the invoker authenticates delivery. */
97
+ authMode,
98
+ /** Scheduling options (run-at, dispatch deadline). */
99
+ scheduleInfo,
100
+ /** The resolved name, customizer and gax for this logical enqueue. */
101
+ plan) {
57
102
  this.targetUrl = targetUrl;
58
103
  this.path = path;
59
104
  this.queueName = queueName;
@@ -61,6 +106,7 @@ class TaskRequest {
61
106
  this.contextHeaders = contextHeaders;
62
107
  this.authMode = authMode;
63
108
  this.scheduleInfo = scheduleInfo;
109
+ this.plan = plan;
64
110
  }
65
111
  }
66
112
  exports.TaskRequest = TaskRequest;
@@ -70,6 +116,10 @@ exports.TaskRequest = TaskRequest;
70
116
  * InMemoryTaskInvoker in tests/local dev. Both deliver a task the same way — a plain
71
117
  * HTTP POST to `targetUrl + path` — GcpTaskInvoker via Google Cloud Tasks, and
72
118
  * InMemoryTaskInvoker via an in-process queue that fetches the target directly.
119
+ *
120
+ * An invoker performs ONE attempt and never retries: retries belong to `CallRegistry` (webpieces'
121
+ * `RetryStrategy` by default) or, when `plan.gax` is set, to gax. It throws
122
+ * `TaskAlreadyExistsError` for a name that already exists.
73
123
  */
74
124
  class TaskInvoker {
75
125
  selectAuthentication(meta) {
@@ -1 +1 @@
1
- {"version":3,"file":"TaskTypes.js","sourceRoot":"","sources":["../../../../../packages/cloud/cloudtasks-client/src/TaskTypes.ts"],"names":[],"mappings":";;;AAEA;;;;GAIG;AACH,MAAa,YAAY;IACrB,0EAA0E;IAC1E,cAAc,CAAU;IACxB,6CAA6C;IAC7C,kBAAkB,CAAU;IAC5B;;;OAGG;IACH,SAAS,CAAU;IAEnB,YAAY,cAAuB,EAAE,kBAA2B,EAAE,SAAkB;QAChF,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACrC,IAAI,CAAC,kBAAkB,GAAG,kBAAkB,CAAC;QAC7C,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;IAC/B,CAAC;CACJ;AAhBD,oCAgBC;AAED,kFAAkF;AAClF,MAAa,YAAY;IACrB,MAAM,CAAS;IAEf,YAAY,MAAc;QACtB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;IACzB,CAAC;CACJ;AAND,oCAMC;AAED;;;GAGG;AACH,MAAa,WAAW;IACpB,wEAAwE;IACxE,SAAS,CAAS;IAClB,sFAAsF;IACtF,IAAI,CAAS;IACb,oFAAoF;IACpF,SAAS,CAAS;IAClB,iEAAiE;IACjE,sFAAsF;IACtF,IAAI,CAAU;IACd,+EAA+E;IAC/E,cAAc,CAAsB;IACpC,yEAAyE;IACzE,QAAQ,CAAW;IACnB,wDAAwD;IACxD,YAAY,CAAe;IAE3B,YACI,SAAiB,EACjB,IAAY,EACZ,SAAiB;IACjB,sFAAsF;IACtF,IAAa,EACb,cAAmC,EACnC,QAAkB,EAClB,YAA0B;QAE1B,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAC;QAC3B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,cAAc,GAAG,cAAc,CAAC;QACrC,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,YAAY,GAAG,YAAY,CAAC;IACrC,CAAC;CACJ;AAnCD,kCAmCC;AAED;;;;;;GAMG;AACH,MAAsB,WAAW;IAC7B,oBAAoB,CAAC,IAA6C;QAC9D,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAgB,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;QAC7G,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAC9B,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,2EAA2E,CAAC,CAAC;IACjG,CAAC;CAGJ;AATD,kCASC","sourcesContent":["import { AuthMode } from '@webpieces/core-util';\n\n/**\n * Scheduling options for an enqueued task. Travels out-of-band (via the scheduler\n * frame in RequestContext) so the shared API method signature stays `foo(req)` on\n * both the client and the controller.\n */\nexport class ScheduleInfo {\n /** Absolute epoch-millis to run at; omitted = run as soon as possible. */\n epochMsToRunAt?: number;\n /** Per-task dispatch deadline in seconds. */\n taskTimeoutSeconds?: number;\n /**\n * Deterministic dedup name → the Cloud Task resource name. A second enqueue with\n * the same name is rejected ALREADY_EXISTS (treated as success = idempotent).\n */\n dedupName?: string;\n\n constructor(epochMsToRunAt?: number, taskTimeoutSeconds?: number, dedupName?: string) {\n this.epochMsToRunAt = epochMsToRunAt;\n this.taskTimeoutSeconds = taskTimeoutSeconds;\n this.dedupName = dedupName;\n }\n}\n\n/** Handle to an enqueued task (its Cloud Tasks id), returned by the scheduler. */\nexport class JobReference {\n taskId: string;\n\n constructor(taskId: string) {\n this.taskId = taskId;\n }\n}\n\n/**\n * Everything an invoker needs to enqueue one task. Built by the enqueue proxy from\n * the shared @PubSub contract's decorators + the ambient RequestContext.\n */\nexport class TaskRequest {\n /** Callee base URL (e.g. https://email-svc-123.us-central1.run.app). */\n targetUrl: string;\n /** Endpoint path the task is delivered to (basePath + endpoint, e.g. /email/send). */\n path: string;\n /** Cloud Tasks queue name (getQueueName: @Queue override or `${Api}-${method}`). */\n queueName: string;\n /** The request DTO (serialized to the POST body on delivery). */\n // webpieces-disable no-any-unknown -- request DTO type is erased at the task boundary\n body: unknown;\n /** Context headers to propagate (txId/requestId/tenant…), already resolved. */\n contextHeaders: Map<string, string>;\n /** The endpoint's auth mode — how the invoker authenticates delivery. */\n authMode: AuthMode;\n /** Scheduling options (dedup name, run-at, timeout). */\n scheduleInfo: ScheduleInfo;\n\n constructor(\n targetUrl: string,\n path: string,\n queueName: string,\n // webpieces-disable no-any-unknown -- request DTO type is erased at the task boundary\n body: unknown,\n contextHeaders: Map<string, string>,\n authMode: AuthMode,\n scheduleInfo: ScheduleInfo,\n ) {\n this.targetUrl = targetUrl;\n this.path = path;\n this.queueName = queueName;\n this.body = body;\n this.contextHeaders = contextHeaders;\n this.authMode = authMode;\n this.scheduleInfo = scheduleInfo;\n }\n}\n\n/**\n * The transport that actually enqueues a task. Abstract class so it doubles as the\n * inversify DI token (inject by type, no Symbol). Bind GcpTaskInvoker in prod or\n * InMemoryTaskInvoker in tests/local dev. Both deliver a task the same way — a plain\n * HTTP POST to `targetUrl + path` — GcpTaskInvoker via Google Cloud Tasks, and\n * InMemoryTaskInvoker via an in-process queue that fetches the target directly.\n */\nexport abstract class TaskInvoker {\n selectAuthentication(meta: import('@webpieces/core-util').AuthMeta): AuthMode {\n const selected = meta.methods.find((method: AuthMode) => method.kind === 'oidc' || method.kind === 'public');\n if (selected) return selected;\n if (meta.methods.length === 1) return meta.methods[0];\n throw new Error('No configured Cloud Tasks credential satisfies the declared alternatives.');\n }\n abstract enqueue(request: TaskRequest): Promise<JobReference>;\n abstract delete(ref: JobReference): Promise<void>;\n}\n"]}
1
+ {"version":3,"file":"TaskTypes.js","sourceRoot":"","sources":["../../../../../packages/cloud/cloudtasks-client/src/TaskTypes.ts"],"names":[],"mappings":";;;AAoBA;;;;GAIG;AACH,MAAa,YAAY;IAGR;IAEA;IAEA;IAEA;IAEA;IAVb;IACI,qFAAqF;IAC5E,QAAkB;IAC3B,0EAA0E;IACjE,cAAuB;IAChC,uEAAuE;IAC9D,kBAA2B;IACpC,yFAAyF;IAChF,aAA8B;IACvC,wFAAwF;IAC/E,GAAiB;QARjB,aAAQ,GAAR,QAAQ,CAAU;QAElB,mBAAc,GAAd,cAAc,CAAS;QAEvB,uBAAkB,GAAlB,kBAAkB,CAAS;QAE3B,kBAAa,GAAb,aAAa,CAAiB;QAE9B,QAAG,GAAH,GAAG,CAAc;IAC3B,CAAC;CACP;AAbD,oCAaC;AAED,oFAAoF;AACpF,MAAa,YAAY;IAGR;IAKA;IAPb;IACI,2FAA2F;IAClF,MAAc;IACvB;;;OAGG;IACM,cAAuB;QALvB,WAAM,GAAN,MAAM,CAAQ;QAKd,mBAAc,GAAd,cAAc,CAAS;IACjC,CAAC;CACP;AAVD,oCAUC;AAED;;;;GAIG;AACH,MAAa,WAAW;IAGP;IACA;IAEA;IALb;IACI,+EAA+E;IACtE,MAA0B,EAC1B,aAA8B;IACvC,sFAAsF;IAC7E,GAAiB;QAHjB,WAAM,GAAN,MAAM,CAAoB;QAC1B,kBAAa,GAAb,aAAa,CAAiB;QAE9B,QAAG,GAAH,GAAG,CAAc;IAC3B,CAAC;CACP;AARD,kCAQC;AAED;;;GAGG;AACH,MAAa,WAAW;IAGP;IAEA;IAEA;IAGA;IAEA;IAEA;IAEA;IAEA;IAjBb;IACI,wEAAwE;IAC/D,SAAiB;IAC1B,sFAAsF;IAC7E,IAAY;IACrB,oFAAoF;IAC3E,SAAiB;IAC1B,iEAAiE;IACjE,sFAAsF;IAC7E,IAAa;IACtB,+EAA+E;IACtE,cAAmC;IAC5C,yEAAyE;IAChE,QAAkB;IAC3B,sDAAsD;IAC7C,YAA0B;IACnC,sEAAsE;IAC7D,IAAiB;QAfjB,cAAS,GAAT,SAAS,CAAQ;QAEjB,SAAI,GAAJ,IAAI,CAAQ;QAEZ,cAAS,GAAT,SAAS,CAAQ;QAGjB,SAAI,GAAJ,IAAI,CAAS;QAEb,mBAAc,GAAd,cAAc,CAAqB;QAEnC,aAAQ,GAAR,QAAQ,CAAU;QAElB,iBAAY,GAAZ,YAAY,CAAc;QAE1B,SAAI,GAAJ,IAAI,CAAa;IAC3B,CAAC;CACP;AApBD,kCAoBC;AAED;;;;;;;;;;GAUG;AACH,MAAsB,WAAW;IAC7B,oBAAoB,CAAC,IAAc;QAC/B,MAAM,QAAQ,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,MAAgB,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,MAAM,IAAI,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC,CAAC;QAC7G,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAC9B,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QACtD,MAAM,IAAI,KAAK,CAAC,2EAA2E,CAAC,CAAC;IACjG,CAAC;CAYJ;AAlBD,kCAkBC","sourcesContent":["import { AuthMeta, AuthMode } from '@webpieces/core-util';\nimport type { protos } from '@google-cloud/tasks';\nimport type { CallOptions } from 'google-gax';\nimport { TaskName } from './TaskName';\n\n/** The Google `CreateTask` request: `{ parent, task }`, built by webpieces on every attempt. */\nexport type ICreateTaskRequest = protos.google.cloud.tasks.v2.ICreateTaskRequest;\n\n/**\n * Changes the Google request of ONE enqueue attempt. It runs on every attempt, LAST, on a freshly\n * built `ICreateTaskRequest` (url, body, headers, auth, name, scheduleTime and dispatchDeadline are\n * already set) and returns the request to send — the same object mutated, or a new one.\n *\n * It is Google-request-only and never touches timing: gax `CallOptions` are\n * `ScheduleOptions.gax` / `GcpTaskRegistry.setGax`. If it changes `task.name`, it must stay under the\n * same queue path and return the same name on every attempt, or webpieces' own retries can create\n * duplicates. The in-memory invoker has no Google request and ignores it.\n */\nexport type TaskCustomizer = (request: ICreateTaskRequest) => ICreateTaskRequest;\n\n/**\n * Scheduling options for an enqueued task. Travels out-of-band (via the scheduler\n * frame in RequestContext) so the shared API method signature stays `foo(req)` on\n * both the client and the controller.\n */\nexport class ScheduleInfo {\n constructor(\n /** How the task is named; resolved ONCE per logical enqueue by `TaskProxyClient`. */\n readonly taskName: TaskName,\n /** Absolute epoch-millis to run at; omitted = run as soon as possible. */\n readonly epochMsToRunAt?: number,\n /** Per-task dispatch deadline in seconds (`task.dispatchDeadline`). */\n readonly taskTimeoutSeconds?: number,\n /** The call site's {@link TaskCustomizer}; wins over `GcpTaskRegistry.setCustomizer`. */\n readonly customizeTask?: TaskCustomizer,\n /** The call site's gax `CallOptions`; set ⇒ the caller owns timing for this enqueue. */\n readonly gax?: CallOptions,\n ) {}\n}\n\n/** Handle to an enqueued task (its Cloud Tasks name), returned by the scheduler. */\nexport class JobReference {\n constructor(\n /** The task's full resource name (`projects/.../tasks/<id>`), or the in-memory task id. */\n readonly taskId: string,\n /**\n * True when the task already existed (ALREADY_EXISTS): a retry whose earlier attempt landed,\n * or a `TaskName.dedup` collision. Reported as success, never as an error.\n */\n readonly alreadyExisted: boolean,\n ) {}\n}\n\n/**\n * What `TaskProxyClient` resolved ONCE for a logical enqueue, before any attempt: the task id every\n * attempt carries, and the EFFECTIVE customizer and gax options (call site, else `GcpTaskRegistry`\n * method -> API -> ALL).\n */\nexport class EnqueuePlan {\n constructor(\n /** The task id (not the full path); undefined = let Cloud Tasks assign one. */\n readonly taskId: string | undefined,\n readonly customizeTask?: TaskCustomizer,\n /** Set ⇒ the caller owns timing: one attempt, `createTask(request, gax)` as given. */\n readonly gax?: CallOptions,\n ) {}\n}\n\n/**\n * Everything an invoker needs to enqueue one task. Built by the enqueue proxy from\n * the shared @PubSub contract's decorators + the ambient RequestContext.\n */\nexport class TaskRequest {\n constructor(\n /** Callee base URL (e.g. https://email-svc-123.us-central1.run.app). */\n readonly targetUrl: string,\n /** Endpoint path the task is delivered to (basePath + endpoint, e.g. /email/send). */\n readonly path: string,\n /** Cloud Tasks queue name (getQueueName: @Queue override or `${Api}-${method}`). */\n readonly queueName: string,\n /** The request DTO (serialized to the POST body on delivery). */\n // webpieces-disable no-any-unknown -- request DTO type is erased at the task boundary\n readonly body: unknown,\n /** Context headers to propagate (txId/requestId/tenant…), already resolved. */\n readonly contextHeaders: Map<string, string>,\n /** The endpoint's auth mode — how the invoker authenticates delivery. */\n readonly authMode: AuthMode,\n /** Scheduling options (run-at, dispatch deadline). */\n readonly scheduleInfo: ScheduleInfo,\n /** The resolved name, customizer and gax for this logical enqueue. */\n readonly plan: EnqueuePlan,\n ) {}\n}\n\n/**\n * The transport that actually enqueues a task. Abstract class so it doubles as the\n * inversify DI token (inject by type, no Symbol). Bind GcpTaskInvoker in prod or\n * InMemoryTaskInvoker in tests/local dev. Both deliver a task the same way — a plain\n * HTTP POST to `targetUrl + path` — GcpTaskInvoker via Google Cloud Tasks, and\n * InMemoryTaskInvoker via an in-process queue that fetches the target directly.\n *\n * An invoker performs ONE attempt and never retries: retries belong to `CallRegistry` (webpieces'\n * `RetryStrategy` by default) or, when `plan.gax` is set, to gax. It throws\n * `TaskAlreadyExistsError` for a name that already exists.\n */\nexport abstract class TaskInvoker {\n selectAuthentication(meta: AuthMeta): AuthMode {\n const selected = meta.methods.find((method: AuthMode) => method.kind === 'oidc' || method.kind === 'public');\n if (selected) return selected;\n if (meta.methods.length === 1) return meta.methods[0];\n throw new Error('No configured Cloud Tasks credential satisfies the declared alternatives.');\n }\n\n /**\n * Enqueue ONE attempt.\n *\n * @param request - the task, including the resolved {@link EnqueuePlan}\n * @param timeoutMs - this attempt's deadline (webpieces' attempt timeout, or the safety cap when\n * the caller owns timing); the GCP invoker sends it as the gRPC deadline\n * @param signal - aborted when the attempt's deadline fires\n */\n abstract enqueue(request: TaskRequest, timeoutMs: number, signal: AbortSignal): Promise<JobReference>;\n abstract delete(ref: JobReference): Promise<void>;\n}\n"]}
@@ -0,0 +1,51 @@
1
+ /** What the fake server does with ONE createTask call. */
2
+ export declare enum FakeBehavior {
3
+ /** Answer the task (named as sent, or server-assigned). */
4
+ OK = "ok",
5
+ /** UNAVAILABLE (14) — what grpc-js surfaces for a connection reset. */
6
+ RESET = "reset",
7
+ /** ALREADY_EXISTS (6). */
8
+ ALREADY_EXISTS = "already-exists",
9
+ /** Never answer: only the client's deadline can end the call. */
10
+ SILENT = "silent"
11
+ }
12
+ /** One createTask the fake server received, as it arrived on the wire. */
13
+ export declare class ReceivedCreateTask {
14
+ /** `task.name` exactly as sent; '' or undefined when the client sent none. */
15
+ readonly taskName: string | undefined;
16
+ /** Milliseconds left on the gRPC deadline when the call arrived (Infinity = none sent). */
17
+ readonly deadlineMs: number;
18
+ /** `task.dispatchDeadline.seconds`, if the request carried one. */
19
+ readonly dispatchDeadlineSeconds: string | undefined;
20
+ /** `task.httpRequest.headers`. */
21
+ readonly headers: Record<string, string>;
22
+ cancelled: boolean;
23
+ constructor(
24
+ /** `task.name` exactly as sent; '' or undefined when the client sent none. */
25
+ taskName: string | undefined,
26
+ /** Milliseconds left on the gRPC deadline when the call arrived (Infinity = none sent). */
27
+ deadlineMs: number,
28
+ /** `task.dispatchDeadline.seconds`, if the request carried one. */
29
+ dispatchDeadlineSeconds: string | undefined,
30
+ /** `task.httpRequest.headers`. */
31
+ headers: Record<string, string>);
32
+ }
33
+ /**
34
+ * A LOCAL Cloud Tasks v2 gRPC server: the real `CloudTasksClient` (and therefore real gax and real
35
+ * grpc-js) talks to it over a real socket, so what it records is what actually went on the wire —
36
+ * the task name, the gRPC deadline of EVERY attempt, the request the customizer produced.
37
+ *
38
+ * It loads the CloudTasks v2 protos shipped inside `@google-cloud/tasks` (plus gax's common protos)
39
+ * and implements only `CreateTask`; each call consumes the next {@link FakeBehavior} in `script`.
40
+ */
41
+ export declare class FakeCloudTasksServer {
42
+ readonly received: ReceivedCreateTask[];
43
+ script: FakeBehavior[];
44
+ port: number;
45
+ private readonly server;
46
+ start(): Promise<void>;
47
+ /** Every listener and every open call closed, so the suite is clean under --detectOpenHandles. */
48
+ stop(): void;
49
+ private serviceDefinition;
50
+ private createTask;
51
+ }
@@ -0,0 +1,121 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FakeCloudTasksServer = exports.ReceivedCreateTask = exports.FakeBehavior = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const path = tslib_1.__importStar(require("path"));
6
+ const module_1 = require("module");
7
+ const grpc = tslib_1.__importStar(require("@grpc/grpc-js"));
8
+ const protoLoader = tslib_1.__importStar(require("@grpc/proto-loader"));
9
+ /** What the fake server does with ONE createTask call. */
10
+ var FakeBehavior;
11
+ (function (FakeBehavior) {
12
+ /** Answer the task (named as sent, or server-assigned). */
13
+ FakeBehavior["OK"] = "ok";
14
+ /** UNAVAILABLE (14) — what grpc-js surfaces for a connection reset. */
15
+ FakeBehavior["RESET"] = "reset";
16
+ /** ALREADY_EXISTS (6). */
17
+ FakeBehavior["ALREADY_EXISTS"] = "already-exists";
18
+ /** Never answer: only the client's deadline can end the call. */
19
+ FakeBehavior["SILENT"] = "silent";
20
+ })(FakeBehavior || (exports.FakeBehavior = FakeBehavior = {}));
21
+ /** One createTask the fake server received, as it arrived on the wire. */
22
+ class ReceivedCreateTask {
23
+ taskName;
24
+ deadlineMs;
25
+ dispatchDeadlineSeconds;
26
+ headers;
27
+ cancelled = false;
28
+ constructor(
29
+ /** `task.name` exactly as sent; '' or undefined when the client sent none. */
30
+ taskName,
31
+ /** Milliseconds left on the gRPC deadline when the call arrived (Infinity = none sent). */
32
+ deadlineMs,
33
+ /** `task.dispatchDeadline.seconds`, if the request carried one. */
34
+ dispatchDeadlineSeconds,
35
+ /** `task.httpRequest.headers`. */
36
+ headers) {
37
+ this.taskName = taskName;
38
+ this.deadlineMs = deadlineMs;
39
+ this.dispatchDeadlineSeconds = dispatchDeadlineSeconds;
40
+ this.headers = headers;
41
+ }
42
+ }
43
+ exports.ReceivedCreateTask = ReceivedCreateTask;
44
+ /* eslint-enable @typescript-eslint/no-explicit-any */
45
+ /**
46
+ * A LOCAL Cloud Tasks v2 gRPC server: the real `CloudTasksClient` (and therefore real gax and real
47
+ * grpc-js) talks to it over a real socket, so what it records is what actually went on the wire —
48
+ * the task name, the gRPC deadline of EVERY attempt, the request the customizer produced.
49
+ *
50
+ * It loads the CloudTasks v2 protos shipped inside `@google-cloud/tasks` (plus gax's common protos)
51
+ * and implements only `CreateTask`; each call consumes the next {@link FakeBehavior} in `script`.
52
+ */
53
+ class FakeCloudTasksServer {
54
+ received = [];
55
+ script = [];
56
+ port = 0;
57
+ server = new grpc.Server();
58
+ async start() {
59
+ const localRequire = (0, module_1.createRequire)(__filename);
60
+ // .../@google-cloud/tasks/build/cjs/src/index.cjs -> .../build/protos
61
+ const tasksProtos = path.resolve(path.dirname(localRequire.resolve('@google-cloud/tasks')), '../../protos');
62
+ // .../google-gax/build/src/index.js -> .../build/protos (google/api, google/rpc, ...)
63
+ const gaxProtos = path.resolve(path.dirname(localRequire.resolve('google-gax')), '../protos');
64
+ const definition = protoLoader.loadSync('google/cloud/tasks/v2/cloudtasks.proto', {
65
+ includeDirs: [tasksProtos, gaxProtos],
66
+ keepCase: false,
67
+ longs: String,
68
+ enums: String,
69
+ defaults: false,
70
+ oneofs: true,
71
+ });
72
+ const loaded = grpc.loadPackageDefinition(definition);
73
+ const service = this.serviceDefinition(loaded);
74
+ this.server.addService(service, {
75
+ createTask: (call, callback) => this.createTask(call, callback),
76
+ });
77
+ this.port = await new Promise((resolve, reject) => {
78
+ this.server.bindAsync('127.0.0.1:0', grpc.ServerCredentials.createInsecure(), (error, port) => (error ? reject(error) : resolve(port)));
79
+ });
80
+ }
81
+ /** Every listener and every open call closed, so the suite is clean under --detectOpenHandles. */
82
+ stop() {
83
+ this.server.forceShutdown();
84
+ }
85
+ serviceDefinition(loaded) {
86
+ const google = loaded['google'];
87
+ const cloud = google['cloud'];
88
+ const tasks = cloud['tasks'];
89
+ const v2 = tasks['v2'];
90
+ const client = v2['CloudTasks'];
91
+ return client.service;
92
+ }
93
+ createTask(call, callback) {
94
+ const deadline = call.getDeadline();
95
+ const deadlineMs = deadline instanceof Date ? deadline.getTime() - Date.now() : Number(deadline) - Date.now();
96
+ const task = call.request.task ?? {};
97
+ const received = new ReceivedCreateTask(task.name, Number.isFinite(deadlineMs) ? deadlineMs : Infinity, task.dispatchDeadline?.seconds, task.httpRequest?.headers ?? {});
98
+ this.received.push(received);
99
+ call.on('cancelled', () => {
100
+ received.cancelled = true;
101
+ });
102
+ const behavior = this.script.shift() ?? FakeBehavior.OK;
103
+ switch (behavior) {
104
+ case FakeBehavior.OK:
105
+ callback(null, {
106
+ name: task.name || `${call.request.parent}/tasks/server-assigned-${this.received.length}`,
107
+ });
108
+ return;
109
+ case FakeBehavior.RESET:
110
+ callback({ code: grpc.status.UNAVAILABLE, details: 'read ECONNRESET' });
111
+ return;
112
+ case FakeBehavior.ALREADY_EXISTS:
113
+ callback({ code: grpc.status.ALREADY_EXISTS, details: 'The task cannot be created because a task with this name existed too recently.' });
114
+ return;
115
+ case FakeBehavior.SILENT:
116
+ return;
117
+ }
118
+ }
119
+ }
120
+ exports.FakeCloudTasksServer = FakeCloudTasksServer;
121
+ //# sourceMappingURL=FakeCloudTasksServer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FakeCloudTasksServer.js","sourceRoot":"","sources":["../../../../../../packages/cloud/cloudtasks-client/src/__tests__/FakeCloudTasksServer.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'path';\nimport { createRequire } from 'module';\nimport * as grpc from '@grpc/grpc-js';\nimport * as protoLoader from '@grpc/proto-loader';\n\n/** What the fake server does with ONE createTask call. */\nexport enum FakeBehavior {\n /** Answer the task (named as sent, or server-assigned). */\n OK = 'ok',\n /** UNAVAILABLE (14) — what grpc-js surfaces for a connection reset. */\n RESET = 'reset',\n /** ALREADY_EXISTS (6). */\n ALREADY_EXISTS = 'already-exists',\n /** Never answer: only the client's deadline can end the call. */\n SILENT = 'silent',\n}\n\n/** One createTask the fake server received, as it arrived on the wire. */\nexport class ReceivedCreateTask {\n cancelled = false;\n\n constructor(\n /** `task.name` exactly as sent; '' or undefined when the client sent none. */\n readonly taskName: string | undefined,\n /** Milliseconds left on the gRPC deadline when the call arrived (Infinity = none sent). */\n readonly deadlineMs: number,\n /** `task.dispatchDeadline.seconds`, if the request carried one. */\n readonly dispatchDeadlineSeconds: string | undefined,\n /** `task.httpRequest.headers`. */\n readonly headers: Record<string, string>,\n ) {}\n}\n\n/* eslint-disable @typescript-eslint/no-explicit-any -- grpc-js handler payloads are untyped proto objects */\n// webpieces-disable no-any-unknown -- grpc-js unary handler payloads are untyped proto-loader objects\ntype UnaryCall = grpc.ServerUnaryCall<any, any>;\n// webpieces-disable no-any-unknown -- grpc-js unary handler payloads are untyped proto-loader objects\ntype UnaryCallback = grpc.sendUnaryData<any>;\n/* eslint-enable @typescript-eslint/no-explicit-any */\n\n/**\n * A LOCAL Cloud Tasks v2 gRPC server: the real `CloudTasksClient` (and therefore real gax and real\n * grpc-js) talks to it over a real socket, so what it records is what actually went on the wire —\n * the task name, the gRPC deadline of EVERY attempt, the request the customizer produced.\n *\n * It loads the CloudTasks v2 protos shipped inside `@google-cloud/tasks` (plus gax's common protos)\n * and implements only `CreateTask`; each call consumes the next {@link FakeBehavior} in `script`.\n */\nexport class FakeCloudTasksServer {\n readonly received: ReceivedCreateTask[] = [];\n script: FakeBehavior[] = [];\n port = 0;\n private readonly server = new grpc.Server();\n\n async start(): Promise<void> {\n const localRequire = createRequire(__filename);\n // .../@google-cloud/tasks/build/cjs/src/index.cjs -> .../build/protos\n const tasksProtos = path.resolve(path.dirname(localRequire.resolve('@google-cloud/tasks')), '../../protos');\n // .../google-gax/build/src/index.js -> .../build/protos (google/api, google/rpc, ...)\n const gaxProtos = path.resolve(path.dirname(localRequire.resolve('google-gax')), '../protos');\n const definition = protoLoader.loadSync('google/cloud/tasks/v2/cloudtasks.proto', {\n includeDirs: [tasksProtos, gaxProtos],\n keepCase: false,\n longs: String,\n enums: String,\n defaults: false,\n oneofs: true,\n });\n const loaded = grpc.loadPackageDefinition(definition);\n const service = this.serviceDefinition(loaded);\n this.server.addService(service, {\n createTask: (call: UnaryCall, callback: UnaryCallback) => this.createTask(call, callback),\n });\n this.port = await new Promise<number>(\n (resolve: (port: number) => void, reject: (error: Error) => void) => {\n this.server.bindAsync(\n '127.0.0.1:0',\n grpc.ServerCredentials.createInsecure(),\n (error: Error | null, port: number) => (error ? reject(error) : resolve(port)),\n );\n },\n );\n }\n\n /** Every listener and every open call closed, so the suite is clean under --detectOpenHandles. */\n stop(): void {\n this.server.forceShutdown();\n }\n\n private serviceDefinition(loaded: grpc.GrpcObject): grpc.ServiceDefinition {\n const google = loaded['google'] as grpc.GrpcObject;\n const cloud = google['cloud'] as grpc.GrpcObject;\n const tasks = cloud['tasks'] as grpc.GrpcObject;\n const v2 = tasks['v2'] as grpc.GrpcObject;\n const client = v2['CloudTasks'] as grpc.ServiceClientConstructor;\n return client.service;\n }\n\n private createTask(call: UnaryCall, callback: UnaryCallback): void {\n const deadline = call.getDeadline();\n const deadlineMs =\n deadline instanceof Date ? deadline.getTime() - Date.now() : Number(deadline) - Date.now();\n const task = call.request.task ?? {};\n const received = new ReceivedCreateTask(\n task.name,\n Number.isFinite(deadlineMs) ? deadlineMs : Infinity,\n task.dispatchDeadline?.seconds,\n task.httpRequest?.headers ?? {},\n );\n this.received.push(received);\n call.on('cancelled', () => {\n received.cancelled = true;\n });\n const behavior = this.script.shift() ?? FakeBehavior.OK;\n switch (behavior) {\n case FakeBehavior.OK:\n callback(null, {\n name: task.name || `${call.request.parent}/tasks/server-assigned-${this.received.length}`,\n });\n return;\n case FakeBehavior.RESET:\n callback({ code: grpc.status.UNAVAILABLE, details: 'read ECONNRESET' });\n return;\n case FakeBehavior.ALREADY_EXISTS:\n callback({ code: grpc.status.ALREADY_EXISTS, details: 'The task cannot be created because a task with this name existed too recently.' });\n return;\n case FakeBehavior.SILENT:\n return;\n }\n }\n}\n"]}
package/src/index.d.ts CHANGED
@@ -6,7 +6,12 @@
6
6
  * share ONE abstract API class; calling a method enqueues a task that is later
7
7
  * delivered to the same endpoint through the full server filter chain.
8
8
  */
9
- export { ScheduleInfo, JobReference, TaskRequest, TaskInvoker } from './TaskTypes';
9
+ export { ScheduleInfo, JobReference, TaskRequest, TaskInvoker, EnqueuePlan } from './TaskTypes';
10
+ export type { TaskCustomizer, ICreateTaskRequest } from './TaskTypes';
11
+ export { TaskName } from './TaskName';
12
+ export { TaskAlreadyExistsError } from './TaskAlreadyExistsError';
13
+ export { GcpTaskRegistry } from './GcpTaskRegistry';
14
+ export { GrpcErrorMapper } from './GrpcErrorMapper';
10
15
  export { ClientCloudTasksFactory } from './ClientCloudTasksFactory';
11
16
  export { TaskProxyClient, TASK_PROXY_CLIENT_PROVIDER } from './TaskProxyClient';
12
17
  export { TaskClientConfig } from './TaskClientConfig';
@@ -14,4 +19,5 @@ export type { ApiPrototype } from './TaskClientConfig';
14
19
  export { CloudTaskScheduler, ScheduleOptions } from './CloudTaskScheduler';
15
20
  export { InMemoryTaskInvoker } from './InMemoryTaskInvoker';
16
21
  export { GcpTaskInvoker } from './GcpTaskInvoker';
22
+ export { CloudTasksClientProvider } from './CloudTasksClientProvider';
17
23
  export { ScheduleFrame, setScheduleFrame, currentScheduleFrame, clearScheduleFrame, } from './ScheduleContext';
package/src/index.js CHANGED
@@ -8,12 +8,21 @@
8
8
  * delivered to the same endpoint through the full server filter chain.
9
9
  */
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
- exports.clearScheduleFrame = exports.currentScheduleFrame = exports.setScheduleFrame = exports.ScheduleFrame = exports.GcpTaskInvoker = exports.InMemoryTaskInvoker = exports.ScheduleOptions = exports.CloudTaskScheduler = exports.TaskClientConfig = exports.TASK_PROXY_CLIENT_PROVIDER = exports.TaskProxyClient = exports.ClientCloudTasksFactory = exports.TaskInvoker = exports.TaskRequest = exports.JobReference = exports.ScheduleInfo = void 0;
11
+ exports.clearScheduleFrame = exports.currentScheduleFrame = exports.setScheduleFrame = exports.ScheduleFrame = exports.CloudTasksClientProvider = exports.GcpTaskInvoker = exports.InMemoryTaskInvoker = exports.ScheduleOptions = exports.CloudTaskScheduler = exports.TaskClientConfig = exports.TASK_PROXY_CLIENT_PROVIDER = exports.TaskProxyClient = exports.ClientCloudTasksFactory = exports.GrpcErrorMapper = exports.GcpTaskRegistry = exports.TaskAlreadyExistsError = exports.TaskName = exports.EnqueuePlan = exports.TaskInvoker = exports.TaskRequest = exports.JobReference = exports.ScheduleInfo = void 0;
12
12
  var TaskTypes_1 = require("./TaskTypes");
13
13
  Object.defineProperty(exports, "ScheduleInfo", { enumerable: true, get: function () { return TaskTypes_1.ScheduleInfo; } });
14
14
  Object.defineProperty(exports, "JobReference", { enumerable: true, get: function () { return TaskTypes_1.JobReference; } });
15
15
  Object.defineProperty(exports, "TaskRequest", { enumerable: true, get: function () { return TaskTypes_1.TaskRequest; } });
16
16
  Object.defineProperty(exports, "TaskInvoker", { enumerable: true, get: function () { return TaskTypes_1.TaskInvoker; } });
17
+ Object.defineProperty(exports, "EnqueuePlan", { enumerable: true, get: function () { return TaskTypes_1.EnqueuePlan; } });
18
+ var TaskName_1 = require("./TaskName");
19
+ Object.defineProperty(exports, "TaskName", { enumerable: true, get: function () { return TaskName_1.TaskName; } });
20
+ var TaskAlreadyExistsError_1 = require("./TaskAlreadyExistsError");
21
+ Object.defineProperty(exports, "TaskAlreadyExistsError", { enumerable: true, get: function () { return TaskAlreadyExistsError_1.TaskAlreadyExistsError; } });
22
+ var GcpTaskRegistry_1 = require("./GcpTaskRegistry");
23
+ Object.defineProperty(exports, "GcpTaskRegistry", { enumerable: true, get: function () { return GcpTaskRegistry_1.GcpTaskRegistry; } });
24
+ var GrpcErrorMapper_1 = require("./GrpcErrorMapper");
25
+ Object.defineProperty(exports, "GrpcErrorMapper", { enumerable: true, get: function () { return GrpcErrorMapper_1.GrpcErrorMapper; } });
17
26
  var ClientCloudTasksFactory_1 = require("./ClientCloudTasksFactory");
18
27
  Object.defineProperty(exports, "ClientCloudTasksFactory", { enumerable: true, get: function () { return ClientCloudTasksFactory_1.ClientCloudTasksFactory; } });
19
28
  var TaskProxyClient_1 = require("./TaskProxyClient");
@@ -29,6 +38,9 @@ var InMemoryTaskInvoker_1 = require("./InMemoryTaskInvoker");
29
38
  Object.defineProperty(exports, "InMemoryTaskInvoker", { enumerable: true, get: function () { return InMemoryTaskInvoker_1.InMemoryTaskInvoker; } });
30
39
  var GcpTaskInvoker_1 = require("./GcpTaskInvoker");
31
40
  Object.defineProperty(exports, "GcpTaskInvoker", { enumerable: true, get: function () { return GcpTaskInvoker_1.GcpTaskInvoker; } });
41
+ // Process-wide CloudTasksClient settings (keepalive, endpoint): override this provider at startup.
42
+ var CloudTasksClientProvider_1 = require("./CloudTasksClientProvider");
43
+ Object.defineProperty(exports, "CloudTasksClientProvider", { enumerable: true, get: function () { return CloudTasksClientProvider_1.CloudTasksClientProvider; } });
32
44
  // NOTE: server-side delivery-auth is enforced by the framework AuthFilter (AuthMode-driven)
33
45
  // in @webpieces/http-routing — a client library has no server filters / routing machinery.
34
46
  var ScheduleContext_1 = require("./ScheduleContext");
package/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/cloud/cloudtasks-client/src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAEH,yCAAmF;AAA1E,yGAAA,YAAY,OAAA;AAAE,yGAAA,YAAY,OAAA;AAAE,wGAAA,WAAW,OAAA;AAAE,wGAAA,WAAW,OAAA;AAC7D,qEAAoE;AAA3D,kIAAA,uBAAuB,OAAA;AAChC,qDAAgF;AAAvE,kHAAA,eAAe,OAAA;AAAE,6HAAA,0BAA0B,OAAA;AACpD,uDAAsD;AAA7C,oHAAA,gBAAgB,OAAA;AAEzB,2DAA2E;AAAlE,wHAAA,kBAAkB,OAAA;AAAE,qHAAA,eAAe,OAAA;AAC5C,2FAA2F;AAC3F,6DAA4D;AAAnD,0HAAA,mBAAmB,OAAA;AAC5B,mDAAkD;AAAzC,gHAAA,cAAc,OAAA;AACvB,4FAA4F;AAC5F,2FAA2F;AAC3F,qDAK2B;AAJvB,gHAAA,aAAa,OAAA;AACb,mHAAA,gBAAgB,OAAA;AAChB,uHAAA,oBAAoB,OAAA;AACpB,qHAAA,kBAAkB,OAAA","sourcesContent":["/**\n * @webpieces/cloudtasks-client\n *\n * Cloud Tasks enqueue client generated from a shared @PubSub API contract — the\n * fire-and-forget twin of @webpieces/http-client. The client and the controller\n * share ONE abstract API class; calling a method enqueues a task that is later\n * delivered to the same endpoint through the full server filter chain.\n */\n\nexport { ScheduleInfo, JobReference, TaskRequest, TaskInvoker } from './TaskTypes';\nexport { ClientCloudTasksFactory } from './ClientCloudTasksFactory';\nexport { TaskProxyClient, TASK_PROXY_CLIENT_PROVIDER } from './TaskProxyClient';\nexport { TaskClientConfig } from './TaskClientConfig';\nexport type { ApiPrototype } from './TaskClientConfig';\nexport { CloudTaskScheduler, ScheduleOptions } from './CloudTaskScheduler';\n// The two task transports (local HTTP-queue + remote GCP), both delivering over real HTTP.\nexport { InMemoryTaskInvoker } from './InMemoryTaskInvoker';\nexport { GcpTaskInvoker } from './GcpTaskInvoker';\n// NOTE: server-side delivery-auth is enforced by the framework AuthFilter (AuthMode-driven)\n// in @webpieces/http-routing — a client library has no server filters / routing machinery.\nexport {\n ScheduleFrame,\n setScheduleFrame,\n currentScheduleFrame,\n clearScheduleFrame,\n} from './ScheduleContext';\n\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/cloud/cloudtasks-client/src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;;AAEH,yCAAgG;AAAvF,yGAAA,YAAY,OAAA;AAAE,yGAAA,YAAY,OAAA;AAAE,wGAAA,WAAW,OAAA;AAAE,wGAAA,WAAW,OAAA;AAAE,wGAAA,WAAW,OAAA;AAE1E,uCAAsC;AAA7B,oGAAA,QAAQ,OAAA;AACjB,mEAAkE;AAAzD,gIAAA,sBAAsB,OAAA;AAC/B,qDAAoD;AAA3C,kHAAA,eAAe,OAAA;AACxB,qDAAoD;AAA3C,kHAAA,eAAe,OAAA;AACxB,qEAAoE;AAA3D,kIAAA,uBAAuB,OAAA;AAChC,qDAAgF;AAAvE,kHAAA,eAAe,OAAA;AAAE,6HAAA,0BAA0B,OAAA;AACpD,uDAAsD;AAA7C,oHAAA,gBAAgB,OAAA;AAEzB,2DAA2E;AAAlE,wHAAA,kBAAkB,OAAA;AAAE,qHAAA,eAAe,OAAA;AAC5C,2FAA2F;AAC3F,6DAA4D;AAAnD,0HAAA,mBAAmB,OAAA;AAC5B,mDAAkD;AAAzC,gHAAA,cAAc,OAAA;AACvB,mGAAmG;AACnG,uEAAsE;AAA7D,oIAAA,wBAAwB,OAAA;AACjC,4FAA4F;AAC5F,2FAA2F;AAC3F,qDAK2B;AAJvB,gHAAA,aAAa,OAAA;AACb,mHAAA,gBAAgB,OAAA;AAChB,uHAAA,oBAAoB,OAAA;AACpB,qHAAA,kBAAkB,OAAA","sourcesContent":["/**\n * @webpieces/cloudtasks-client\n *\n * Cloud Tasks enqueue client generated from a shared @PubSub API contract — the\n * fire-and-forget twin of @webpieces/http-client. The client and the controller\n * share ONE abstract API class; calling a method enqueues a task that is later\n * delivered to the same endpoint through the full server filter chain.\n */\n\nexport { ScheduleInfo, JobReference, TaskRequest, TaskInvoker, EnqueuePlan } from './TaskTypes';\nexport type { TaskCustomizer, ICreateTaskRequest } from './TaskTypes';\nexport { TaskName } from './TaskName';\nexport { TaskAlreadyExistsError } from './TaskAlreadyExistsError';\nexport { GcpTaskRegistry } from './GcpTaskRegistry';\nexport { GrpcErrorMapper } from './GrpcErrorMapper';\nexport { ClientCloudTasksFactory } from './ClientCloudTasksFactory';\nexport { TaskProxyClient, TASK_PROXY_CLIENT_PROVIDER } from './TaskProxyClient';\nexport { TaskClientConfig } from './TaskClientConfig';\nexport type { ApiPrototype } from './TaskClientConfig';\nexport { CloudTaskScheduler, ScheduleOptions } from './CloudTaskScheduler';\n// The two task transports (local HTTP-queue + remote GCP), both delivering over real HTTP.\nexport { InMemoryTaskInvoker } from './InMemoryTaskInvoker';\nexport { GcpTaskInvoker } from './GcpTaskInvoker';\n// Process-wide CloudTasksClient settings (keepalive, endpoint): override this provider at startup.\nexport { CloudTasksClientProvider } from './CloudTasksClientProvider';\n// NOTE: server-side delivery-auth is enforced by the framework AuthFilter (AuthMode-driven)\n// in @webpieces/http-routing — a client library has no server filters / routing machinery.\nexport {\n ScheduleFrame,\n setScheduleFrame,\n currentScheduleFrame,\n clearScheduleFrame,\n} from './ScheduleContext';\n\n"]}