@tapi-dev/sdk 0.1.34 → 0.1.38

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/client.d.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import type { TapiClientOptions } from "./types.js";
2
2
  export declare class HttpClient {
3
3
  private readonly baseUrl;
4
- private readonly apiKey;
4
+ private readonly authToken;
5
+ private readonly tappId;
5
6
  private readonly projectId?;
6
7
  private readonly fetchImpl;
7
8
  constructor(options: TapiClientOptions);
package/dist/client.js CHANGED
@@ -1,12 +1,28 @@
1
1
  import { TapiError } from "./errors.js";
2
2
  export class HttpClient {
3
3
  baseUrl;
4
- apiKey;
4
+ authToken;
5
+ tappId;
5
6
  projectId;
6
7
  fetchImpl;
7
8
  constructor(options) {
8
9
  this.baseUrl = options.baseUrl.replace(/\/+$/, "");
9
- this.apiKey = options.apiKey;
10
+ this.authToken = options.authToken.trim();
11
+ if (!this.authToken) {
12
+ throw new TapiError("authToken is required", {
13
+ status: 0,
14
+ code: "invalid_client_config",
15
+ details: { field: "authToken" },
16
+ });
17
+ }
18
+ this.tappId = options.tappId.trim();
19
+ if (!this.tappId) {
20
+ throw new TapiError("tappId is required", {
21
+ status: 0,
22
+ code: "invalid_client_config",
23
+ details: { field: "tappId" },
24
+ });
25
+ }
10
26
  this.projectId = options.projectId;
11
27
  this.fetchImpl = options.fetch ?? fetch;
12
28
  }
@@ -26,8 +42,9 @@ export class HttpClient {
26
42
  const response = await this.fetchImpl(`${this.baseUrl}${path}`, {
27
43
  method,
28
44
  headers: {
29
- Authorization: `Bearer ${this.apiKey}`,
45
+ Authorization: `Bearer ${this.authToken}`,
30
46
  "Content-Type": "application/json",
47
+ "X-Tapi-Tapp": this.tappId,
31
48
  ...(this.projectId ? { "X-Tapi-Project": this.projectId } : {}),
32
49
  },
33
50
  body: body === undefined ? undefined : JSON.stringify(body),
@@ -1,10 +1,11 @@
1
+ import { normalizeCloudBatchRun } from "./service-contract.js";
1
2
  export class CloudRunsResource {
2
3
  http;
3
4
  constructor(http) {
4
5
  this.http = http;
5
6
  }
6
- get(runId) {
7
- return this.http.get(`/api/sdk/v1/cloud-runs/${encodeURIComponent(runId)}`);
7
+ async get(runId) {
8
+ return normalizeCloudBatchRun(await this.http.get(`/api/sdk/v1/cloud-runs/${encodeURIComponent(runId)}`));
8
9
  }
9
10
  events(runId) {
10
11
  return this.http.get(`/api/sdk/v1/cloud-runs/${encodeURIComponent(runId)}/events`);
@@ -12,8 +13,8 @@ export class CloudRunsResource {
12
13
  results(runId) {
13
14
  return this.http.get(`/api/sdk/v1/cloud-runs/${encodeURIComponent(runId)}/results`);
14
15
  }
15
- cancel(runId) {
16
- return this.http.post(`/api/sdk/v1/cloud-runs/${encodeURIComponent(runId)}/cancel`);
16
+ async cancel(runId) {
17
+ return normalizeCloudBatchRun(await this.http.post(`/api/sdk/v1/cloud-runs/${encodeURIComponent(runId)}/cancel`));
17
18
  }
18
19
  balance(user) {
19
20
  return this.http.get(`/api/sdk/v1/billing/cloud/balance${cloudUserQuery(user)}`);
package/dist/runs.js CHANGED
@@ -1,16 +1,17 @@
1
+ import { normalizeTapiRun } from "./service-contract.js";
1
2
  export class RunsResource {
2
3
  http;
3
4
  constructor(http) {
4
5
  this.http = http;
5
6
  }
6
- get(runId) {
7
- return this.http.get(`/api/sdk/v1/runs/${encodeURIComponent(runId)}`);
7
+ async get(runId) {
8
+ return normalizeTapiRun(await this.http.get(`/api/sdk/v1/runs/${encodeURIComponent(runId)}`));
8
9
  }
9
- cancel(runId) {
10
- return this.http.post(`/api/sdk/v1/runs/${encodeURIComponent(runId)}/cancel`);
10
+ async cancel(runId) {
11
+ return normalizeTapiRun(await this.http.post(`/api/sdk/v1/runs/${encodeURIComponent(runId)}/cancel`));
11
12
  }
12
- provideInput(runId, inputKey, value) {
13
- return this.http.post(`/api/sdk/v1/runs/${encodeURIComponent(runId)}/inputs/${encodeURIComponent(inputKey)}`, { value });
13
+ async provideInput(runId, inputKey, value) {
14
+ return normalizeTapiRun(await this.http.post(`/api/sdk/v1/runs/${encodeURIComponent(runId)}/inputs/${encodeURIComponent(inputKey)}`, { value }));
14
15
  }
15
16
  async wait(runId, options = {}) {
16
17
  const intervalMs = options.intervalMs ?? 1000;
@@ -0,0 +1,6 @@
1
+ import type { CloudBatchRun, SdkCatalog, ServiceOperation, TapiDevSessionsList, TapiRun } from "./types.js";
2
+ export declare function normalizeSdkCatalog(payload: unknown): SdkCatalog;
3
+ export declare function normalizeServiceOperation(payload: unknown): ServiceOperation;
4
+ export declare function normalizeTapiRun(payload: unknown): TapiRun;
5
+ export declare function normalizeCloudBatchRun(payload: unknown): CloudBatchRun;
6
+ export declare function normalizeDevSessions(payload: TapiDevSessionsList): TapiDevSessionsList;
@@ -0,0 +1,84 @@
1
+ const RETIRED_SERVICE_NAME_FIELD = "api" + "Name";
2
+ const RETIRED_SERVICE_KEY_FIELD = "request" + "Key";
3
+ const RETIRED_CATALOG_FIELD = "ap" + "is";
4
+ export function normalizeSdkCatalog(payload) {
5
+ const record = isRecord(payload) ? payload : {};
6
+ rejectRetiredCatalogShape(record);
7
+ const currentServices = Array.isArray(record.services) ? record.services : [];
8
+ return {
9
+ services: currentServices.map(normalizeCatalogService),
10
+ };
11
+ }
12
+ export function normalizeServiceOperation(payload) {
13
+ return normalizeServiceIdentity(payload);
14
+ }
15
+ export function normalizeTapiRun(payload) {
16
+ return normalizeServiceIdentity(payload);
17
+ }
18
+ export function normalizeCloudBatchRun(payload) {
19
+ return normalizeServiceIdentity(payload);
20
+ }
21
+ export function normalizeDevSessions(payload) {
22
+ return {
23
+ ...payload,
24
+ groups: (payload.groups || []).map((group) => ({
25
+ ...group,
26
+ sessions: (group.sessions || []).map((session) => normalizeServiceIdentity(session)),
27
+ })),
28
+ };
29
+ }
30
+ function normalizeCatalogService(payload) {
31
+ const record = normalizeServiceIdentity(payload, { requireIdentity: false });
32
+ const serviceName = stringField(record.serviceName).trim();
33
+ if (!serviceName) {
34
+ throw new Error("SDK service catalog responses must use serviceName.");
35
+ }
36
+ record.serviceName = serviceName;
37
+ if (Array.isArray(record.requests)) {
38
+ record.requests = record.requests.map(normalizeCatalogRequest);
39
+ }
40
+ return record;
41
+ }
42
+ function normalizeCatalogRequest(payload) {
43
+ return normalizeServiceIdentity(payload);
44
+ }
45
+ function normalizeServiceIdentity(payload, options = {}) {
46
+ const record = isRecord(payload) ? { ...payload } : {};
47
+ rejectRetiredServiceIdentity(record);
48
+ const serviceName = stringField(record.serviceName).trim();
49
+ const serviceKey = stringField(record.serviceKey).trim();
50
+ if (serviceName) {
51
+ record.serviceName = serviceName;
52
+ }
53
+ if (serviceKey) {
54
+ record.serviceKey = serviceKey;
55
+ }
56
+ if (options.requireIdentity !== false) {
57
+ requireServiceIdentity(serviceName, serviceKey);
58
+ }
59
+ if (!stringField(record.serviceRun) && serviceName && serviceKey) {
60
+ record.serviceRun = `${serviceName}.${serviceKey}`;
61
+ }
62
+ return record;
63
+ }
64
+ function rejectRetiredServiceIdentity(record) {
65
+ if (RETIRED_SERVICE_NAME_FIELD in record || RETIRED_SERVICE_KEY_FIELD in record) {
66
+ throw new Error("SDK service responses must use serviceName/serviceKey fields.");
67
+ }
68
+ }
69
+ function rejectRetiredCatalogShape(record) {
70
+ if (RETIRED_CATALOG_FIELD in record) {
71
+ throw new Error("SDK service catalog responses must use services.");
72
+ }
73
+ }
74
+ function requireServiceIdentity(serviceName, serviceKey) {
75
+ if (!serviceName || !serviceKey) {
76
+ throw new Error("SDK service responses must use serviceName/serviceKey fields.");
77
+ }
78
+ }
79
+ function stringField(value) {
80
+ return typeof value === "string" ? value : "";
81
+ }
82
+ function isRecord(value) {
83
+ return typeof value === "object" && value !== null && !Array.isArray(value);
84
+ }
@@ -4,14 +4,15 @@ import type { TapiClientOptions } from "./types.js";
4
4
  export declare class ServicesResource {
5
5
  private readonly http;
6
6
  private readonly dev;
7
+ private readonly tappId;
7
8
  constructor(http: HttpClient, options: TapiClientOptions);
8
- run(serviceRequest: string, request?: ServiceRunRequest): Promise<TapiRun>;
9
- quoteCloud(serviceRequest: string, request?: ServiceCloudQuoteRequest): Promise<CloudRunQuote>;
10
- runCloud(serviceRequest: string, request?: ServiceRunRequest & {
9
+ run(serviceRun: string, request: ServiceRunRequest): Promise<TapiRun>;
10
+ quoteCloud(serviceRun: string, request?: ServiceCloudQuoteRequest): Promise<CloudRunQuote>;
11
+ runCloud(serviceRun: string, request: ServiceRunRequest & {
11
12
  cloud?: ServiceCloudBatchRequest["cloud"];
12
13
  user?: ServiceCloudBatchRequest["user"];
13
14
  payment?: ServiceCloudBatchRequest["payment"];
14
15
  }): Promise<CloudBatchRun>;
15
- runCloudBatch(serviceRequest: string, request: ServiceCloudBatchRequest): Promise<CloudBatchRun>;
16
- describe(serviceRequest: string): Promise<ServiceOperation>;
16
+ runCloudBatch(serviceRun: string, request: ServiceCloudBatchRequest): Promise<CloudBatchRun>;
17
+ describe(serviceRun: string): Promise<ServiceOperation>;
17
18
  }
package/dist/services.js CHANGED
@@ -1,44 +1,97 @@
1
1
  import { TapiError } from "./errors.js";
2
2
  import { attachLateInputs, serializeInputs } from "./late-input.js";
3
+ import { normalizeCloudBatchRun, normalizeServiceOperation, normalizeTapiRun, } from "./service-contract.js";
4
+ const RETIRED_RUN_BODY_SERVICE_MAP_FIELDS = [
5
+ "sitemap" + "Id",
6
+ "sitemap_id",
7
+ "serviceMapId",
8
+ "service_map_id",
9
+ ];
10
+ const RUNTIME_OWNER_ALIAS_FIELDS = ["runtimeOwner", "runtime_owner"];
11
+ const RUNTIME_SERVICE_MAP_SELECTOR_FIELDS = [
12
+ "sitemap" + "Id",
13
+ "sitemap_id",
14
+ "serviceMapId",
15
+ "service_map_id",
16
+ ];
17
+ const RUN_BODY_SERVICE_IDENTITY_FIELDS = [
18
+ "serviceName",
19
+ "serviceKey",
20
+ "serviceRun",
21
+ "service_name",
22
+ "service_key",
23
+ "service_run",
24
+ ];
25
+ const SERVICE_RUN_RUNTIME_OWNER = "service_run";
3
26
  export class ServicesResource {
4
27
  http;
5
28
  dev;
29
+ tappId;
6
30
  constructor(http, options) {
7
31
  this.http = http;
8
32
  this.dev = options.dev === true;
33
+ this.tappId = options.tappId.trim();
34
+ if (!this.tappId) {
35
+ throw new TapiError("tappId is required", {
36
+ status: 0,
37
+ code: "invalid_client_config",
38
+ details: { field: "tappId" },
39
+ });
40
+ }
9
41
  }
10
- async run(serviceRequest, request = {}) {
11
- const { serviceName, requestKey } = splitServiceRequest(serviceRequest);
42
+ async run(serviceRun, request) {
43
+ const { serviceName, serviceKey } = splitServiceRun(serviceRun);
44
+ rejectRetiredRunBodySelectors(request);
12
45
  const { bodyInputs, lateInputs } = serializeInputs(request.inputs);
46
+ const runtime = normalizeServiceRunRuntime(request.runtime);
47
+ const tappId = normalizePathSegment(this.tappId, "tappId");
48
+ const queueId = normalizePathSegment(request.queueId, "queueId");
49
+ const { queueId: _queueId, ...requestBody } = request;
13
50
  const body = {
14
- ...request,
51
+ ...requestBody,
15
52
  ...(bodyInputs === undefined ? {} : { inputs: bodyInputs }),
53
+ ...(runtime === undefined ? {} : { runtime }),
16
54
  ...(this.dev ? { dev: true } : {}),
17
55
  };
18
- const run = await this.http.post(`/api/sdk/v1/services/${encodeURIComponent(serviceName)}/requests/${encodeURIComponent(requestKey)}/runs`, body);
56
+ const run = normalizeTapiRun(await this.http.post(`/api/sdk/v1/tapps/${encodeURIComponent(tappId)}/queues/${encodeURIComponent(queueId)}/services/${encodeURIComponent(`${serviceName}.${serviceKey}`)}/runs`, body));
19
57
  if (run.id && lateInputs.length > 0) {
20
58
  attachLateInputs(lateInputs, run.id, (runId, inputKey, value) => this.http.post(`/api/sdk/v1/runs/${encodeURIComponent(runId)}/inputs/${encodeURIComponent(inputKey)}`, { value }));
21
59
  }
22
60
  return run;
23
61
  }
24
- quoteCloud(serviceRequest, request = {}) {
25
- const { serviceName, requestKey } = splitServiceRequest(serviceRequest);
26
- return this.http.post(`/api/sdk/v1/services/${encodeURIComponent(serviceName)}/requests/${encodeURIComponent(requestKey)}/cloud/quote`, request);
62
+ quoteCloud(serviceRun, request = {}) {
63
+ const { serviceName, serviceKey } = splitServiceRun(serviceRun);
64
+ rejectRetiredRunBodySelectors(request);
65
+ const runtime = normalizeServiceRunRuntime(request.runtime);
66
+ const body = {
67
+ ...request,
68
+ ...(runtime === undefined ? {} : { runtime }),
69
+ };
70
+ return this.http.post(`/api/sdk/v1/services/${encodeURIComponent(serviceName)}/service-keys/${encodeURIComponent(serviceKey)}/cloud/quote`, body);
27
71
  }
28
- runCloud(serviceRequest, request = {}) {
29
- return this.runCloudBatch(serviceRequest, {
72
+ runCloud(serviceRun, request) {
73
+ rejectRetiredRunBodySelectors(request);
74
+ return this.runCloudBatch(serviceRun, {
30
75
  inputs: [request.inputs ?? {}],
76
+ queueId: request.queueId,
31
77
  cloud: request.cloud,
32
78
  user: request.user,
79
+ runtime: request.runtime,
33
80
  payment: request.payment,
34
81
  site: typeof request.site === "string" ? request.site : undefined,
35
82
  idempotencyKey: request.idempotencyKey,
36
83
  });
37
84
  }
38
- async runCloudBatch(serviceRequest, request) {
39
- const { serviceName, requestKey } = splitServiceRequest(serviceRequest);
85
+ async runCloudBatch(serviceRun, request) {
86
+ const { serviceName, serviceKey } = splitServiceRun(serviceRun);
87
+ rejectRetiredRunBodySelectors(request);
88
+ const runtime = normalizeServiceRunRuntime(request.runtime);
89
+ const body = {
90
+ ...request,
91
+ ...(runtime === undefined ? {} : { runtime }),
92
+ };
40
93
  try {
41
- return await this.http.post(`/api/sdk/v1/services/${encodeURIComponent(serviceName)}/requests/${encodeURIComponent(requestKey)}/cloud/batch`, request);
94
+ return normalizeCloudBatchRun(await this.http.post(`/api/sdk/v1/services/${encodeURIComponent(serviceName)}/service-keys/${encodeURIComponent(serviceKey)}/cloud/batch`, body));
42
95
  }
43
96
  catch (error) {
44
97
  if (error instanceof TapiError && error.status === 402 && isRecord(error.details)) {
@@ -47,26 +100,72 @@ export class ServicesResource {
47
100
  throw error;
48
101
  }
49
102
  }
50
- describe(serviceRequest) {
51
- const { serviceName, requestKey } = splitServiceRequest(serviceRequest);
52
- return this.http.get(`/api/sdk/v1/services/${encodeURIComponent(serviceName)}/requests/${encodeURIComponent(requestKey)}`);
103
+ async describe(serviceRun) {
104
+ const { serviceName, serviceKey } = splitServiceRun(serviceRun);
105
+ const tappId = normalizePathSegment(this.tappId, "tappId");
106
+ return normalizeServiceOperation(await this.http.get(`/api/sdk/v1/tapps/${encodeURIComponent(tappId)}/services/${encodeURIComponent(`${serviceName}.${serviceKey}`)}`));
107
+ }
108
+ }
109
+ function rejectRetiredRunBodySelectors(request) {
110
+ const raw = isRecord(request) ? request : {};
111
+ for (const field of RETIRED_RUN_BODY_SERVICE_MAP_FIELDS) {
112
+ if (field in raw) {
113
+ throw new Error("service runs are addressed by serviceRun; body ServiceMap selectors are not accepted.");
114
+ }
115
+ }
116
+ for (const field of RUN_BODY_SERVICE_IDENTITY_FIELDS) {
117
+ if (field in raw) {
118
+ throw new Error("service runs are addressed by serviceRun; body service identity fields are not accepted.");
119
+ }
120
+ }
121
+ }
122
+ function normalizeServiceRunRuntime(runtime) {
123
+ if (!isRecord(runtime)) {
124
+ return undefined;
125
+ }
126
+ for (const field of RUNTIME_OWNER_ALIAS_FIELDS) {
127
+ if (field in runtime) {
128
+ throw new Error(`unsupported service run runtime field: ${field}`);
129
+ }
130
+ }
131
+ for (const field of RUNTIME_SERVICE_MAP_SELECTOR_FIELDS) {
132
+ if (field in runtime) {
133
+ throw new Error(`unsupported service run runtime field: ${field}`);
134
+ }
53
135
  }
136
+ const normalized = { ...runtime };
137
+ const owner = String(normalized.owner ?? "").trim().toLowerCase();
138
+ if (owner && owner !== SERVICE_RUN_RUNTIME_OWNER) {
139
+ throw new Error("service-run runtime owner must be service_run.");
140
+ }
141
+ normalized.owner = SERVICE_RUN_RUNTIME_OWNER;
142
+ return normalized;
54
143
  }
55
- function splitServiceRequest(value) {
144
+ function splitServiceRun(value) {
56
145
  const text = value.trim();
57
146
  const index = text.lastIndexOf(".");
58
147
  if (index <= 0 || index === text.length - 1) {
59
- throw new Error("service request must be formatted as '<serviceName>.<requestKey>'");
148
+ throw new Error("serviceRun must be formatted as '<serviceName>.<serviceKey>'");
60
149
  }
61
150
  return {
62
151
  serviceName: text.slice(0, index),
63
- requestKey: text.slice(index + 1),
152
+ serviceKey: text.slice(index + 1),
64
153
  };
65
154
  }
155
+ function normalizePathSegment(value, label) {
156
+ const text = typeof value === "string" ? value.trim() : "";
157
+ if (!text) {
158
+ throw new Error(`${label} is required.`);
159
+ }
160
+ if (text.includes("/") || text.includes("\\") || text.includes("..")) {
161
+ throw new Error(`${label} is invalid.`);
162
+ }
163
+ return text;
164
+ }
66
165
  function cloudPaymentRequiredRun(detail) {
67
166
  const run = isRecord(detail.run) ? detail.run : {};
68
167
  const quote = isRecord(detail.quote) ? detail.quote : undefined;
69
- return {
168
+ return normalizeCloudBatchRun({
70
169
  ...run,
71
170
  id: typeof run.id === "string" ? run.id : "",
72
171
  status: "payment_required",
@@ -79,7 +178,7 @@ function cloudPaymentRequiredRun(detail) {
79
178
  paymentProviderEnabled: typeof detail.paymentProviderEnabled === "boolean" ? detail.paymentProviderEnabled : undefined,
80
179
  availableBalanceCents: typeof detail.availableBalanceCents === "number" ? detail.availableBalanceCents : undefined,
81
180
  requiredBalanceCents: typeof detail.requiredBalanceCents === "number" ? detail.requiredBalanceCents : undefined,
82
- };
181
+ });
83
182
  }
84
183
  function cloudRunUser(value) {
85
184
  if (!isRecord(value) || typeof value.externalUserId !== "string") {
package/dist/sessions.js CHANGED
@@ -1,14 +1,15 @@
1
+ import { normalizeDevSessions } from "./service-contract.js";
1
2
  export class SessionsResource {
2
3
  http;
3
4
  constructor(http) {
4
5
  this.http = http;
5
6
  }
6
- list(options = {}) {
7
+ async list(options = {}) {
7
8
  const params = new URLSearchParams();
8
9
  if (options.site) {
9
10
  params.set("site", options.site);
10
11
  }
11
12
  const query = params.toString();
12
- return this.http.get(`/api/sdk/v1/dev/sessions${query ? `?${query}` : ""}`);
13
+ return normalizeDevSessions(await this.http.get(`/api/sdk/v1/dev/sessions${query ? `?${query}` : ""}`));
13
14
  }
14
15
  }
@@ -1,17 +1,17 @@
1
1
  import type { HttpClient } from "./client.js";
2
- import type { WebsiteApiTrigger, WebsiteApiTriggerCreateRequest, WebsiteApiTriggerList, WebsiteApiTriggerRunResult, WebsiteApiTriggerUpdateRequest } from "./types.js";
2
+ import type { ServiceTrigger, ServiceTriggerCreateRequest, ServiceTriggerList, ServiceTriggerRunResult, ServiceTriggerUpdateRequest } from "./types.js";
3
3
  export declare class TriggersResource {
4
4
  private readonly http;
5
5
  constructor(http: HttpClient);
6
- list(): Promise<WebsiteApiTriggerList>;
7
- create(request: WebsiteApiTriggerCreateRequest): Promise<WebsiteApiTrigger>;
8
- get(triggerId: string): Promise<WebsiteApiTrigger>;
9
- update(triggerId: string, request: WebsiteApiTriggerUpdateRequest): Promise<WebsiteApiTrigger>;
10
- enable(triggerId: string): Promise<WebsiteApiTrigger>;
11
- disable(triggerId: string): Promise<WebsiteApiTrigger>;
6
+ list(): Promise<ServiceTriggerList>;
7
+ create(request: ServiceTriggerCreateRequest): Promise<ServiceTrigger>;
8
+ get(triggerId: string): Promise<ServiceTrigger>;
9
+ update(triggerId: string, request: ServiceTriggerUpdateRequest): Promise<ServiceTrigger>;
10
+ enable(triggerId: string): Promise<ServiceTrigger>;
11
+ disable(triggerId: string): Promise<ServiceTrigger>;
12
12
  delete(triggerId: string): Promise<{
13
13
  deleted: boolean;
14
14
  id: string;
15
15
  }>;
16
- fire(triggerId: string): Promise<WebsiteApiTriggerRunResult>;
16
+ fire(triggerId: string): Promise<ServiceTriggerRunResult>;
17
17
  }
package/dist/triggers.js CHANGED
@@ -1,3 +1,18 @@
1
+ const RETIRED_TRIGGER_TARGET_FIELD = "api" + "Request";
2
+ const RETIRED_TRIGGER_BODY_SERVICE_MAP_FIELDS = [
3
+ "sitemap" + "Id",
4
+ "sitemap_id",
5
+ "serviceMapId",
6
+ "service_map_id",
7
+ ];
8
+ const RUNTIME_OWNER_ALIAS_FIELDS = ["runtimeOwner", "runtime_owner"];
9
+ const RUNTIME_SERVICE_MAP_SELECTOR_FIELDS = [
10
+ "sitemap" + "Id",
11
+ "sitemap_id",
12
+ "serviceMapId",
13
+ "service_map_id",
14
+ ];
15
+ const SERVICE_RUN_RUNTIME_OWNER = "service_run";
1
16
  export class TriggersResource {
2
17
  http;
3
18
  constructor(http) {
@@ -29,13 +44,47 @@ export class TriggersResource {
29
44
  }
30
45
  }
31
46
  function toServiceTriggerWireRequest(request) {
32
- const apiRequest = request.serviceRun ?? request.service ?? request.apiRequest;
33
- if (!apiRequest) {
47
+ const raw = request;
48
+ if (RETIRED_TRIGGER_TARGET_FIELD in raw) {
49
+ throw new Error("service trigger uses serviceRun; retired trigger target fields are not accepted.");
50
+ }
51
+ for (const field of RETIRED_TRIGGER_BODY_SERVICE_MAP_FIELDS) {
52
+ if (field in raw) {
53
+ throw new Error("service triggers are addressed by serviceRun; body ServiceMap selectors are not accepted.");
54
+ }
55
+ }
56
+ if (typeof request.serviceRun !== "string" || !request.serviceRun.trim()) {
34
57
  throw new Error("service trigger requires serviceRun.");
35
58
  }
36
- const { serviceRun, service, ...wireRequest } = request;
59
+ const runtime = normalizeServiceTriggerRuntime(request.runtime);
37
60
  return {
38
- ...wireRequest,
39
- apiRequest,
61
+ ...request,
62
+ serviceRun: request.serviceRun.trim(),
63
+ ...(runtime === undefined ? {} : { runtime }),
40
64
  };
41
65
  }
66
+ function normalizeServiceTriggerRuntime(runtime) {
67
+ if (!isRecord(runtime)) {
68
+ return undefined;
69
+ }
70
+ for (const field of RUNTIME_OWNER_ALIAS_FIELDS) {
71
+ if (field in runtime) {
72
+ throw new Error(`unsupported service trigger runtime field: ${field}`);
73
+ }
74
+ }
75
+ for (const field of RUNTIME_SERVICE_MAP_SELECTOR_FIELDS) {
76
+ if (field in runtime) {
77
+ throw new Error(`unsupported service trigger runtime field: ${field}`);
78
+ }
79
+ }
80
+ const normalized = { ...runtime };
81
+ const owner = String(normalized.owner ?? "").trim().toLowerCase();
82
+ if (owner && owner !== SERVICE_RUN_RUNTIME_OWNER) {
83
+ throw new Error("service trigger runtime owner must be service_run.");
84
+ }
85
+ normalized.owner = SERVICE_RUN_RUNTIME_OWNER;
86
+ return normalized;
87
+ }
88
+ function isRecord(value) {
89
+ return typeof value === "object" && value !== null && !Array.isArray(value);
90
+ }