@relyio/sdk 0.2.0
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/CHANGELOG.md +13 -0
- package/LICENSE +201 -0
- package/README.md +314 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/chunk-2QUK2ZGD.js +738 -0
- package/dist/chunk-7W4MJXQ4.cjs +4429 -0
- package/dist/chunk-FJK7ZWKK.cjs +776 -0
- package/dist/chunk-HT6NFRM2.cjs +196 -0
- package/dist/chunk-I3ESNANB.js +177 -0
- package/dist/chunk-IY5LSKXU.js +3 -0
- package/dist/chunk-MWMSFIWN.cjs +17 -0
- package/dist/chunk-PPA2WWOH.cjs +4 -0
- package/dist/chunk-RJBIJ2ZK.js +4375 -0
- package/dist/chunk-XQ4QHWZE.js +13 -0
- package/dist/client/index.cjs +17 -0
- package/dist/client/index.d.cts +3 -0
- package/dist/client/index.d.ts +3 -0
- package/dist/client/index.js +4 -0
- package/dist/define-schema-DHvUCKwj.d.cts +2101 -0
- package/dist/define-schema-DHvUCKwj.d.ts +2101 -0
- package/dist/index-B-KiFHbx.d.ts +2708 -0
- package/dist/index-BsUqCAu8.d.cts +7855 -0
- package/dist/index-BsUqCAu8.d.ts +7855 -0
- package/dist/index-DGWaawHt.d.cts +2708 -0
- package/dist/index.cjs +499 -0
- package/dist/index.d.cts +523 -0
- package/dist/index.d.ts +523 -0
- package/dist/index.js +148 -0
- package/dist/react/index.cjs +4 -0
- package/dist/react/index.d.cts +2 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.js +3 -0
- package/dist/schema/index.cjs +157 -0
- package/dist/schema/index.d.cts +223 -0
- package/dist/schema/index.d.ts +223 -0
- package/dist/schema/index.js +4 -0
- package/dist/types/index.cjs +6 -0
- package/dist/types/index.d.cts +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +3 -0
- package/dist/utils/index.cjs +81 -0
- package/dist/utils/index.d.cts +87 -0
- package/dist/utils/index.d.ts +87 -0
- package/dist/utils/index.js +4 -0
- package/package.json +159 -0
|
@@ -0,0 +1,4429 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkMWMSFIWN_cjs = require('./chunk-MWMSFIWN.cjs');
|
|
4
|
+
|
|
5
|
+
/* RelyIO SDK - Built with ❤️ by the RelyIO Team */
|
|
6
|
+
|
|
7
|
+
// src/errors/sdk-error.ts
|
|
8
|
+
var _SDKError = class _SDKError extends Error {
|
|
9
|
+
constructor(message, statusCode, cause, context) {
|
|
10
|
+
super(message);
|
|
11
|
+
/** HTTP status code if applicable */
|
|
12
|
+
chunkMWMSFIWN_cjs.__publicField(this, "statusCode");
|
|
13
|
+
/** Original error that caused this error */
|
|
14
|
+
chunkMWMSFIWN_cjs.__publicField(this, "cause");
|
|
15
|
+
/** Additional context about the error */
|
|
16
|
+
chunkMWMSFIWN_cjs.__publicField(this, "context");
|
|
17
|
+
/** Timestamp when the error occurred */
|
|
18
|
+
chunkMWMSFIWN_cjs.__publicField(this, "timestamp");
|
|
19
|
+
this.name = this.constructor.name;
|
|
20
|
+
this.statusCode = statusCode;
|
|
21
|
+
this.cause = cause;
|
|
22
|
+
this.context = context;
|
|
23
|
+
this.timestamp = Date.now();
|
|
24
|
+
Object.setPrototypeOf(this, new.target.prototype);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Convert error to JSON for logging and debugging
|
|
28
|
+
*/
|
|
29
|
+
toJSON() {
|
|
30
|
+
return {
|
|
31
|
+
name: this.name,
|
|
32
|
+
message: this.message,
|
|
33
|
+
code: this.code,
|
|
34
|
+
statusCode: this.statusCode,
|
|
35
|
+
retryable: this.retryable,
|
|
36
|
+
context: this.context,
|
|
37
|
+
timestamp: this.timestamp,
|
|
38
|
+
stack: this.stack
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Get a user-friendly error message
|
|
43
|
+
*/
|
|
44
|
+
getUserMessage() {
|
|
45
|
+
return this.message;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Get detailed error information for debugging
|
|
49
|
+
*/
|
|
50
|
+
getDebugInfo() {
|
|
51
|
+
return `${this.name}: ${this.message} (${this.code})`;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Check if this error is of a specific type
|
|
55
|
+
*/
|
|
56
|
+
isType(errorType) {
|
|
57
|
+
return this.code === errorType;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Create a new error with additional context
|
|
61
|
+
*/
|
|
62
|
+
withContext(context) {
|
|
63
|
+
const Constructor = this.constructor;
|
|
64
|
+
return new Constructor(this.message, this.statusCode, this.cause, {
|
|
65
|
+
...this.context,
|
|
66
|
+
...context
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
chunkMWMSFIWN_cjs.__name(_SDKError, "SDKError");
|
|
71
|
+
var SDKError = _SDKError;
|
|
72
|
+
|
|
73
|
+
// src/errors/auth-errors.ts
|
|
74
|
+
var _AuthenticationError = class _AuthenticationError extends SDKError {
|
|
75
|
+
constructor(message = "Authentication failed", statusCode = 401, cause, context) {
|
|
76
|
+
super(message, statusCode, cause, context);
|
|
77
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code", "AUTHENTICATION_ERROR");
|
|
78
|
+
chunkMWMSFIWN_cjs.__publicField(this, "retryable", false);
|
|
79
|
+
}
|
|
80
|
+
getUserMessage() {
|
|
81
|
+
return "Authentication failed. Please check your API key and try again.";
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
chunkMWMSFIWN_cjs.__name(_AuthenticationError, "AuthenticationError");
|
|
85
|
+
var AuthenticationError = _AuthenticationError;
|
|
86
|
+
var _AuthorizationError = class _AuthorizationError extends SDKError {
|
|
87
|
+
constructor(message = "Authorization failed", statusCode = 403, cause, context) {
|
|
88
|
+
super(message, statusCode, cause, context);
|
|
89
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code", "AUTHORIZATION_ERROR");
|
|
90
|
+
chunkMWMSFIWN_cjs.__publicField(this, "retryable", false);
|
|
91
|
+
}
|
|
92
|
+
getUserMessage() {
|
|
93
|
+
return "You do not have permission to access this resource.";
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
chunkMWMSFIWN_cjs.__name(_AuthorizationError, "AuthorizationError");
|
|
97
|
+
var AuthorizationError = _AuthorizationError;
|
|
98
|
+
var _InvalidApiKeyError = class _InvalidApiKeyError extends SDKError {
|
|
99
|
+
constructor(message = "Invalid API key", cause, context) {
|
|
100
|
+
super(message, 401, cause, context);
|
|
101
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code", "INVALID_API_KEY");
|
|
102
|
+
chunkMWMSFIWN_cjs.__publicField(this, "retryable", false);
|
|
103
|
+
}
|
|
104
|
+
getUserMessage() {
|
|
105
|
+
return "Invalid API key. Please check your API key and try again.";
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
chunkMWMSFIWN_cjs.__name(_InvalidApiKeyError, "InvalidApiKeyError");
|
|
109
|
+
var InvalidApiKeyError = _InvalidApiKeyError;
|
|
110
|
+
|
|
111
|
+
// src/errors/contract-errors.ts
|
|
112
|
+
var _MissingApiKeyError = class _MissingApiKeyError extends SDKError {
|
|
113
|
+
constructor(message = "No API key was sent", context) {
|
|
114
|
+
super(message, 401, void 0, context);
|
|
115
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code", "MISSING_API_KEY");
|
|
116
|
+
chunkMWMSFIWN_cjs.__publicField(this, "retryable", false);
|
|
117
|
+
}
|
|
118
|
+
getUserMessage() {
|
|
119
|
+
return "No API key was sent. Set `apiKey` when creating the client.";
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
chunkMWMSFIWN_cjs.__name(_MissingApiKeyError, "MissingApiKeyError");
|
|
123
|
+
var MissingApiKeyError = _MissingApiKeyError;
|
|
124
|
+
var _ReadOnlyKeyError = class _ReadOnlyKeyError extends SDKError {
|
|
125
|
+
constructor(message = "This API key is read-only", context) {
|
|
126
|
+
super(message, 403, void 0, context);
|
|
127
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code", "READ_ONLY_KEY");
|
|
128
|
+
chunkMWMSFIWN_cjs.__publicField(this, "retryable", false);
|
|
129
|
+
}
|
|
130
|
+
getUserMessage() {
|
|
131
|
+
return "This API key is read-only. Use a key with write access for this operation.";
|
|
132
|
+
}
|
|
133
|
+
};
|
|
134
|
+
chunkMWMSFIWN_cjs.__name(_ReadOnlyKeyError, "ReadOnlyKeyError");
|
|
135
|
+
var ReadOnlyKeyError = _ReadOnlyKeyError;
|
|
136
|
+
var _PlanFeatureUnavailableError = class _PlanFeatureUnavailableError extends SDKError {
|
|
137
|
+
constructor(message = "This feature is not available on the current plan", feature, context) {
|
|
138
|
+
super(message, 403, void 0, context);
|
|
139
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code", "PLAN_FEATURE_UNAVAILABLE");
|
|
140
|
+
chunkMWMSFIWN_cjs.__publicField(this, "retryable", false);
|
|
141
|
+
/** The entitlement key the service named, when it named one. */
|
|
142
|
+
chunkMWMSFIWN_cjs.__publicField(this, "feature");
|
|
143
|
+
this.feature = feature;
|
|
144
|
+
}
|
|
145
|
+
getUserMessage() {
|
|
146
|
+
return this.feature === void 0 ? "This feature is not available on the current plan." : `The current plan does not include "${this.feature}".`;
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
chunkMWMSFIWN_cjs.__name(_PlanFeatureUnavailableError, "PlanFeatureUnavailableError");
|
|
150
|
+
var PlanFeatureUnavailableError = _PlanFeatureUnavailableError;
|
|
151
|
+
var _UsageQuotaExceededError = class _UsageQuotaExceededError extends SDKError {
|
|
152
|
+
constructor(message = "Usage quota exceeded", meter, limit, context) {
|
|
153
|
+
super(message, 403, void 0, context);
|
|
154
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code", "USAGE_QUOTA_EXCEEDED");
|
|
155
|
+
chunkMWMSFIWN_cjs.__publicField(this, "retryable", false);
|
|
156
|
+
/** The meter the service named. Always present on a refusal from the contract. */
|
|
157
|
+
chunkMWMSFIWN_cjs.__publicField(this, "meter");
|
|
158
|
+
/** The threshold, where the deciding seam carried one — often it does not. */
|
|
159
|
+
chunkMWMSFIWN_cjs.__publicField(this, "limit");
|
|
160
|
+
this.meter = meter;
|
|
161
|
+
this.limit = limit;
|
|
162
|
+
}
|
|
163
|
+
getUserMessage() {
|
|
164
|
+
return this.meter === void 0 ? "A usage quota for this account has been exceeded." : `The "${this.meter}" quota for this account has been exceeded.`;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
chunkMWMSFIWN_cjs.__name(_UsageQuotaExceededError, "UsageQuotaExceededError");
|
|
168
|
+
var UsageQuotaExceededError = _UsageQuotaExceededError;
|
|
169
|
+
var _RateLimitError = class _RateLimitError extends SDKError {
|
|
170
|
+
constructor(message = "Rate limit exceeded", limit, resetAt, context) {
|
|
171
|
+
super(message, 429, void 0, context);
|
|
172
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code", "RATE_LIMIT_EXCEEDED");
|
|
173
|
+
chunkMWMSFIWN_cjs.__publicField(this, "retryable", true);
|
|
174
|
+
chunkMWMSFIWN_cjs.__publicField(this, "limit");
|
|
175
|
+
chunkMWMSFIWN_cjs.__publicField(this, "resetAt");
|
|
176
|
+
this.limit = limit;
|
|
177
|
+
this.resetAt = resetAt;
|
|
178
|
+
}
|
|
179
|
+
getUserMessage() {
|
|
180
|
+
return this.resetAt === void 0 ? "Too many requests. Please retry shortly." : `Too many requests. The window resets at ${this.resetAt}.`;
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
chunkMWMSFIWN_cjs.__name(_RateLimitError, "RateLimitError");
|
|
184
|
+
var RateLimitError = _RateLimitError;
|
|
185
|
+
var _MethodNotAllowedError = class _MethodNotAllowedError extends SDKError {
|
|
186
|
+
constructor(message = "Method not allowed", context) {
|
|
187
|
+
super(message, 405, void 0, context);
|
|
188
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code", "METHOD_NOT_ALLOWED");
|
|
189
|
+
chunkMWMSFIWN_cjs.__publicField(this, "retryable", false);
|
|
190
|
+
}
|
|
191
|
+
getUserMessage() {
|
|
192
|
+
return "That operation is not available on this resource.";
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
chunkMWMSFIWN_cjs.__name(_MethodNotAllowedError, "MethodNotAllowedError");
|
|
196
|
+
var MethodNotAllowedError = _MethodNotAllowedError;
|
|
197
|
+
var _PayloadTooLargeError = class _PayloadTooLargeError extends SDKError {
|
|
198
|
+
constructor(message = "Payload too large", limitBytes, context) {
|
|
199
|
+
super(message, 413, void 0, context);
|
|
200
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code", "PAYLOAD_TOO_LARGE");
|
|
201
|
+
chunkMWMSFIWN_cjs.__publicField(this, "retryable", false);
|
|
202
|
+
chunkMWMSFIWN_cjs.__publicField(this, "limitBytes");
|
|
203
|
+
this.limitBytes = limitBytes;
|
|
204
|
+
}
|
|
205
|
+
getUserMessage() {
|
|
206
|
+
return this.limitBytes === void 0 ? "The request body is larger than this endpoint accepts." : `The request body is larger than the ${this.limitBytes}-byte limit.`;
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
chunkMWMSFIWN_cjs.__name(_PayloadTooLargeError, "PayloadTooLargeError");
|
|
210
|
+
var PayloadTooLargeError = _PayloadTooLargeError;
|
|
211
|
+
var _TransactionRolledBackError = class _TransactionRolledBackError extends SDKError {
|
|
212
|
+
constructor(message = "Transaction rolled back", failedIndex, context) {
|
|
213
|
+
super(message, 409, void 0, context);
|
|
214
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code", "TRANSACTION_ROLLED_BACK");
|
|
215
|
+
chunkMWMSFIWN_cjs.__publicField(this, "retryable", false);
|
|
216
|
+
chunkMWMSFIWN_cjs.__publicField(this, "failedIndex");
|
|
217
|
+
this.failedIndex = failedIndex;
|
|
218
|
+
}
|
|
219
|
+
getUserMessage() {
|
|
220
|
+
return this.failedIndex === void 0 ? "The batch was rolled back; nothing was written." : `The batch was rolled back at operation ${this.failedIndex}; nothing was written.`;
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
chunkMWMSFIWN_cjs.__name(_TransactionRolledBackError, "TransactionRolledBackError");
|
|
224
|
+
var TransactionRolledBackError = _TransactionRolledBackError;
|
|
225
|
+
var _ServiceIndeterminateError = class _ServiceIndeterminateError extends SDKError {
|
|
226
|
+
constructor(message = "The service could not decide this request", missing, context) {
|
|
227
|
+
super(message, 503, void 0, context);
|
|
228
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code", "SERVICE_INDETERMINATE");
|
|
229
|
+
chunkMWMSFIWN_cjs.__publicField(this, "retryable", true);
|
|
230
|
+
chunkMWMSFIWN_cjs.__publicField(this, "missing");
|
|
231
|
+
this.missing = missing;
|
|
232
|
+
}
|
|
233
|
+
getUserMessage() {
|
|
234
|
+
return "The service is temporarily unable to answer. Please retry.";
|
|
235
|
+
}
|
|
236
|
+
};
|
|
237
|
+
chunkMWMSFIWN_cjs.__name(_ServiceIndeterminateError, "ServiceIndeterminateError");
|
|
238
|
+
var ServiceIndeterminateError = _ServiceIndeterminateError;
|
|
239
|
+
var _ServerError = class _ServerError extends SDKError {
|
|
240
|
+
constructor(message = "The service failed to handle the request", statusCode = 500, context) {
|
|
241
|
+
super(message, statusCode, void 0, context);
|
|
242
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code", "INTERNAL_ERROR");
|
|
243
|
+
chunkMWMSFIWN_cjs.__publicField(this, "retryable", true);
|
|
244
|
+
}
|
|
245
|
+
getUserMessage() {
|
|
246
|
+
return "Something went wrong on the RelyIO side. Please retry.";
|
|
247
|
+
}
|
|
248
|
+
};
|
|
249
|
+
chunkMWMSFIWN_cjs.__name(_ServerError, "ServerError");
|
|
250
|
+
var ServerError = _ServerError;
|
|
251
|
+
var _UnknownContractError = class _UnknownContractError extends SDKError {
|
|
252
|
+
constructor(code, message, statusCode, context) {
|
|
253
|
+
super(message, statusCode, void 0, context);
|
|
254
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code");
|
|
255
|
+
chunkMWMSFIWN_cjs.__publicField(this, "retryable", false);
|
|
256
|
+
this.code = code;
|
|
257
|
+
}
|
|
258
|
+
getUserMessage() {
|
|
259
|
+
return `The service refused this request with "${this.code}".`;
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
chunkMWMSFIWN_cjs.__name(_UnknownContractError, "UnknownContractError");
|
|
263
|
+
var UnknownContractError = _UnknownContractError;
|
|
264
|
+
|
|
265
|
+
// src/errors/content-errors.ts
|
|
266
|
+
var _ResourceNotFoundError = class _ResourceNotFoundError extends SDKError {
|
|
267
|
+
constructor(message = "Resource not found", statusCode = 404, cause, context) {
|
|
268
|
+
super(message, statusCode, cause, context);
|
|
269
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code", "RESOURCE_NOT_FOUND");
|
|
270
|
+
chunkMWMSFIWN_cjs.__publicField(this, "retryable", false);
|
|
271
|
+
}
|
|
272
|
+
getUserMessage() {
|
|
273
|
+
const resourceType = this.context?.resourceType;
|
|
274
|
+
const resourceId = this.context?.resourceId;
|
|
275
|
+
if (resourceType != null && resourceId != null) {
|
|
276
|
+
return `${resourceType} with ID "${resourceId}" was not found.`;
|
|
277
|
+
}
|
|
278
|
+
return "The requested resource was not found.";
|
|
279
|
+
}
|
|
280
|
+
};
|
|
281
|
+
chunkMWMSFIWN_cjs.__name(_ResourceNotFoundError, "ResourceNotFoundError");
|
|
282
|
+
var ResourceNotFoundError = _ResourceNotFoundError;
|
|
283
|
+
var _ResourceConflictError = class _ResourceConflictError extends SDKError {
|
|
284
|
+
constructor(message = "Resource conflict", statusCode = 409, cause, context) {
|
|
285
|
+
super(message, statusCode, cause, context);
|
|
286
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code", "RESOURCE_CONFLICT");
|
|
287
|
+
chunkMWMSFIWN_cjs.__publicField(this, "retryable", false);
|
|
288
|
+
}
|
|
289
|
+
getUserMessage() {
|
|
290
|
+
return "The resource could not be created or updated due to a conflict.";
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
chunkMWMSFIWN_cjs.__name(_ResourceConflictError, "ResourceConflictError");
|
|
294
|
+
var ResourceConflictError = _ResourceConflictError;
|
|
295
|
+
var _ResourceExpiredError = class _ResourceExpiredError extends SDKError {
|
|
296
|
+
constructor(message = "Resource has expired", statusCode = 410, cause, context) {
|
|
297
|
+
super(message, statusCode, cause, context);
|
|
298
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code", "RESOURCE_EXPIRED");
|
|
299
|
+
chunkMWMSFIWN_cjs.__publicField(this, "retryable", false);
|
|
300
|
+
}
|
|
301
|
+
getUserMessage() {
|
|
302
|
+
return "The resource is no longer available.";
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
chunkMWMSFIWN_cjs.__name(_ResourceExpiredError, "ResourceExpiredError");
|
|
306
|
+
var ResourceExpiredError = _ResourceExpiredError;
|
|
307
|
+
function isResourceNotFound(error) {
|
|
308
|
+
return error instanceof ResourceNotFoundError;
|
|
309
|
+
}
|
|
310
|
+
chunkMWMSFIWN_cjs.__name(isResourceNotFound, "isResourceNotFound");
|
|
311
|
+
|
|
312
|
+
// src/errors/network-errors.ts
|
|
313
|
+
var _NetworkError = class _NetworkError extends SDKError {
|
|
314
|
+
constructor(message = "Network request failed", statusCode, cause, context) {
|
|
315
|
+
super(message, statusCode, cause, context);
|
|
316
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code", "NETWORK_ERROR");
|
|
317
|
+
chunkMWMSFIWN_cjs.__publicField(this, "retryable", true);
|
|
318
|
+
}
|
|
319
|
+
getUserMessage() {
|
|
320
|
+
return "Network connection failed. Please check your internet connection and try again.";
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
chunkMWMSFIWN_cjs.__name(_NetworkError, "NetworkError");
|
|
324
|
+
var NetworkError = _NetworkError;
|
|
325
|
+
var _TimeoutError = class _TimeoutError extends SDKError {
|
|
326
|
+
constructor(message = "Request timed out", timeout, statusCode, cause) {
|
|
327
|
+
super(message, statusCode, cause, { timeout });
|
|
328
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code", "TIMEOUT_ERROR");
|
|
329
|
+
chunkMWMSFIWN_cjs.__publicField(this, "retryable", true);
|
|
330
|
+
}
|
|
331
|
+
getUserMessage() {
|
|
332
|
+
const timeout = this.context?.timeout;
|
|
333
|
+
return `Request timed out${timeout != null ? ` after ${timeout}ms` : ""}. Please try again.`;
|
|
334
|
+
}
|
|
335
|
+
};
|
|
336
|
+
chunkMWMSFIWN_cjs.__name(_TimeoutError, "TimeoutError");
|
|
337
|
+
var TimeoutError = _TimeoutError;
|
|
338
|
+
var _ConnectionError = class _ConnectionError extends SDKError {
|
|
339
|
+
constructor(message = "Connection failed", statusCode, cause, context) {
|
|
340
|
+
super(message, statusCode, cause, context);
|
|
341
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code", "CONNECTION_ERROR");
|
|
342
|
+
chunkMWMSFIWN_cjs.__publicField(this, "retryable", true);
|
|
343
|
+
}
|
|
344
|
+
getUserMessage() {
|
|
345
|
+
return "Failed to connect to RelyIO servers. Please check your connection and try again.";
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
chunkMWMSFIWN_cjs.__name(_ConnectionError, "ConnectionError");
|
|
349
|
+
var ConnectionError = _ConnectionError;
|
|
350
|
+
|
|
351
|
+
// src/errors/validation-errors.ts
|
|
352
|
+
var _BaseValidationError = class _BaseValidationError extends SDKError {
|
|
353
|
+
constructor(message = "Validation failed", violations = [], statusCode = 400, cause) {
|
|
354
|
+
super(message, statusCode, cause, { violations });
|
|
355
|
+
chunkMWMSFIWN_cjs.__publicField(this, "retryable", false);
|
|
356
|
+
chunkMWMSFIWN_cjs.__publicField(this, "violations");
|
|
357
|
+
this.violations = violations;
|
|
358
|
+
}
|
|
359
|
+
getUserMessage() {
|
|
360
|
+
if (this.violations.length === 0) {
|
|
361
|
+
return "Validation failed. Please check your input and try again.";
|
|
362
|
+
}
|
|
363
|
+
if (this.violations.length === 1) {
|
|
364
|
+
const violation = this.violations[0];
|
|
365
|
+
return `Validation failed: ${violation?.message ?? "Unknown error"}`;
|
|
366
|
+
}
|
|
367
|
+
return `Validation failed with ${this.violations.length} errors. Please check your input and try again.`;
|
|
368
|
+
}
|
|
369
|
+
/**
|
|
370
|
+
* Get violations for a specific field
|
|
371
|
+
*/
|
|
372
|
+
getViolationsForField(fieldName) {
|
|
373
|
+
return this.violations.filter((v) => v.field === fieldName);
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Check if a specific field has validation errors
|
|
377
|
+
*/
|
|
378
|
+
hasFieldError(fieldName) {
|
|
379
|
+
return this.violations.some((v) => v.field === fieldName);
|
|
380
|
+
}
|
|
381
|
+
/**
|
|
382
|
+
* Get all validation messages as a formatted string
|
|
383
|
+
*/
|
|
384
|
+
getFormattedMessages() {
|
|
385
|
+
return this.violations.map((v) => `${v.field}: ${v.message}`).join("\n");
|
|
386
|
+
}
|
|
387
|
+
};
|
|
388
|
+
chunkMWMSFIWN_cjs.__name(_BaseValidationError, "BaseValidationError");
|
|
389
|
+
var BaseValidationError = _BaseValidationError;
|
|
390
|
+
var _ValidationError = class _ValidationError extends BaseValidationError {
|
|
391
|
+
constructor() {
|
|
392
|
+
super(...arguments);
|
|
393
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code", "VALIDATION_ERROR");
|
|
394
|
+
}
|
|
395
|
+
};
|
|
396
|
+
chunkMWMSFIWN_cjs.__name(_ValidationError, "ValidationError");
|
|
397
|
+
var ValidationError = _ValidationError;
|
|
398
|
+
var _InvalidPayloadError = class _InvalidPayloadError extends BaseValidationError {
|
|
399
|
+
constructor(message = "Invalid payload", violations = [], cause) {
|
|
400
|
+
super(message, violations, 400, cause);
|
|
401
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code", "INVALID_PAYLOAD");
|
|
402
|
+
}
|
|
403
|
+
getUserMessage() {
|
|
404
|
+
return "The request payload is invalid. Please check your data and try again.";
|
|
405
|
+
}
|
|
406
|
+
};
|
|
407
|
+
chunkMWMSFIWN_cjs.__name(_InvalidPayloadError, "InvalidPayloadError");
|
|
408
|
+
var InvalidPayloadError = _InvalidPayloadError;
|
|
409
|
+
var _SchemaValidationError = class _SchemaValidationError extends BaseValidationError {
|
|
410
|
+
constructor(message = "Schema validation failed", violations = [], cause) {
|
|
411
|
+
super(message, violations, 400, cause);
|
|
412
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code", "SCHEMA_VALIDATION_ERROR");
|
|
413
|
+
}
|
|
414
|
+
getUserMessage() {
|
|
415
|
+
return "The data does not match the expected schema. Please check your content structure.";
|
|
416
|
+
}
|
|
417
|
+
};
|
|
418
|
+
chunkMWMSFIWN_cjs.__name(_SchemaValidationError, "SchemaValidationError");
|
|
419
|
+
var SchemaValidationError = _SchemaValidationError;
|
|
420
|
+
var _RequiredFieldError = class _RequiredFieldError extends BaseValidationError {
|
|
421
|
+
constructor(fieldName, message, cause) {
|
|
422
|
+
const violation = {
|
|
423
|
+
field: fieldName,
|
|
424
|
+
message: message ?? `Field "${fieldName}" is required`,
|
|
425
|
+
code: "REQUIRED"
|
|
426
|
+
};
|
|
427
|
+
super(`Required field "${fieldName}" is missing`, [violation], 400, cause);
|
|
428
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code", "REQUIRED_FIELD_ERROR");
|
|
429
|
+
}
|
|
430
|
+
getUserMessage() {
|
|
431
|
+
const violation = this.violations[0];
|
|
432
|
+
const fieldName = violation?.field;
|
|
433
|
+
return `The field "${fieldName}" is required and cannot be empty.`;
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
chunkMWMSFIWN_cjs.__name(_RequiredFieldError, "RequiredFieldError");
|
|
437
|
+
var RequiredFieldError = _RequiredFieldError;
|
|
438
|
+
var _InvalidFieldTypeError = class _InvalidFieldTypeError extends BaseValidationError {
|
|
439
|
+
constructor(fieldName, expectedType, actualType, value, cause) {
|
|
440
|
+
const violation = {
|
|
441
|
+
field: fieldName,
|
|
442
|
+
message: `Expected ${expectedType}, got ${actualType}`,
|
|
443
|
+
code: "INVALID_TYPE",
|
|
444
|
+
value
|
|
445
|
+
};
|
|
446
|
+
super(
|
|
447
|
+
`Invalid type for field "${fieldName}": expected ${expectedType}, got ${actualType}`,
|
|
448
|
+
[violation],
|
|
449
|
+
400,
|
|
450
|
+
cause
|
|
451
|
+
);
|
|
452
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code", "INVALID_FIELD_TYPE");
|
|
453
|
+
}
|
|
454
|
+
getUserMessage() {
|
|
455
|
+
const violation = this.violations[0];
|
|
456
|
+
return `The field "${violation?.field}" has an invalid type. ${violation?.message}.`;
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
chunkMWMSFIWN_cjs.__name(_InvalidFieldTypeError, "InvalidFieldTypeError");
|
|
460
|
+
var InvalidFieldTypeError = _InvalidFieldTypeError;
|
|
461
|
+
|
|
462
|
+
// ../../platform/api-contract/dist/client/index.js
|
|
463
|
+
var _a;
|
|
464
|
+
var ContractBodyError = (_a = class extends Error {
|
|
465
|
+
constructor(message, body) {
|
|
466
|
+
super(message);
|
|
467
|
+
chunkMWMSFIWN_cjs.__publicField(this, "body");
|
|
468
|
+
this.name = "ContractBodyError";
|
|
469
|
+
this.body = body;
|
|
470
|
+
}
|
|
471
|
+
}, chunkMWMSFIWN_cjs.__name(_a, "ContractBodyError"), _a);
|
|
472
|
+
function describe(body) {
|
|
473
|
+
try {
|
|
474
|
+
return JSON.stringify(body)?.slice(0, 200) ?? String(body);
|
|
475
|
+
} catch {
|
|
476
|
+
return String(body);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
chunkMWMSFIWN_cjs.__name(describe, "describe");
|
|
480
|
+
function readPayload(body) {
|
|
481
|
+
if (typeof body !== "object" || body === null || !("data" in body)) {
|
|
482
|
+
throw new ContractBodyError(
|
|
483
|
+
`expected a contract success body carrying \`data\`, got ${describe(body)}`,
|
|
484
|
+
body
|
|
485
|
+
);
|
|
486
|
+
}
|
|
487
|
+
return body.data;
|
|
488
|
+
}
|
|
489
|
+
chunkMWMSFIWN_cjs.__name(readPayload, "readPayload");
|
|
490
|
+
function readError(body) {
|
|
491
|
+
if (typeof body !== "object" || body === null || !("error" in body)) return void 0;
|
|
492
|
+
const error = body.error;
|
|
493
|
+
if (typeof error !== "object" || error === null || !("code" in error)) return void 0;
|
|
494
|
+
const code = error.code;
|
|
495
|
+
if (typeof code !== "string") return void 0;
|
|
496
|
+
const messages = error.messages;
|
|
497
|
+
return {
|
|
498
|
+
code,
|
|
499
|
+
messages: Array.isArray(messages) ? messages.filter((m) => typeof m === "string") : [],
|
|
500
|
+
..."details" in error ? { details: error.details } : {}
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
chunkMWMSFIWN_cjs.__name(readError, "readError");
|
|
504
|
+
|
|
505
|
+
// src/errors/error-mapper.ts
|
|
506
|
+
var _ErrorMapper = class _ErrorMapper {
|
|
507
|
+
/** Map a non-2xx response to an SDK error. */
|
|
508
|
+
static mapHttpError(response, requestContext) {
|
|
509
|
+
const { status, statusText, body } = response;
|
|
510
|
+
const refusal = readError(body);
|
|
511
|
+
const context = { ...requestContext, response: body };
|
|
512
|
+
if (refusal === void 0) {
|
|
513
|
+
return new NetworkError(`HTTP ${status}: ${statusText}`.trim(), status, void 0, context);
|
|
514
|
+
}
|
|
515
|
+
return this.fromContractCode(refusal, status, context);
|
|
516
|
+
}
|
|
517
|
+
static fromContractCode(refusal, status, context) {
|
|
518
|
+
const message = refusal.messages[0] ?? `RelyIO refused with ${refusal.code}`;
|
|
519
|
+
switch (refusal.code) {
|
|
520
|
+
case "MISSING_API_KEY": {
|
|
521
|
+
return new MissingApiKeyError(message, context);
|
|
522
|
+
}
|
|
523
|
+
case "INVALID_API_KEY": {
|
|
524
|
+
return new InvalidApiKeyError(message, void 0, context);
|
|
525
|
+
}
|
|
526
|
+
case "ACCESS_DENIED": {
|
|
527
|
+
return new AuthorizationError(message, 403, void 0, context);
|
|
528
|
+
}
|
|
529
|
+
case "READ_ONLY_KEY": {
|
|
530
|
+
return new ReadOnlyKeyError(message, context);
|
|
531
|
+
}
|
|
532
|
+
case "PLAN_FEATURE_UNAVAILABLE": {
|
|
533
|
+
return new PlanFeatureUnavailableError(message, detailString(refusal, "feature"), context);
|
|
534
|
+
}
|
|
535
|
+
case "USAGE_QUOTA_EXCEEDED": {
|
|
536
|
+
return new UsageQuotaExceededError(
|
|
537
|
+
message,
|
|
538
|
+
detailString(refusal, "meter"),
|
|
539
|
+
detailNumber(refusal, "limit"),
|
|
540
|
+
context
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
case "RATE_LIMIT_EXCEEDED": {
|
|
544
|
+
return new RateLimitError(
|
|
545
|
+
message,
|
|
546
|
+
detailNumber(refusal, "limit"),
|
|
547
|
+
detailString(refusal, "resetAt"),
|
|
548
|
+
context
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
case "REQUEST_INVALID": {
|
|
552
|
+
return new InvalidPayloadError(message, violationsOf(refusal));
|
|
553
|
+
}
|
|
554
|
+
case "CONTENT_INVALID": {
|
|
555
|
+
return new ValidationError(message, violationsOf(refusal), 400);
|
|
556
|
+
}
|
|
557
|
+
case "METHOD_NOT_ALLOWED": {
|
|
558
|
+
return new MethodNotAllowedError(message, context);
|
|
559
|
+
}
|
|
560
|
+
case "RESOURCE_NOT_FOUND": {
|
|
561
|
+
return new ResourceNotFoundError(message, 404, void 0, {
|
|
562
|
+
...context,
|
|
563
|
+
resourceType: detailString(refusal, "kind"),
|
|
564
|
+
resourceId: detailString(refusal, "id")
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
case "RESOURCE_CONFLICT": {
|
|
568
|
+
return new ResourceConflictError(message, 409, void 0, {
|
|
569
|
+
...context,
|
|
570
|
+
expectedVersion: detailNumber(refusal, "expectedVersion"),
|
|
571
|
+
actualVersion: detailNumber(refusal, "actualVersion")
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
case "RESOURCE_EXPIRED": {
|
|
575
|
+
return new ResourceExpiredError(message, 410, void 0, context);
|
|
576
|
+
}
|
|
577
|
+
case "REQUEST_TIMEOUT": {
|
|
578
|
+
return new TimeoutError(message, void 0, void 0, void 0);
|
|
579
|
+
}
|
|
580
|
+
case "PAYLOAD_TOO_LARGE": {
|
|
581
|
+
return new PayloadTooLargeError(message, detailNumber(refusal, "limitBytes"), context);
|
|
582
|
+
}
|
|
583
|
+
case "TRANSACTION_ROLLED_BACK": {
|
|
584
|
+
return new TransactionRolledBackError(
|
|
585
|
+
message,
|
|
586
|
+
detailNumber(refusal, "failedIndex"),
|
|
587
|
+
context
|
|
588
|
+
);
|
|
589
|
+
}
|
|
590
|
+
case "SERVICE_INDETERMINATE": {
|
|
591
|
+
return new ServiceIndeterminateError(message, detailString(refusal, "missing"), context);
|
|
592
|
+
}
|
|
593
|
+
case "INTERNAL_ERROR": {
|
|
594
|
+
return new ServerError(message, status, context);
|
|
595
|
+
}
|
|
596
|
+
default: {
|
|
597
|
+
return new UnknownContractError(refusal.code, message, status, context);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
/**
|
|
602
|
+
* Map generic JavaScript errors to SDK errors
|
|
603
|
+
*/
|
|
604
|
+
static mapGenericError(error, _context) {
|
|
605
|
+
if (error instanceof SDKError) {
|
|
606
|
+
return error;
|
|
607
|
+
}
|
|
608
|
+
if (error.name === "TypeError" && error.message.includes("fetch")) {
|
|
609
|
+
return new NetworkError("Network request failed", void 0, error, _context);
|
|
610
|
+
}
|
|
611
|
+
if (error.name === "AbortError" || error.message.includes("timeout")) {
|
|
612
|
+
return new TimeoutError("Request timed out", void 0, void 0, error);
|
|
613
|
+
}
|
|
614
|
+
if (error.message.includes("Network Error") || error.message.includes("ECONNREFUSED")) {
|
|
615
|
+
return new ConnectionError("Connection failed", void 0, error, _context);
|
|
616
|
+
}
|
|
617
|
+
return new NetworkError(
|
|
618
|
+
error.message ?? "An unknown error occurred",
|
|
619
|
+
void 0,
|
|
620
|
+
error,
|
|
621
|
+
_context
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Check if an error is retryable
|
|
626
|
+
*/
|
|
627
|
+
static isRetryable(error) {
|
|
628
|
+
if (error instanceof SDKError) {
|
|
629
|
+
return error.retryable;
|
|
630
|
+
}
|
|
631
|
+
return error.name === "TypeError" && error.message.includes("fetch") || error.message.includes("timeout") || error.message.includes("Network Error") || error.message.includes("ECONNREFUSED");
|
|
632
|
+
}
|
|
633
|
+
/**
|
|
634
|
+
* Extract retry delay from error
|
|
635
|
+
*/
|
|
636
|
+
static getRetryDelay(_error) {
|
|
637
|
+
return 0;
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
chunkMWMSFIWN_cjs.__name(_ErrorMapper, "ErrorMapper");
|
|
641
|
+
var ErrorMapper = _ErrorMapper;
|
|
642
|
+
function knownCode(refusal) {
|
|
643
|
+
return typeof refusal.code === "string" ? refusal.code : void 0;
|
|
644
|
+
}
|
|
645
|
+
chunkMWMSFIWN_cjs.__name(knownCode, "knownCode");
|
|
646
|
+
function detailBag(refusal) {
|
|
647
|
+
if (knownCode(refusal) === void 0) return void 0;
|
|
648
|
+
const details = refusal.details;
|
|
649
|
+
if (typeof details !== "object" || details === null) return void 0;
|
|
650
|
+
return details;
|
|
651
|
+
}
|
|
652
|
+
chunkMWMSFIWN_cjs.__name(detailBag, "detailBag");
|
|
653
|
+
function detailString(refusal, key) {
|
|
654
|
+
const value = detailBag(refusal)?.[key];
|
|
655
|
+
return typeof value === "string" ? value : void 0;
|
|
656
|
+
}
|
|
657
|
+
chunkMWMSFIWN_cjs.__name(detailString, "detailString");
|
|
658
|
+
function detailNumber(refusal, key) {
|
|
659
|
+
const value = detailBag(refusal)?.[key];
|
|
660
|
+
return typeof value === "number" ? value : void 0;
|
|
661
|
+
}
|
|
662
|
+
chunkMWMSFIWN_cjs.__name(detailNumber, "detailNumber");
|
|
663
|
+
function violationsOf(refusal) {
|
|
664
|
+
const issues = detailBag(refusal)?.issues;
|
|
665
|
+
if (!Array.isArray(issues)) return [];
|
|
666
|
+
return issues.filter((issue) => typeof issue === "object" && issue !== null).map((issue) => ({
|
|
667
|
+
field: issue.path,
|
|
668
|
+
message: issue.message,
|
|
669
|
+
code: issue.code,
|
|
670
|
+
path: issue.path === "" ? void 0 : issue.path.split(".")
|
|
671
|
+
}));
|
|
672
|
+
}
|
|
673
|
+
chunkMWMSFIWN_cjs.__name(violationsOf, "violationsOf");
|
|
674
|
+
|
|
675
|
+
// src/http/api-routes.ts
|
|
676
|
+
function fillPath(template, params) {
|
|
677
|
+
const missing = [];
|
|
678
|
+
const filled = template.replace(/\{(\w+)\}/g, (_match, name) => {
|
|
679
|
+
const value = params[name];
|
|
680
|
+
if (value === void 0) {
|
|
681
|
+
missing.push(name);
|
|
682
|
+
return "";
|
|
683
|
+
}
|
|
684
|
+
return encodeURIComponent(value);
|
|
685
|
+
});
|
|
686
|
+
if (missing.length > 0) {
|
|
687
|
+
throw new Error(`${template} needs path parameter(s) ${missing.join(", ")}`);
|
|
688
|
+
}
|
|
689
|
+
const extra = Object.keys(params).filter((name) => !template.includes(`{${name}}`));
|
|
690
|
+
if (extra.length > 0) {
|
|
691
|
+
throw new Error(`${template} declares no path parameter(s) ${extra.join(", ")}`);
|
|
692
|
+
}
|
|
693
|
+
return filled;
|
|
694
|
+
}
|
|
695
|
+
chunkMWMSFIWN_cjs.__name(fillPath, "fillPath");
|
|
696
|
+
|
|
697
|
+
// src/http/http-client.ts
|
|
698
|
+
var _HttpClient = class _HttpClient {
|
|
699
|
+
constructor(config) {
|
|
700
|
+
chunkMWMSFIWN_cjs.__publicField(this, "baseUrl");
|
|
701
|
+
chunkMWMSFIWN_cjs.__publicField(this, "defaultHeaders");
|
|
702
|
+
chunkMWMSFIWN_cjs.__publicField(this, "timeout");
|
|
703
|
+
chunkMWMSFIWN_cjs.__publicField(this, "retryConfig");
|
|
704
|
+
chunkMWMSFIWN_cjs.__publicField(this, "debug");
|
|
705
|
+
chunkMWMSFIWN_cjs.__publicField(this, "logger");
|
|
706
|
+
chunkMWMSFIWN_cjs.__publicField(this, "config");
|
|
707
|
+
this.config = config;
|
|
708
|
+
this.baseUrl = config.baseUrl;
|
|
709
|
+
this.timeout = config.timeout;
|
|
710
|
+
this.debug = config.debug.enabled ?? false;
|
|
711
|
+
this.logger = config.debug.logger;
|
|
712
|
+
this.defaultHeaders = {
|
|
713
|
+
"Content-Type": "application/json",
|
|
714
|
+
"X-API-Key": config.apiKey,
|
|
715
|
+
...config.headers
|
|
716
|
+
};
|
|
717
|
+
if (config.dataset != null) {
|
|
718
|
+
this.defaultHeaders["X-RelyIO-Dataset"] = config.dataset;
|
|
719
|
+
}
|
|
720
|
+
if (typeof window === "undefined") {
|
|
721
|
+
const globalWithVersion = globalThis;
|
|
722
|
+
this.defaultHeaders["User-Agent"] = `RelyIO-SDK/${globalWithVersion.__VERSION__ ?? "0.0.0"}`;
|
|
723
|
+
}
|
|
724
|
+
this.retryConfig = {
|
|
725
|
+
maxAttempts: config.retryAttempts,
|
|
726
|
+
baseDelay: 1e3,
|
|
727
|
+
maxDelay: 1e4,
|
|
728
|
+
backoffFactor: 2,
|
|
729
|
+
jitter: true,
|
|
730
|
+
retryCondition: /* @__PURE__ */ chunkMWMSFIWN_cjs.__name((error, attempt) => {
|
|
731
|
+
return ErrorMapper.isRetryable(error) && attempt < config.retryAttempts;
|
|
732
|
+
}, "retryCondition")
|
|
733
|
+
};
|
|
734
|
+
this.log("debug", "HttpClient initialized", {
|
|
735
|
+
baseUrl: this.baseUrl,
|
|
736
|
+
timeout: this.timeout,
|
|
737
|
+
retryAttempts: config.retryAttempts,
|
|
738
|
+
environment: typeof window === "undefined" ? "node" : "browser"
|
|
739
|
+
});
|
|
740
|
+
}
|
|
741
|
+
/**
|
|
742
|
+
* Call a route the committed document declares, and get back the body that
|
|
743
|
+
* document says it serves.
|
|
744
|
+
*/
|
|
745
|
+
async send(path, method, options) {
|
|
746
|
+
const pathParams = options.path === void 0 ? {} : { ...options.path };
|
|
747
|
+
return this.request({
|
|
748
|
+
method: method.toUpperCase(),
|
|
749
|
+
url: fillPath(path, pathParams),
|
|
750
|
+
params: options.query,
|
|
751
|
+
body: options.body,
|
|
752
|
+
headers: options.headers,
|
|
753
|
+
signal: options.signal,
|
|
754
|
+
timeout: options.timeout
|
|
755
|
+
});
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* Call a route and return its payload — `send` plus the one unwrap, which is
|
|
759
|
+
* what all but a handful of call sites want.
|
|
760
|
+
*/
|
|
761
|
+
async fetchPayload(path, method, options) {
|
|
762
|
+
const response = await this.send(path, method, options);
|
|
763
|
+
return readPayload(response.body);
|
|
764
|
+
}
|
|
765
|
+
/**
|
|
766
|
+
* Make an HTTP request with retry logic
|
|
767
|
+
*/
|
|
768
|
+
async request(config) {
|
|
769
|
+
const mergedConfig = this.mergeRequestConfig(config);
|
|
770
|
+
this.log("debug", "Making request", {
|
|
771
|
+
method: mergedConfig.method,
|
|
772
|
+
url: mergedConfig.url
|
|
773
|
+
// Show the final URL with query params
|
|
774
|
+
});
|
|
775
|
+
let lastError;
|
|
776
|
+
let attempt = 0;
|
|
777
|
+
while (attempt < this.retryConfig.maxAttempts) {
|
|
778
|
+
attempt++;
|
|
779
|
+
try {
|
|
780
|
+
if (config.method === "DELETE" && mergedConfig.headers) {
|
|
781
|
+
delete mergedConfig.headers["Content-Type"];
|
|
782
|
+
}
|
|
783
|
+
const response = await this.makeRequest(mergedConfig.url, mergedConfig);
|
|
784
|
+
this.log("debug", "Request successful", {
|
|
785
|
+
status: response.status,
|
|
786
|
+
attempt,
|
|
787
|
+
url: mergedConfig.url
|
|
788
|
+
});
|
|
789
|
+
return response;
|
|
790
|
+
} catch (error) {
|
|
791
|
+
lastError = error;
|
|
792
|
+
this.log("warn", "Request failed", {
|
|
793
|
+
error: error instanceof Error ? error.message : "Unknown error",
|
|
794
|
+
attempt,
|
|
795
|
+
url: mergedConfig.url
|
|
796
|
+
});
|
|
797
|
+
if (attempt < this.retryConfig.maxAttempts && this.retryConfig.retryCondition?.(lastError, attempt) === true) {
|
|
798
|
+
const delay = this.calculateRetryDelay(attempt);
|
|
799
|
+
this.log("debug", "Retrying request", { attempt, delay });
|
|
800
|
+
await this.sleep(delay);
|
|
801
|
+
continue;
|
|
802
|
+
}
|
|
803
|
+
break;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
const sdkError = ErrorMapper.mapGenericError(lastError, {
|
|
807
|
+
url: mergedConfig.url,
|
|
808
|
+
method: mergedConfig.method,
|
|
809
|
+
attempt
|
|
810
|
+
});
|
|
811
|
+
this.log("error", "Request failed after all retries", {
|
|
812
|
+
error: sdkError.toJSON(),
|
|
813
|
+
url: mergedConfig.url
|
|
814
|
+
});
|
|
815
|
+
throw sdkError;
|
|
816
|
+
}
|
|
817
|
+
/**
|
|
818
|
+
* Make a GET request
|
|
819
|
+
*/
|
|
820
|
+
async get(url, params) {
|
|
821
|
+
return this.request({
|
|
822
|
+
method: "GET",
|
|
823
|
+
url,
|
|
824
|
+
params
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
/**
|
|
828
|
+
* Make a POST request
|
|
829
|
+
*/
|
|
830
|
+
async post(url, data, params) {
|
|
831
|
+
return this.request({
|
|
832
|
+
method: "POST",
|
|
833
|
+
url,
|
|
834
|
+
body: data,
|
|
835
|
+
params
|
|
836
|
+
});
|
|
837
|
+
}
|
|
838
|
+
/**
|
|
839
|
+
* Make a PUT request
|
|
840
|
+
*/
|
|
841
|
+
async put(url, data, params) {
|
|
842
|
+
return this.request({
|
|
843
|
+
method: "PUT",
|
|
844
|
+
url,
|
|
845
|
+
body: data,
|
|
846
|
+
params
|
|
847
|
+
});
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* Make a DELETE request
|
|
851
|
+
*/
|
|
852
|
+
async delete(url, params) {
|
|
853
|
+
return this.request({
|
|
854
|
+
method: "DELETE",
|
|
855
|
+
url,
|
|
856
|
+
params
|
|
857
|
+
});
|
|
858
|
+
}
|
|
859
|
+
/**
|
|
860
|
+
* Make a PATCH request
|
|
861
|
+
*/
|
|
862
|
+
async patch(url, data, params) {
|
|
863
|
+
return this.request({
|
|
864
|
+
method: "PATCH",
|
|
865
|
+
url,
|
|
866
|
+
body: data,
|
|
867
|
+
params
|
|
868
|
+
});
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* Set default headers for all requests
|
|
872
|
+
*/
|
|
873
|
+
setDefaultHeaders(headers) {
|
|
874
|
+
this.defaultHeaders = { ...this.defaultHeaders, ...headers };
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Set authentication token
|
|
878
|
+
*/
|
|
879
|
+
setAuthToken(token) {
|
|
880
|
+
this.defaultHeaders.Authorization = `Bearer ${token}`;
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Get current default headers
|
|
884
|
+
*/
|
|
885
|
+
getDefaultHeaders() {
|
|
886
|
+
return { ...this.defaultHeaders };
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* Update base URL
|
|
890
|
+
*/
|
|
891
|
+
setBaseUrl(baseUrl) {
|
|
892
|
+
this.baseUrl = baseUrl;
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* Get current base URL
|
|
896
|
+
*/
|
|
897
|
+
getBaseUrl() {
|
|
898
|
+
return this.baseUrl;
|
|
899
|
+
}
|
|
900
|
+
async makeRequest(url, config) {
|
|
901
|
+
const controller = new AbortController();
|
|
902
|
+
const timeoutId = setTimeout(() => controller.abort(), config.timeout ?? this.timeout);
|
|
903
|
+
try {
|
|
904
|
+
const filteredHeaders = this.filterHeadersForBrowser(config.headers ?? {});
|
|
905
|
+
const requestInit = {
|
|
906
|
+
method: config.method,
|
|
907
|
+
headers: filteredHeaders,
|
|
908
|
+
body: this.prepareRequestBody(config),
|
|
909
|
+
signal: config.signal ?? controller.signal,
|
|
910
|
+
// Include credentials for CORS requests if in browser and configured
|
|
911
|
+
...typeof window !== "undefined" && this.config.browser.includeCredentials != null && { credentials: "include" }
|
|
912
|
+
};
|
|
913
|
+
const response = await fetch(url, requestInit);
|
|
914
|
+
clearTimeout(timeoutId);
|
|
915
|
+
const responseBody = await this.parseResponse(response);
|
|
916
|
+
if (!response.ok) {
|
|
917
|
+
const error = ErrorMapper.mapHttpError(
|
|
918
|
+
{
|
|
919
|
+
status: response.status,
|
|
920
|
+
statusText: response.statusText,
|
|
921
|
+
body: responseBody,
|
|
922
|
+
headers: this.headersToObject(response.headers)
|
|
923
|
+
},
|
|
924
|
+
{
|
|
925
|
+
url: config.url,
|
|
926
|
+
method: config.method
|
|
927
|
+
}
|
|
928
|
+
);
|
|
929
|
+
throw error;
|
|
930
|
+
}
|
|
931
|
+
return {
|
|
932
|
+
body: responseBody,
|
|
933
|
+
status: response.status,
|
|
934
|
+
statusText: response.statusText,
|
|
935
|
+
headers: this.headersToObject(response.headers),
|
|
936
|
+
config
|
|
937
|
+
};
|
|
938
|
+
} catch (error) {
|
|
939
|
+
clearTimeout(timeoutId);
|
|
940
|
+
if (error instanceof SDKError) {
|
|
941
|
+
throw error;
|
|
942
|
+
}
|
|
943
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
944
|
+
throw new TimeoutError(
|
|
945
|
+
`Request timed out after ${config.timeout ?? this.timeout}ms`,
|
|
946
|
+
config.timeout ?? this.timeout
|
|
947
|
+
);
|
|
948
|
+
}
|
|
949
|
+
if (error instanceof TypeError && error.message.includes("CORS")) {
|
|
950
|
+
const corsError = new NetworkError(
|
|
951
|
+
"CORS policy error: The request was blocked by CORS policy. This usually means the server is not configured to allow requests from this origin. Please check the server CORS configuration.",
|
|
952
|
+
void 0,
|
|
953
|
+
error,
|
|
954
|
+
{
|
|
955
|
+
url: config.url,
|
|
956
|
+
method: config.method,
|
|
957
|
+
corsError: true,
|
|
958
|
+
suggestion: "Check if the API server allows requests from your domain and has proper CORS headers configured."
|
|
959
|
+
}
|
|
960
|
+
);
|
|
961
|
+
if (typeof window !== "undefined" && this.config.browser.onCorsError) {
|
|
962
|
+
try {
|
|
963
|
+
this.config.browser.onCorsError(corsError, {
|
|
964
|
+
url: config.url,
|
|
965
|
+
method: config.method,
|
|
966
|
+
originalError: error
|
|
967
|
+
});
|
|
968
|
+
} catch (handlerError) {
|
|
969
|
+
this.log("warn", "Custom CORS error handler threw an error", handlerError);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
throw corsError;
|
|
973
|
+
}
|
|
974
|
+
throw error;
|
|
975
|
+
}
|
|
976
|
+
}
|
|
977
|
+
/**
|
|
978
|
+
* Filter headers that browsers don't allow to be set manually
|
|
979
|
+
*/
|
|
980
|
+
filterHeadersForBrowser(headers) {
|
|
981
|
+
if (typeof window !== "undefined") {
|
|
982
|
+
const filteredHeaders = { ...headers };
|
|
983
|
+
const forbiddenHeaders = ["user-agent", "referer", "origin", "host", "connection", "upgrade"];
|
|
984
|
+
forbiddenHeaders.forEach((header) => {
|
|
985
|
+
delete filteredHeaders[header];
|
|
986
|
+
delete filteredHeaders[header.toLowerCase()];
|
|
987
|
+
delete filteredHeaders[header.toUpperCase()];
|
|
988
|
+
});
|
|
989
|
+
return filteredHeaders;
|
|
990
|
+
}
|
|
991
|
+
return headers;
|
|
992
|
+
}
|
|
993
|
+
/**
|
|
994
|
+
* Prepare request body based on content type and environment
|
|
995
|
+
*/
|
|
996
|
+
prepareRequestBody(config) {
|
|
997
|
+
if (config.body == null) return void 0;
|
|
998
|
+
if (config.body instanceof FormData || config.body instanceof Blob || typeof config.body === "string") {
|
|
999
|
+
return config.body;
|
|
1000
|
+
}
|
|
1001
|
+
return JSON.stringify(config.body);
|
|
1002
|
+
}
|
|
1003
|
+
async parseResponse(response) {
|
|
1004
|
+
if (response.status === 204 || response.status === 205) {
|
|
1005
|
+
return void 0;
|
|
1006
|
+
}
|
|
1007
|
+
const contentType = response.headers.get("content-type");
|
|
1008
|
+
if (contentType == null) {
|
|
1009
|
+
return await response.text();
|
|
1010
|
+
}
|
|
1011
|
+
if (contentType.includes("application/json")) {
|
|
1012
|
+
return await response.json();
|
|
1013
|
+
}
|
|
1014
|
+
if (contentType.includes("text/")) {
|
|
1015
|
+
return await response.text();
|
|
1016
|
+
}
|
|
1017
|
+
try {
|
|
1018
|
+
return await response.json();
|
|
1019
|
+
} catch {
|
|
1020
|
+
return await response.text();
|
|
1021
|
+
}
|
|
1022
|
+
}
|
|
1023
|
+
resolveUrl(url) {
|
|
1024
|
+
if (url.startsWith("http://") || url.startsWith("https://")) {
|
|
1025
|
+
return url;
|
|
1026
|
+
}
|
|
1027
|
+
return `${this.baseUrl}${url.startsWith("/") ? "" : "/"}${url}`;
|
|
1028
|
+
}
|
|
1029
|
+
mergeRequestConfig(config) {
|
|
1030
|
+
const url = this.resolveUrl(config.url);
|
|
1031
|
+
const queryString = config.params != null ? this.buildQueryString(config.params) : "";
|
|
1032
|
+
const finalUrl = queryString.length > 0 ? `${url}?${queryString}` : url;
|
|
1033
|
+
return {
|
|
1034
|
+
...config,
|
|
1035
|
+
url: finalUrl,
|
|
1036
|
+
headers: {
|
|
1037
|
+
...this.defaultHeaders,
|
|
1038
|
+
...config.headers
|
|
1039
|
+
},
|
|
1040
|
+
timeout: config.timeout ?? this.timeout
|
|
1041
|
+
};
|
|
1042
|
+
}
|
|
1043
|
+
buildQueryString(params) {
|
|
1044
|
+
const searchParams = new URLSearchParams();
|
|
1045
|
+
for (const [key, value] of Object.entries(params)) {
|
|
1046
|
+
if (value !== void 0 && value !== null) {
|
|
1047
|
+
if (Array.isArray(value)) {
|
|
1048
|
+
value.forEach((v) => searchParams.append(key, String(v)));
|
|
1049
|
+
} else {
|
|
1050
|
+
searchParams.append(key, String(value));
|
|
1051
|
+
}
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
return searchParams.toString();
|
|
1055
|
+
}
|
|
1056
|
+
headersToObject(headers) {
|
|
1057
|
+
const obj = {};
|
|
1058
|
+
headers.forEach((value, key) => {
|
|
1059
|
+
obj[key] = value;
|
|
1060
|
+
});
|
|
1061
|
+
return obj;
|
|
1062
|
+
}
|
|
1063
|
+
calculateRetryDelay(attempt) {
|
|
1064
|
+
const exponentialDelay = this.retryConfig.baseDelay * Math.pow(this.retryConfig.backoffFactor, attempt - 1);
|
|
1065
|
+
const delay = Math.min(exponentialDelay, this.retryConfig.maxDelay);
|
|
1066
|
+
if (this.retryConfig.jitter) {
|
|
1067
|
+
return delay * (0.5 + Math.random() * 0.5);
|
|
1068
|
+
}
|
|
1069
|
+
return delay;
|
|
1070
|
+
}
|
|
1071
|
+
sleep(ms) {
|
|
1072
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
1073
|
+
}
|
|
1074
|
+
log(level, message, data) {
|
|
1075
|
+
if (!this.debug) return;
|
|
1076
|
+
if (this.logger != null) {
|
|
1077
|
+
this.logger(level, message, data);
|
|
1078
|
+
} else {
|
|
1079
|
+
console.log(`[RelyIO SDK ${level.toUpperCase()}] ${message}`, data ?? "");
|
|
1080
|
+
}
|
|
1081
|
+
}
|
|
1082
|
+
};
|
|
1083
|
+
chunkMWMSFIWN_cjs.__name(_HttpClient, "HttpClient");
|
|
1084
|
+
var HttpClient = _HttpClient;
|
|
1085
|
+
|
|
1086
|
+
// src/content/query-builder.ts
|
|
1087
|
+
var _ContentQueryBuilder = class _ContentQueryBuilder {
|
|
1088
|
+
constructor(httpClient, contentType, defaultLocale) {
|
|
1089
|
+
this.httpClient = httpClient;
|
|
1090
|
+
this.contentType = contentType;
|
|
1091
|
+
this.defaultLocale = defaultLocale;
|
|
1092
|
+
chunkMWMSFIWN_cjs.__publicField(this, "queryOptions", {});
|
|
1093
|
+
chunkMWMSFIWN_cjs.__publicField(this, "localeValue");
|
|
1094
|
+
chunkMWMSFIWN_cjs.__publicField(this, "localesArray");
|
|
1095
|
+
chunkMWMSFIWN_cjs.__publicField(this, "fetchAllLocales", false);
|
|
1096
|
+
chunkMWMSFIWN_cjs.__publicField(this, "fieldsProjection");
|
|
1097
|
+
chunkMWMSFIWN_cjs.__publicField(this, "childrenOptions");
|
|
1098
|
+
chunkMWMSFIWN_cjs.__publicField(this, "pageNumber");
|
|
1099
|
+
chunkMWMSFIWN_cjs.__publicField(this, "pageSizeValue");
|
|
1100
|
+
}
|
|
1101
|
+
/**
|
|
1102
|
+
* Add where conditions to the query
|
|
1103
|
+
*/
|
|
1104
|
+
where(conditions) {
|
|
1105
|
+
this.queryOptions.where = {
|
|
1106
|
+
...this.queryOptions.where,
|
|
1107
|
+
...conditions
|
|
1108
|
+
};
|
|
1109
|
+
return this;
|
|
1110
|
+
}
|
|
1111
|
+
/**
|
|
1112
|
+
* Select specific fields to return
|
|
1113
|
+
*/
|
|
1114
|
+
select(fields) {
|
|
1115
|
+
this.queryOptions.select = fields;
|
|
1116
|
+
return this;
|
|
1117
|
+
}
|
|
1118
|
+
/**
|
|
1119
|
+
* Include related content in the response
|
|
1120
|
+
*/
|
|
1121
|
+
include(relations) {
|
|
1122
|
+
this.queryOptions.include = [...this.queryOptions.include ?? [], ...relations];
|
|
1123
|
+
return this;
|
|
1124
|
+
}
|
|
1125
|
+
sort(fieldOrOptions, direction) {
|
|
1126
|
+
if (typeof fieldOrOptions === "string") {
|
|
1127
|
+
this.queryOptions.sort = {
|
|
1128
|
+
...this.queryOptions.sort,
|
|
1129
|
+
[fieldOrOptions]: direction ?? "asc"
|
|
1130
|
+
};
|
|
1131
|
+
} else {
|
|
1132
|
+
this.queryOptions.sort = {
|
|
1133
|
+
...this.queryOptions.sort,
|
|
1134
|
+
...fieldOrOptions
|
|
1135
|
+
};
|
|
1136
|
+
}
|
|
1137
|
+
return this;
|
|
1138
|
+
}
|
|
1139
|
+
/**
|
|
1140
|
+
* Sort by a specific field in ascending order
|
|
1141
|
+
*/
|
|
1142
|
+
sortAsc(field) {
|
|
1143
|
+
return this.sort(field, "asc");
|
|
1144
|
+
}
|
|
1145
|
+
/**
|
|
1146
|
+
* Sort by a specific field in descending order
|
|
1147
|
+
*/
|
|
1148
|
+
sortDesc(field) {
|
|
1149
|
+
return this.sort(field, "desc");
|
|
1150
|
+
}
|
|
1151
|
+
/**
|
|
1152
|
+
* Limit the number of results
|
|
1153
|
+
*/
|
|
1154
|
+
limit(count) {
|
|
1155
|
+
this.queryOptions.limit = count;
|
|
1156
|
+
return this;
|
|
1157
|
+
}
|
|
1158
|
+
/**
|
|
1159
|
+
* Set offset for pagination
|
|
1160
|
+
*/
|
|
1161
|
+
offset(count) {
|
|
1162
|
+
this.queryOptions.offset = count;
|
|
1163
|
+
return this;
|
|
1164
|
+
}
|
|
1165
|
+
/**
|
|
1166
|
+
* Set cursor for cursor-based pagination
|
|
1167
|
+
*/
|
|
1168
|
+
cursor(cursor) {
|
|
1169
|
+
this.queryOptions.cursor = cursor;
|
|
1170
|
+
return this;
|
|
1171
|
+
}
|
|
1172
|
+
/**
|
|
1173
|
+
* Set locale for localized content
|
|
1174
|
+
* @param locale - Locale code (e.g., 'en-US', 'de-DE', 'fr-FR')
|
|
1175
|
+
*
|
|
1176
|
+
* @example
|
|
1177
|
+
* ```typescript
|
|
1178
|
+
* // Fetch in Spanish
|
|
1179
|
+
* .locale('es-ES')
|
|
1180
|
+
* ```
|
|
1181
|
+
*/
|
|
1182
|
+
locale(locale) {
|
|
1183
|
+
this.localeValue = locale;
|
|
1184
|
+
this.localesArray = void 0;
|
|
1185
|
+
this.fetchAllLocales = false;
|
|
1186
|
+
return this;
|
|
1187
|
+
}
|
|
1188
|
+
/**
|
|
1189
|
+
* Fetch content in multiple specific locales
|
|
1190
|
+
* Returns primary locale at top level, others in _metadata.localizations
|
|
1191
|
+
*
|
|
1192
|
+
* @param localeCodes - Array of locale codes to fetch
|
|
1193
|
+
*
|
|
1194
|
+
* @example
|
|
1195
|
+
* ```typescript
|
|
1196
|
+
* const product = await client.content('product')
|
|
1197
|
+
* .findById('prod_123')
|
|
1198
|
+
* .locales(['en-US', 'es-ES', 'de-DE'])
|
|
1199
|
+
* .execute()
|
|
1200
|
+
*
|
|
1201
|
+
* // Access: product.title (first locale)
|
|
1202
|
+
* // Access: product._metadata.localizations['es-ES'].title
|
|
1203
|
+
* ```
|
|
1204
|
+
*/
|
|
1205
|
+
locales(localeCodes) {
|
|
1206
|
+
this.localesArray = localeCodes;
|
|
1207
|
+
this.localeValue = void 0;
|
|
1208
|
+
this.fetchAllLocales = false;
|
|
1209
|
+
return this;
|
|
1210
|
+
}
|
|
1211
|
+
/**
|
|
1212
|
+
* Fetch content in all available locales
|
|
1213
|
+
* Returns default locale at top level, others in _metadata.localizations
|
|
1214
|
+
*
|
|
1215
|
+
* @example
|
|
1216
|
+
* ```typescript
|
|
1217
|
+
* const product = await client.content('product')
|
|
1218
|
+
* .findById('prod_123')
|
|
1219
|
+
* .allLocales()
|
|
1220
|
+
* .execute()
|
|
1221
|
+
*
|
|
1222
|
+
* // Access: product.title (default locale)
|
|
1223
|
+
* // Access: product._metadata.localizations['es-ES'].title
|
|
1224
|
+
* // Access: product._metadata.availableLocales
|
|
1225
|
+
* ```
|
|
1226
|
+
*/
|
|
1227
|
+
allLocales() {
|
|
1228
|
+
this.fetchAllLocales = true;
|
|
1229
|
+
this.localeValue = void 0;
|
|
1230
|
+
this.localesArray = void 0;
|
|
1231
|
+
return this;
|
|
1232
|
+
}
|
|
1233
|
+
/**
|
|
1234
|
+
* Select specific fields to return (projection)
|
|
1235
|
+
* @param fieldNames - Array of field names to include
|
|
1236
|
+
*
|
|
1237
|
+
* @example
|
|
1238
|
+
* ```typescript
|
|
1239
|
+
* .fields(['id', 'title', 'price', 'brand.name'])
|
|
1240
|
+
* ```
|
|
1241
|
+
*/
|
|
1242
|
+
fields(fieldNames) {
|
|
1243
|
+
this.fieldsProjection = fieldNames;
|
|
1244
|
+
return this;
|
|
1245
|
+
}
|
|
1246
|
+
/**
|
|
1247
|
+
* Include children in results (for hierarchical content)
|
|
1248
|
+
* @param options - Children options or boolean to enable with defaults
|
|
1249
|
+
*
|
|
1250
|
+
* @example
|
|
1251
|
+
* ```typescript
|
|
1252
|
+
* // Include all children
|
|
1253
|
+
* .children(true)
|
|
1254
|
+
*
|
|
1255
|
+
* // Include with options
|
|
1256
|
+
* .children({ contentTypes: ['variant'], limit: 10 })
|
|
1257
|
+
* ```
|
|
1258
|
+
*/
|
|
1259
|
+
children(options) {
|
|
1260
|
+
if (typeof options === "boolean") {
|
|
1261
|
+
this.childrenOptions = options ? {} : void 0;
|
|
1262
|
+
} else {
|
|
1263
|
+
this.childrenOptions = options;
|
|
1264
|
+
}
|
|
1265
|
+
return this;
|
|
1266
|
+
}
|
|
1267
|
+
/**
|
|
1268
|
+
* Set page number for page-based pagination
|
|
1269
|
+
* @param pageNum - Page number (1-based)
|
|
1270
|
+
*/
|
|
1271
|
+
page(pageNum) {
|
|
1272
|
+
this.pageNumber = pageNum;
|
|
1273
|
+
return this;
|
|
1274
|
+
}
|
|
1275
|
+
/**
|
|
1276
|
+
* Set page size for page-based pagination
|
|
1277
|
+
* @param size - Items per page
|
|
1278
|
+
*/
|
|
1279
|
+
pageSize(size) {
|
|
1280
|
+
this.pageSizeValue = size;
|
|
1281
|
+
return this;
|
|
1282
|
+
}
|
|
1283
|
+
/**
|
|
1284
|
+
* Execute the query and return all matching results
|
|
1285
|
+
*/
|
|
1286
|
+
async execute() {
|
|
1287
|
+
return readPayload(await this.executeWithMeta());
|
|
1288
|
+
}
|
|
1289
|
+
/**
|
|
1290
|
+
* Execute the query and return the whole body — the items under `data`, the
|
|
1291
|
+
* cursor facts under `page`.
|
|
1292
|
+
*/
|
|
1293
|
+
async executeWithMeta() {
|
|
1294
|
+
const response = await this.httpClient.send(
|
|
1295
|
+
"/v1/{contentType}",
|
|
1296
|
+
"get",
|
|
1297
|
+
{ path: { contentType: this.contentType }, query: this.buildQueryParams() }
|
|
1298
|
+
);
|
|
1299
|
+
return response.body;
|
|
1300
|
+
}
|
|
1301
|
+
/**
|
|
1302
|
+
* Execute the query and return a single result (first match)
|
|
1303
|
+
*/
|
|
1304
|
+
async first() {
|
|
1305
|
+
const originalLimit = this.queryOptions.limit;
|
|
1306
|
+
this.limit(1);
|
|
1307
|
+
try {
|
|
1308
|
+
const results = await this.execute();
|
|
1309
|
+
return results.length > 0 ? results[0] ?? null : null;
|
|
1310
|
+
} finally {
|
|
1311
|
+
if (originalLimit !== void 0) {
|
|
1312
|
+
this.queryOptions.limit = originalLimit;
|
|
1313
|
+
} else {
|
|
1314
|
+
delete this.queryOptions.limit;
|
|
1315
|
+
}
|
|
1316
|
+
}
|
|
1317
|
+
}
|
|
1318
|
+
/**
|
|
1319
|
+
* Get paginated results
|
|
1320
|
+
*/
|
|
1321
|
+
async paginate(limit = 20) {
|
|
1322
|
+
return this.limit(limit).executeWithMeta();
|
|
1323
|
+
}
|
|
1324
|
+
/**
|
|
1325
|
+
* Create an async iterator for streaming results
|
|
1326
|
+
*/
|
|
1327
|
+
async *[Symbol.asyncIterator]() {
|
|
1328
|
+
const limit = this.queryOptions.limit ?? 100;
|
|
1329
|
+
let cursor;
|
|
1330
|
+
let hasMore = true;
|
|
1331
|
+
while (hasMore) {
|
|
1332
|
+
if (cursor != null && cursor !== "") {
|
|
1333
|
+
this.cursor(cursor);
|
|
1334
|
+
}
|
|
1335
|
+
const result = await this.limit(limit).executeWithMeta();
|
|
1336
|
+
for (const item of readPayload(result)) {
|
|
1337
|
+
yield item;
|
|
1338
|
+
}
|
|
1339
|
+
hasMore = result.page.hasMore;
|
|
1340
|
+
cursor = result.page.nextCursor ?? void 0;
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
/**
|
|
1344
|
+
* Execute the query and return all results as an array (for large datasets)
|
|
1345
|
+
*/
|
|
1346
|
+
async all() {
|
|
1347
|
+
const allItems = [];
|
|
1348
|
+
for await (const item of this) {
|
|
1349
|
+
allItems.push(item);
|
|
1350
|
+
}
|
|
1351
|
+
return allItems;
|
|
1352
|
+
}
|
|
1353
|
+
/**
|
|
1354
|
+
* Clone the query builder
|
|
1355
|
+
*/
|
|
1356
|
+
clone() {
|
|
1357
|
+
const cloned = new _ContentQueryBuilder(this.httpClient, this.contentType, this.defaultLocale);
|
|
1358
|
+
cloned.queryOptions = JSON.parse(JSON.stringify(this.queryOptions));
|
|
1359
|
+
cloned.localeValue = this.localeValue;
|
|
1360
|
+
cloned.localesArray = this.localesArray ? [...this.localesArray] : void 0;
|
|
1361
|
+
cloned.fetchAllLocales = this.fetchAllLocales;
|
|
1362
|
+
cloned.fieldsProjection = this.fieldsProjection ? [...this.fieldsProjection] : void 0;
|
|
1363
|
+
cloned.pageNumber = this.pageNumber;
|
|
1364
|
+
cloned.pageSizeValue = this.pageSizeValue;
|
|
1365
|
+
return cloned;
|
|
1366
|
+
}
|
|
1367
|
+
/**
|
|
1368
|
+
* Get the current query options
|
|
1369
|
+
*/
|
|
1370
|
+
getQueryOptions() {
|
|
1371
|
+
return { ...this.queryOptions };
|
|
1372
|
+
}
|
|
1373
|
+
/**
|
|
1374
|
+
* Execute the query and return the whole body.
|
|
1375
|
+
*
|
|
1376
|
+
* Identical to {@link executeWithMeta} now that the list serves one shape;
|
|
1377
|
+
* kept as the name callers already import.
|
|
1378
|
+
*/
|
|
1379
|
+
async executeWithMetaExtended() {
|
|
1380
|
+
return this.executeWithMeta();
|
|
1381
|
+
}
|
|
1382
|
+
/**
|
|
1383
|
+
* `count()` and `exists()` are gone, and their absence is the honest answer
|
|
1384
|
+
* rather than an omission (`INV-RD-CAPABILITY-IS-HONEST`).
|
|
1385
|
+
*
|
|
1386
|
+
* `count()` sent `withCount` and read `pagination.total`; API2 moved the
|
|
1387
|
+
* customer list onto the **cursor** dialect, whose page facts are
|
|
1388
|
+
* `{ nextCursor, hasMore }` and nothing else, so no total reaches this
|
|
1389
|
+
* surface at all. Keeping the method would have meant either a request that
|
|
1390
|
+
* always throws or a number assembled from a page the caller happened to
|
|
1391
|
+
* fetch — a count of what was returned, presented as a count of what matched
|
|
1392
|
+
* (rule 6). The plan's §3.3 listed `count` among six methods a type would
|
|
1393
|
+
* repair; what the type actually shows is that the capability is no longer
|
|
1394
|
+
* there. A caller that wants "is there at least one" fetches one and looks at
|
|
1395
|
+
* `data.length`; a caller that wants a true total uses `POST /v1/search`,
|
|
1396
|
+
* which is the offset dialect precisely because a facet-bearing result wants
|
|
1397
|
+
* one (Decision 5).
|
|
1398
|
+
*/
|
|
1399
|
+
/**
|
|
1400
|
+
* Build query parameters for the HTTP request
|
|
1401
|
+
*/
|
|
1402
|
+
buildQueryParams() {
|
|
1403
|
+
const params = {};
|
|
1404
|
+
if (this.queryOptions.where && Object.keys(this.queryOptions.where).length > 0) {
|
|
1405
|
+
params.filters = JSON.stringify(this.queryOptions.where);
|
|
1406
|
+
}
|
|
1407
|
+
if (this.queryOptions.select) {
|
|
1408
|
+
params.fields = this.queryOptions.select.join(",");
|
|
1409
|
+
}
|
|
1410
|
+
if (this.fieldsProjection) {
|
|
1411
|
+
params.fields = this.fieldsProjection.join(",");
|
|
1412
|
+
}
|
|
1413
|
+
if (this.queryOptions.include) {
|
|
1414
|
+
params.include = this.queryOptions.include.join(",");
|
|
1415
|
+
}
|
|
1416
|
+
if (this.queryOptions.sort) {
|
|
1417
|
+
const sortArray = Object.entries(this.queryOptions.sort).map(
|
|
1418
|
+
([field, direction]) => `${field}:${direction}`
|
|
1419
|
+
);
|
|
1420
|
+
params.sort = sortArray.join(",");
|
|
1421
|
+
}
|
|
1422
|
+
if (this.queryOptions.limit !== void 0) {
|
|
1423
|
+
params.limit = this.queryOptions.limit;
|
|
1424
|
+
}
|
|
1425
|
+
if (this.queryOptions.offset !== void 0) {
|
|
1426
|
+
params.offset = this.queryOptions.offset;
|
|
1427
|
+
}
|
|
1428
|
+
if (this.queryOptions.cursor !== void 0) {
|
|
1429
|
+
params.cursor = this.queryOptions.cursor;
|
|
1430
|
+
}
|
|
1431
|
+
if (this.localeValue != null && this.localeValue !== "") {
|
|
1432
|
+
params.locale = this.localeValue;
|
|
1433
|
+
} else if (this.fetchAllLocales) {
|
|
1434
|
+
params.locale = "*";
|
|
1435
|
+
} else if (this.localesArray && this.localesArray.length > 0) {
|
|
1436
|
+
params.locales = this.localesArray.join(",");
|
|
1437
|
+
} else if (this.defaultLocale != null && this.defaultLocale !== "") {
|
|
1438
|
+
params.locale = this.defaultLocale;
|
|
1439
|
+
}
|
|
1440
|
+
if (this.pageNumber !== void 0) {
|
|
1441
|
+
params.page = this.pageNumber;
|
|
1442
|
+
}
|
|
1443
|
+
if (this.pageSizeValue !== void 0) {
|
|
1444
|
+
params.pageSize = this.pageSizeValue;
|
|
1445
|
+
}
|
|
1446
|
+
if (this.childrenOptions) {
|
|
1447
|
+
if (this.childrenOptions.contentTypes) {
|
|
1448
|
+
params.children = this.childrenOptions.contentTypes.join(",");
|
|
1449
|
+
} else {
|
|
1450
|
+
params.children = "true";
|
|
1451
|
+
}
|
|
1452
|
+
if (this.childrenOptions.limit !== void 0) {
|
|
1453
|
+
params.childrenLimit = this.childrenOptions.limit;
|
|
1454
|
+
}
|
|
1455
|
+
}
|
|
1456
|
+
return params;
|
|
1457
|
+
}
|
|
1458
|
+
};
|
|
1459
|
+
chunkMWMSFIWN_cjs.__name(_ContentQueryBuilder, "ContentQueryBuilder");
|
|
1460
|
+
var ContentQueryBuilder = _ContentQueryBuilder;
|
|
1461
|
+
var _ContentByIdBuilder = class _ContentByIdBuilder {
|
|
1462
|
+
constructor(httpClient, contentType, id, defaultLocale) {
|
|
1463
|
+
this.httpClient = httpClient;
|
|
1464
|
+
this.contentType = contentType;
|
|
1465
|
+
this.id = id;
|
|
1466
|
+
this.defaultLocale = defaultLocale;
|
|
1467
|
+
chunkMWMSFIWN_cjs.__publicField(this, "includeRelations");
|
|
1468
|
+
chunkMWMSFIWN_cjs.__publicField(this, "localeValue");
|
|
1469
|
+
chunkMWMSFIWN_cjs.__publicField(this, "localesArray");
|
|
1470
|
+
chunkMWMSFIWN_cjs.__publicField(this, "fetchAllLocales", false);
|
|
1471
|
+
chunkMWMSFIWN_cjs.__publicField(this, "childrenOptions");
|
|
1472
|
+
}
|
|
1473
|
+
/**
|
|
1474
|
+
* Include related content in the response
|
|
1475
|
+
*/
|
|
1476
|
+
include(relations) {
|
|
1477
|
+
this.includeRelations = relations;
|
|
1478
|
+
return this;
|
|
1479
|
+
}
|
|
1480
|
+
/**
|
|
1481
|
+
* Set locale for localized content
|
|
1482
|
+
* @param locale - Locale code (e.g., 'en-US', 'de-DE', 'fr-FR')
|
|
1483
|
+
*/
|
|
1484
|
+
locale(locale) {
|
|
1485
|
+
this.localeValue = locale;
|
|
1486
|
+
this.localesArray = void 0;
|
|
1487
|
+
this.fetchAllLocales = false;
|
|
1488
|
+
return this;
|
|
1489
|
+
}
|
|
1490
|
+
/**
|
|
1491
|
+
* Fetch content in multiple specific locales
|
|
1492
|
+
* Returns primary locale at top level, others in _metadata.localizations
|
|
1493
|
+
*
|
|
1494
|
+
* @param localeCodes - Array of locale codes to fetch
|
|
1495
|
+
*/
|
|
1496
|
+
locales(localeCodes) {
|
|
1497
|
+
this.localesArray = localeCodes;
|
|
1498
|
+
this.localeValue = void 0;
|
|
1499
|
+
this.fetchAllLocales = false;
|
|
1500
|
+
return this;
|
|
1501
|
+
}
|
|
1502
|
+
/**
|
|
1503
|
+
* Fetch content in all available locales
|
|
1504
|
+
* Returns default locale at top level, others in _metadata.localizations
|
|
1505
|
+
*/
|
|
1506
|
+
allLocales() {
|
|
1507
|
+
this.fetchAllLocales = true;
|
|
1508
|
+
this.localeValue = void 0;
|
|
1509
|
+
this.localesArray = void 0;
|
|
1510
|
+
return this;
|
|
1511
|
+
}
|
|
1512
|
+
/**
|
|
1513
|
+
* Include children in results (for hierarchical content)
|
|
1514
|
+
* @param options - Children options or boolean to enable with defaults
|
|
1515
|
+
*/
|
|
1516
|
+
children(options) {
|
|
1517
|
+
if (typeof options === "boolean") {
|
|
1518
|
+
this.childrenOptions = options ? {} : void 0;
|
|
1519
|
+
} else {
|
|
1520
|
+
this.childrenOptions = options;
|
|
1521
|
+
}
|
|
1522
|
+
return this;
|
|
1523
|
+
}
|
|
1524
|
+
/**
|
|
1525
|
+
* Execute the query and return the content item
|
|
1526
|
+
*/
|
|
1527
|
+
async execute() {
|
|
1528
|
+
const params = {};
|
|
1529
|
+
if (this.includeRelations != null) {
|
|
1530
|
+
params.include = this.includeRelations.join(",");
|
|
1531
|
+
}
|
|
1532
|
+
if (this.localeValue != null && this.localeValue !== "") {
|
|
1533
|
+
params.locale = this.localeValue;
|
|
1534
|
+
} else if (this.fetchAllLocales) {
|
|
1535
|
+
params.locale = "*";
|
|
1536
|
+
} else if (this.localesArray && this.localesArray.length > 0) {
|
|
1537
|
+
params.locales = this.localesArray.join(",");
|
|
1538
|
+
} else if (this.defaultLocale != null && this.defaultLocale !== "") {
|
|
1539
|
+
params.locale = this.defaultLocale;
|
|
1540
|
+
}
|
|
1541
|
+
if (this.childrenOptions != null) {
|
|
1542
|
+
if (this.childrenOptions.contentTypes) {
|
|
1543
|
+
params.children = this.childrenOptions.contentTypes.join(",");
|
|
1544
|
+
} else {
|
|
1545
|
+
params.children = "true";
|
|
1546
|
+
}
|
|
1547
|
+
if (this.childrenOptions.limit !== void 0) {
|
|
1548
|
+
params.childrenLimit = this.childrenOptions.limit;
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
try {
|
|
1552
|
+
return await this.httpClient.fetchPayload(
|
|
1553
|
+
"/v1/{contentType}/{id}",
|
|
1554
|
+
"get",
|
|
1555
|
+
{ path: { contentType: this.contentType, id: this.id }, query: params }
|
|
1556
|
+
);
|
|
1557
|
+
} catch (error) {
|
|
1558
|
+
if (isResourceNotFound(error)) return null;
|
|
1559
|
+
throw error;
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
};
|
|
1563
|
+
chunkMWMSFIWN_cjs.__name(_ContentByIdBuilder, "ContentByIdBuilder");
|
|
1564
|
+
var ContentByIdBuilder = _ContentByIdBuilder;
|
|
1565
|
+
|
|
1566
|
+
// ../../platform/api-contract/dist/index.js
|
|
1567
|
+
var HEADER_ENTITY_TAG = { name: "X-Entity-Tag" };
|
|
1568
|
+
var HEADER_IF_MATCH = {
|
|
1569
|
+
name: "If-Match"};
|
|
1570
|
+
var HEADER_IDEMPOTENCY_KEY = {
|
|
1571
|
+
name: "Idempotency-Key"};
|
|
1572
|
+
|
|
1573
|
+
// src/http/write-options.ts
|
|
1574
|
+
function writeHeaders(options) {
|
|
1575
|
+
if (options === void 0) return void 0;
|
|
1576
|
+
const headers = {};
|
|
1577
|
+
if ("ifMatch" in options && options.ifMatch !== void 0) {
|
|
1578
|
+
headers[HEADER_IF_MATCH.name] = options.ifMatch;
|
|
1579
|
+
}
|
|
1580
|
+
if (options.idempotencyKey !== void 0) {
|
|
1581
|
+
headers[HEADER_IDEMPOTENCY_KEY.name] = options.idempotencyKey;
|
|
1582
|
+
}
|
|
1583
|
+
return Object.keys(headers).length > 0 ? headers : void 0;
|
|
1584
|
+
}
|
|
1585
|
+
chunkMWMSFIWN_cjs.__name(writeHeaders, "writeHeaders");
|
|
1586
|
+
function entityTagOf(response) {
|
|
1587
|
+
const wanted = HEADER_ENTITY_TAG.name.toLowerCase();
|
|
1588
|
+
for (const [name, value] of Object.entries(response.headers)) {
|
|
1589
|
+
if (name.toLowerCase() === wanted) return value;
|
|
1590
|
+
}
|
|
1591
|
+
return void 0;
|
|
1592
|
+
}
|
|
1593
|
+
chunkMWMSFIWN_cjs.__name(entityTagOf, "entityTagOf");
|
|
1594
|
+
|
|
1595
|
+
// src/content/content-client.ts
|
|
1596
|
+
var _ContentClient = class _ContentClient {
|
|
1597
|
+
constructor(httpClient, contentType, defaultLocale) {
|
|
1598
|
+
this.httpClient = httpClient;
|
|
1599
|
+
this.contentType = contentType;
|
|
1600
|
+
this.defaultLocale = defaultLocale;
|
|
1601
|
+
}
|
|
1602
|
+
/**
|
|
1603
|
+
* Create a new query builder for this content type
|
|
1604
|
+
*/
|
|
1605
|
+
query() {
|
|
1606
|
+
return new ContentQueryBuilder(this.httpClient, this.contentType, this.defaultLocale);
|
|
1607
|
+
}
|
|
1608
|
+
/**
|
|
1609
|
+
* Find content by ID with fluent options
|
|
1610
|
+
*/
|
|
1611
|
+
findById(id) {
|
|
1612
|
+
return new ContentByIdBuilder(this.httpClient, this.contentType, id, this.defaultLocale);
|
|
1613
|
+
}
|
|
1614
|
+
/**
|
|
1615
|
+
* Fetch all content items
|
|
1616
|
+
* @param locale - Optional locale for localized content
|
|
1617
|
+
*/
|
|
1618
|
+
async fetchAll(locale) {
|
|
1619
|
+
const builder = this.query();
|
|
1620
|
+
if (locale != null && locale !== "") {
|
|
1621
|
+
builder.locale(locale);
|
|
1622
|
+
}
|
|
1623
|
+
return builder.execute();
|
|
1624
|
+
}
|
|
1625
|
+
/**
|
|
1626
|
+
* Fetch content with query options
|
|
1627
|
+
*/
|
|
1628
|
+
async fetch(options) {
|
|
1629
|
+
const builder = this.query();
|
|
1630
|
+
if (options?.where) {
|
|
1631
|
+
builder.where(options.where);
|
|
1632
|
+
}
|
|
1633
|
+
if (options?.limit != null) {
|
|
1634
|
+
builder.limit(options.limit);
|
|
1635
|
+
}
|
|
1636
|
+
if (options?.offset != null) {
|
|
1637
|
+
builder.offset(options.offset);
|
|
1638
|
+
}
|
|
1639
|
+
if (options?.sort) {
|
|
1640
|
+
builder.sort(options.sort);
|
|
1641
|
+
}
|
|
1642
|
+
if (options?.include) {
|
|
1643
|
+
builder.include(options.include);
|
|
1644
|
+
}
|
|
1645
|
+
if (options?.locale != null && options.locale !== "") {
|
|
1646
|
+
builder.locale(options.locale);
|
|
1647
|
+
}
|
|
1648
|
+
if (options?.fields != null) {
|
|
1649
|
+
builder.fields(options.fields);
|
|
1650
|
+
}
|
|
1651
|
+
if (options?.children != null) {
|
|
1652
|
+
builder.children(options.children);
|
|
1653
|
+
}
|
|
1654
|
+
return builder.execute();
|
|
1655
|
+
}
|
|
1656
|
+
/**
|
|
1657
|
+
* Fetch a single content item by ID
|
|
1658
|
+
* @param id - Entry ID or slug
|
|
1659
|
+
* @param options - Fetch options (include, locale)
|
|
1660
|
+
*/
|
|
1661
|
+
async fetchOne(id, options) {
|
|
1662
|
+
const builder = this.findById(id);
|
|
1663
|
+
if (options?.include != null) {
|
|
1664
|
+
builder.include(options.include);
|
|
1665
|
+
}
|
|
1666
|
+
if (options?.locale != null && options.locale !== "") {
|
|
1667
|
+
builder.locale(options.locale);
|
|
1668
|
+
}
|
|
1669
|
+
return builder.execute();
|
|
1670
|
+
}
|
|
1671
|
+
/**
|
|
1672
|
+
* Fetch one entry **and the entity tag that read served**.
|
|
1673
|
+
*
|
|
1674
|
+
* This is the read half of a conditional write. `X-Entity-Tag` is minted from
|
|
1675
|
+
* `(entryUid, locale, localeVersion)` and is exactly what `If-Match` takes,
|
|
1676
|
+
* so a caller reads here, edits, and hands the same string back to `update`
|
|
1677
|
+
* — with nothing to assemble in between
|
|
1678
|
+
* (`INV-API-CONDITIONAL-WRITE-IS-REACHABLE`).
|
|
1679
|
+
*
|
|
1680
|
+
* `entityTag` is `undefined` when the response came from the cache: a cached
|
|
1681
|
+
* body cannot know the current version, and an absent tag beats a stale one
|
|
1682
|
+
* that would refuse the next write.
|
|
1683
|
+
*/
|
|
1684
|
+
async fetchOneWithTag(id, options) {
|
|
1685
|
+
const query = {};
|
|
1686
|
+
if (options?.include != null) query.include = options.include.join(",");
|
|
1687
|
+
const locale = options?.locale ?? this.defaultLocale;
|
|
1688
|
+
if (locale != null && locale !== "") query.locale = locale;
|
|
1689
|
+
const response = await this.httpClient.send(
|
|
1690
|
+
"/v1/{contentType}/{id}",
|
|
1691
|
+
"get",
|
|
1692
|
+
{ path: { contentType: this.contentType, id }, query }
|
|
1693
|
+
);
|
|
1694
|
+
return { entry: readPayload(response.body), entityTag: entityTagOf(response) };
|
|
1695
|
+
}
|
|
1696
|
+
/**
|
|
1697
|
+
* Fetch the first content item matching the conditions
|
|
1698
|
+
* @param where - Filter conditions
|
|
1699
|
+
* @param locale - Optional locale
|
|
1700
|
+
*/
|
|
1701
|
+
async fetchFirst(where, locale) {
|
|
1702
|
+
const builder = this.query();
|
|
1703
|
+
if (where != null) {
|
|
1704
|
+
builder.where(where);
|
|
1705
|
+
}
|
|
1706
|
+
if (locale != null && locale !== "") {
|
|
1707
|
+
builder.locale(locale);
|
|
1708
|
+
}
|
|
1709
|
+
return builder.first();
|
|
1710
|
+
}
|
|
1711
|
+
/**
|
|
1712
|
+
* Create a new content item
|
|
1713
|
+
* @param data - Content data
|
|
1714
|
+
* @param options - Create options (slug, locale, parentId, sortOrder)
|
|
1715
|
+
*/
|
|
1716
|
+
async create(data, options) {
|
|
1717
|
+
const { idempotencyKey, ...body } = options ?? {};
|
|
1718
|
+
return this.httpClient.fetchPayload(
|
|
1719
|
+
"/v1/{contentType}",
|
|
1720
|
+
"post",
|
|
1721
|
+
{
|
|
1722
|
+
path: { contentType: this.contentType },
|
|
1723
|
+
body: { content: data, ...body },
|
|
1724
|
+
headers: writeHeaders({ idempotencyKey })
|
|
1725
|
+
}
|
|
1726
|
+
);
|
|
1727
|
+
}
|
|
1728
|
+
/**
|
|
1729
|
+
* Update a content item by ID (partial update)
|
|
1730
|
+
* @param id - Entry ID
|
|
1731
|
+
* @param data - Fields to update
|
|
1732
|
+
* @param options - Update options (slug, locale, parentId, sortOrder)
|
|
1733
|
+
*/
|
|
1734
|
+
async update(id, data, options) {
|
|
1735
|
+
const { idempotencyKey, ifMatch, ...body } = options ?? {};
|
|
1736
|
+
return this.httpClient.fetchPayload(
|
|
1737
|
+
"/v1/{contentType}/{id}",
|
|
1738
|
+
"patch",
|
|
1739
|
+
{
|
|
1740
|
+
path: { contentType: this.contentType, id },
|
|
1741
|
+
body: { content: data, ...body },
|
|
1742
|
+
headers: writeHeaders({ idempotencyKey, ifMatch })
|
|
1743
|
+
}
|
|
1744
|
+
);
|
|
1745
|
+
}
|
|
1746
|
+
/**
|
|
1747
|
+
* Delete a content item by ID
|
|
1748
|
+
*/
|
|
1749
|
+
async delete(id, options) {
|
|
1750
|
+
await this.httpClient.send("/v1/{contentType}/{id}", "delete", {
|
|
1751
|
+
path: { contentType: this.contentType, id },
|
|
1752
|
+
headers: writeHeaders(options)
|
|
1753
|
+
});
|
|
1754
|
+
}
|
|
1755
|
+
/**
|
|
1756
|
+
* Check if content exists by ID
|
|
1757
|
+
*/
|
|
1758
|
+
async exists(id) {
|
|
1759
|
+
try {
|
|
1760
|
+
const item = await this.fetchOne(id);
|
|
1761
|
+
return item !== null;
|
|
1762
|
+
} catch (error) {
|
|
1763
|
+
if (error instanceof ResourceNotFoundError) {
|
|
1764
|
+
return false;
|
|
1765
|
+
}
|
|
1766
|
+
throw error;
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
/**
|
|
1770
|
+
* Get version history for one locale of an entry.
|
|
1771
|
+
*
|
|
1772
|
+
* Versions are numbered per locale, so this is one locale's timeline: `locale` defaults to the
|
|
1773
|
+
* schema default, no fallback is applied, and a locale with no history returns an empty array
|
|
1774
|
+
* rather than another locale's rows. Every row names the locale it belongs to.
|
|
1775
|
+
*/
|
|
1776
|
+
async versions(id, options) {
|
|
1777
|
+
return this.httpClient.fetchPayload("/v1/{contentType}/{id}/versions", "get", {
|
|
1778
|
+
path: { contentType: this.contentType, id },
|
|
1779
|
+
query: {
|
|
1780
|
+
limit: options?.limit ?? 10,
|
|
1781
|
+
...options?.locale === void 0 ? {} : { locale: options.locale }
|
|
1782
|
+
}
|
|
1783
|
+
});
|
|
1784
|
+
}
|
|
1785
|
+
/**
|
|
1786
|
+
* Get a specific version of an entry.
|
|
1787
|
+
*
|
|
1788
|
+
* `locale` defaults to the schema default. Version numbers are per locale, so version 2 is
|
|
1789
|
+
* different content in each one.
|
|
1790
|
+
*/
|
|
1791
|
+
async version(id, versionNumber, options) {
|
|
1792
|
+
return this.httpClient.fetchPayload("/v1/{contentType}/{id}/versions/{versionNumber}", "get", {
|
|
1793
|
+
path: {
|
|
1794
|
+
contentType: this.contentType,
|
|
1795
|
+
id,
|
|
1796
|
+
versionNumber: String(versionNumber)
|
|
1797
|
+
},
|
|
1798
|
+
query: options?.locale === void 0 ? void 0 : { locale: options.locale }
|
|
1799
|
+
});
|
|
1800
|
+
}
|
|
1801
|
+
/**
|
|
1802
|
+
* Revert one locale of an entry to a previous version.
|
|
1803
|
+
*
|
|
1804
|
+
* `locale` is **required**, and that is not a convention: versions are numbered per locale, so
|
|
1805
|
+
* `versionNumber` alone is ambiguous, and defaulting it would silently revert the wrong
|
|
1806
|
+
* translation. Only that locale's fields change — shared fields and every other locale are left
|
|
1807
|
+
* untouched.
|
|
1808
|
+
*
|
|
1809
|
+
* The restore is validated as strictly as a publish, so it cannot publish invalid content, and it
|
|
1810
|
+
* records a new version of its own rather than rewinding the counter — so the revert is auditable
|
|
1811
|
+
* and itself reversible.
|
|
1812
|
+
*/
|
|
1813
|
+
async restore(id, versionNumber, locale, options) {
|
|
1814
|
+
return this.httpClient.fetchPayload("/v1/{contentType}/{id}/versions/{versionNumber}/restore", "post", {
|
|
1815
|
+
path: {
|
|
1816
|
+
contentType: this.contentType,
|
|
1817
|
+
id,
|
|
1818
|
+
versionNumber: String(versionNumber)
|
|
1819
|
+
},
|
|
1820
|
+
body: { locale },
|
|
1821
|
+
headers: writeHeaders(options)
|
|
1822
|
+
});
|
|
1823
|
+
}
|
|
1824
|
+
/**
|
|
1825
|
+
* Get children of a hierarchical entry
|
|
1826
|
+
* @param id - Parent entry ID
|
|
1827
|
+
* @param options - Fetch options
|
|
1828
|
+
*/
|
|
1829
|
+
async children(id, options) {
|
|
1830
|
+
const params = {};
|
|
1831
|
+
if (options?.depth != null) {
|
|
1832
|
+
params.depth = options.depth;
|
|
1833
|
+
}
|
|
1834
|
+
if (options?.include != null) {
|
|
1835
|
+
params.include = options.include.join(",");
|
|
1836
|
+
}
|
|
1837
|
+
if (options?.locale != null && options.locale !== "") {
|
|
1838
|
+
params.locale = options.locale;
|
|
1839
|
+
}
|
|
1840
|
+
return this.httpClient.fetchPayload(
|
|
1841
|
+
"/v1/{contentType}/{id}/children",
|
|
1842
|
+
"get",
|
|
1843
|
+
{ path: { contentType: this.contentType, id }, query: params }
|
|
1844
|
+
);
|
|
1845
|
+
}
|
|
1846
|
+
/**
|
|
1847
|
+
* Get the content type name
|
|
1848
|
+
*/
|
|
1849
|
+
getContentType() {
|
|
1850
|
+
return this.contentType;
|
|
1851
|
+
}
|
|
1852
|
+
/**
|
|
1853
|
+
* Create a new ContentClient for a different content type
|
|
1854
|
+
*/
|
|
1855
|
+
ofType(contentType) {
|
|
1856
|
+
return new _ContentClient(this.httpClient, contentType, this.defaultLocale);
|
|
1857
|
+
}
|
|
1858
|
+
/**
|
|
1859
|
+
* Create a new ContentClient with a different default locale
|
|
1860
|
+
*/
|
|
1861
|
+
withLocale(locale) {
|
|
1862
|
+
return new _ContentClient(this.httpClient, this.contentType, locale);
|
|
1863
|
+
}
|
|
1864
|
+
/**
|
|
1865
|
+
* Get the current default locale
|
|
1866
|
+
*/
|
|
1867
|
+
getDefaultLocale() {
|
|
1868
|
+
return this.defaultLocale;
|
|
1869
|
+
}
|
|
1870
|
+
};
|
|
1871
|
+
chunkMWMSFIWN_cjs.__name(_ContentClient, "ContentClient");
|
|
1872
|
+
var ContentClient = _ContentClient;
|
|
1873
|
+
|
|
1874
|
+
// src/errors/file-errors.ts
|
|
1875
|
+
var _FileUploadError = class _FileUploadError extends SDKError {
|
|
1876
|
+
constructor(code, message, options = {}) {
|
|
1877
|
+
super(message, options.statusCode, options.cause, { fileErrorCode: code });
|
|
1878
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code");
|
|
1879
|
+
chunkMWMSFIWN_cjs.__publicField(this, "retryable");
|
|
1880
|
+
this.code = code;
|
|
1881
|
+
this.retryable = options.retriable ?? false;
|
|
1882
|
+
}
|
|
1883
|
+
getUserMessage() {
|
|
1884
|
+
return this.message;
|
|
1885
|
+
}
|
|
1886
|
+
};
|
|
1887
|
+
chunkMWMSFIWN_cjs.__name(_FileUploadError, "FileUploadError");
|
|
1888
|
+
var FileUploadError = _FileUploadError;
|
|
1889
|
+
var _FileValidationError = class _FileValidationError extends BaseValidationError {
|
|
1890
|
+
constructor(message, violations = [], cause) {
|
|
1891
|
+
super(message, violations, 400, cause);
|
|
1892
|
+
chunkMWMSFIWN_cjs.__publicField(this, "code", "FILE_VALIDATION_ERROR");
|
|
1893
|
+
}
|
|
1894
|
+
getUserMessage() {
|
|
1895
|
+
if (this.violations.length === 1) return this.violations[0]?.message ?? this.message;
|
|
1896
|
+
return this.message;
|
|
1897
|
+
}
|
|
1898
|
+
};
|
|
1899
|
+
chunkMWMSFIWN_cjs.__name(_FileValidationError, "FileValidationError");
|
|
1900
|
+
var FileValidationError = _FileValidationError;
|
|
1901
|
+
function validateFile(file, constraints = {}) {
|
|
1902
|
+
const violations = [];
|
|
1903
|
+
const { allowedMimeTypes, maxSizeBytes } = constraints;
|
|
1904
|
+
if (allowedMimeTypes && allowedMimeTypes.length > 0 && file.type != null && file.type !== "" && !allowedMimeTypes.includes(file.type)) {
|
|
1905
|
+
violations.push({
|
|
1906
|
+
field: "file",
|
|
1907
|
+
code: "UNSUPPORTED_MIME",
|
|
1908
|
+
message: `Unsupported file type "${file.type}". Allowed: ${allowedMimeTypes.join(", ")}`,
|
|
1909
|
+
value: file.type
|
|
1910
|
+
});
|
|
1911
|
+
}
|
|
1912
|
+
if (typeof maxSizeBytes === "number" && maxSizeBytes > 0 && typeof file.size === "number" && file.size > maxSizeBytes) {
|
|
1913
|
+
violations.push({
|
|
1914
|
+
field: "file",
|
|
1915
|
+
code: "UPLOAD_TOO_LARGE",
|
|
1916
|
+
message: `File is too large (${file.size} bytes); max is ${maxSizeBytes} bytes`,
|
|
1917
|
+
value: file.size
|
|
1918
|
+
});
|
|
1919
|
+
}
|
|
1920
|
+
return violations;
|
|
1921
|
+
}
|
|
1922
|
+
chunkMWMSFIWN_cjs.__name(validateFile, "validateFile");
|
|
1923
|
+
|
|
1924
|
+
// src/content/asset-client.ts
|
|
1925
|
+
function hasText(value) {
|
|
1926
|
+
return value != null && value !== "";
|
|
1927
|
+
}
|
|
1928
|
+
chunkMWMSFIWN_cjs.__name(hasText, "hasText");
|
|
1929
|
+
async function computeSha256Hex(blob) {
|
|
1930
|
+
try {
|
|
1931
|
+
const subtle = globalThis.crypto?.subtle;
|
|
1932
|
+
if (subtle === void 0 || typeof blob.arrayBuffer !== "function") return void 0;
|
|
1933
|
+
const digest = await subtle.digest("SHA-256", await blob.arrayBuffer());
|
|
1934
|
+
const bytes = new Uint8Array(digest);
|
|
1935
|
+
let hex = "";
|
|
1936
|
+
for (const byte of bytes) hex += byte.toString(16).padStart(2, "0");
|
|
1937
|
+
return hex;
|
|
1938
|
+
} catch {
|
|
1939
|
+
return void 0;
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
chunkMWMSFIWN_cjs.__name(computeSha256Hex, "computeSha256Hex");
|
|
1943
|
+
var _AssetFileBuilder = class _AssetFileBuilder {
|
|
1944
|
+
constructor(httpClient, assetType, entryUid, defaultLocale) {
|
|
1945
|
+
this.httpClient = httpClient;
|
|
1946
|
+
this.assetType = assetType;
|
|
1947
|
+
this.entryUid = entryUid;
|
|
1948
|
+
this.defaultLocale = defaultLocale;
|
|
1949
|
+
}
|
|
1950
|
+
/**
|
|
1951
|
+
* The path parameters every `/file/*` route on this builder takes.
|
|
1952
|
+
*
|
|
1953
|
+
* The path *template* is the document's; only these two values are the
|
|
1954
|
+
* builder's, so a route that gains or loses a segment is a compile error here
|
|
1955
|
+
* rather than a hand-built string that silently misses.
|
|
1956
|
+
*/
|
|
1957
|
+
get target() {
|
|
1958
|
+
return { contentType: this.assetType, id: this.entryUid };
|
|
1959
|
+
}
|
|
1960
|
+
withLocale(locale) {
|
|
1961
|
+
return locale ?? this.defaultLocale;
|
|
1962
|
+
}
|
|
1963
|
+
/**
|
|
1964
|
+
* Issue a single upload target — an R2 presigned PUT (image/file) or a
|
|
1965
|
+
* Cloudflare Stream direct-creator URL (video). Resumable multipart is plan 17.
|
|
1966
|
+
*/
|
|
1967
|
+
async presign(input) {
|
|
1968
|
+
const locale = this.withLocale(input.locale);
|
|
1969
|
+
return this.httpClient.fetchPayload("/v1/{contentType}/{id}/file/presign", "post", {
|
|
1970
|
+
path: this.target,
|
|
1971
|
+
body: {
|
|
1972
|
+
fileName: input.fileName,
|
|
1973
|
+
...hasText(input.contentType) ? { contentType: input.contentType } : {},
|
|
1974
|
+
...input.size != null ? { size: input.size } : {},
|
|
1975
|
+
...hasText(locale) ? { locale } : {},
|
|
1976
|
+
...hasText(input.hash) ? { hash: input.hash } : {}
|
|
1977
|
+
}
|
|
1978
|
+
});
|
|
1979
|
+
}
|
|
1980
|
+
/**
|
|
1981
|
+
* Acknowledge a completed client upload (kicks off the fast-path intake for
|
|
1982
|
+
* R2; a no-op for Stream, whose readiness is webhook-driven).
|
|
1983
|
+
*/
|
|
1984
|
+
async confirm(input = {}) {
|
|
1985
|
+
const locale = this.withLocale(input.locale);
|
|
1986
|
+
return this.httpClient.fetchPayload("/v1/{contentType}/{id}/file/confirm", "post", {
|
|
1987
|
+
path: this.target,
|
|
1988
|
+
body: {
|
|
1989
|
+
...hasText(locale) ? { locale } : {},
|
|
1990
|
+
...hasText(input.hash) ? { hash: input.hash } : {}
|
|
1991
|
+
}
|
|
1992
|
+
});
|
|
1993
|
+
}
|
|
1994
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
1995
|
+
// MULTIPART (resumable large-file uploads) — same 5-call protocol the
|
|
1996
|
+
// Studio Uppy path uses. No Uppy dependency in the SDK.
|
|
1997
|
+
// ──────────────────────────────────────────────────────────────────────────
|
|
1998
|
+
/** Open an R2 multipart session for a large file (standalone of `presign`). */
|
|
1999
|
+
async createMultipart(input) {
|
|
2000
|
+
const locale = this.withLocale(input.locale);
|
|
2001
|
+
return this.httpClient.fetchPayload("/v1/{contentType}/{id}/file/multipart/create", "post", {
|
|
2002
|
+
path: this.target,
|
|
2003
|
+
body: {
|
|
2004
|
+
fileName: input.fileName,
|
|
2005
|
+
...hasText(input.contentType) ? { contentType: input.contentType } : {},
|
|
2006
|
+
...input.size != null ? { size: input.size } : {},
|
|
2007
|
+
...hasText(locale) ? { locale } : {},
|
|
2008
|
+
...hasText(input.hash) ? { hash: input.hash } : {}
|
|
2009
|
+
}
|
|
2010
|
+
});
|
|
2011
|
+
}
|
|
2012
|
+
/** Presign a single part PUT (1-based `partNumber`). */
|
|
2013
|
+
async signPart(target) {
|
|
2014
|
+
return this.httpClient.fetchPayload("/v1/{contentType}/{id}/file/multipart/sign-part", "post", {
|
|
2015
|
+
path: this.target,
|
|
2016
|
+
body: {
|
|
2017
|
+
uploadId: target.uploadId,
|
|
2018
|
+
objectKey: target.objectKey,
|
|
2019
|
+
partNumber: target.partNumber,
|
|
2020
|
+
...hasText(target.locale) ? { locale: target.locale } : {}
|
|
2021
|
+
}
|
|
2022
|
+
});
|
|
2023
|
+
}
|
|
2024
|
+
/** List parts already uploaded (cross-session resume). */
|
|
2025
|
+
async listParts(target) {
|
|
2026
|
+
const payload = await this.httpClient.fetchPayload("/v1/{contentType}/{id}/file/multipart/list-parts", "post", {
|
|
2027
|
+
path: this.target,
|
|
2028
|
+
body: {
|
|
2029
|
+
uploadId: target.uploadId,
|
|
2030
|
+
objectKey: target.objectKey,
|
|
2031
|
+
...hasText(target.locale) ? { locale: target.locale } : {}
|
|
2032
|
+
}
|
|
2033
|
+
});
|
|
2034
|
+
return payload.parts;
|
|
2035
|
+
}
|
|
2036
|
+
/** Finalize a multipart session. */
|
|
2037
|
+
async completeMultipart(target) {
|
|
2038
|
+
return this.httpClient.fetchPayload("/v1/{contentType}/{id}/file/multipart/complete", "post", {
|
|
2039
|
+
path: this.target,
|
|
2040
|
+
body: {
|
|
2041
|
+
uploadId: target.uploadId,
|
|
2042
|
+
objectKey: target.objectKey,
|
|
2043
|
+
parts: target.parts.map((p) => ({ partNumber: p.partNumber, etag: p.etag })),
|
|
2044
|
+
...hasText(target.locale) ? { locale: target.locale } : {}
|
|
2045
|
+
}
|
|
2046
|
+
});
|
|
2047
|
+
}
|
|
2048
|
+
/** Abort a multipart session, freeing staged parts. */
|
|
2049
|
+
async abortMultipart(target) {
|
|
2050
|
+
return this.httpClient.fetchPayload("/v1/{contentType}/{id}/file/multipart/abort", "post", {
|
|
2051
|
+
path: this.target,
|
|
2052
|
+
body: {
|
|
2053
|
+
uploadId: target.uploadId,
|
|
2054
|
+
objectKey: target.objectKey,
|
|
2055
|
+
...hasText(target.locale) ? { locale: target.locale } : {}
|
|
2056
|
+
}
|
|
2057
|
+
});
|
|
2058
|
+
}
|
|
2059
|
+
/**
|
|
2060
|
+
* Resolve a single gateway delivery URL for the requested locale/variant/purpose.
|
|
2061
|
+
* Throws if the binding is not `ready`.
|
|
2062
|
+
*/
|
|
2063
|
+
async url(input = {}) {
|
|
2064
|
+
const params = {};
|
|
2065
|
+
const locale = this.withLocale(input.locale);
|
|
2066
|
+
if (hasText(locale)) params.locale = locale;
|
|
2067
|
+
if (hasText(input.variantId)) params.variantId = input.variantId;
|
|
2068
|
+
if (input.purpose !== void 0) params.purpose = input.purpose;
|
|
2069
|
+
return this.httpClient.fetchPayload(
|
|
2070
|
+
"/v1/{contentType}/{id}/file/url",
|
|
2071
|
+
"get",
|
|
2072
|
+
{ path: this.target, query: params }
|
|
2073
|
+
);
|
|
2074
|
+
}
|
|
2075
|
+
/**
|
|
2076
|
+
* Update the binding's delivery visibility (token-based; no byte relocation —
|
|
2077
|
+
* a downgrade purges the gateway cache). Returns the refreshed asset entry.
|
|
2078
|
+
*/
|
|
2079
|
+
async updateVisibility(visibility, options = {}) {
|
|
2080
|
+
const locale = this.withLocale(options.locale);
|
|
2081
|
+
return this.httpClient.fetchPayload("/v1/{contentType}/{id}/file", "patch", {
|
|
2082
|
+
path: this.target,
|
|
2083
|
+
body: { visibility, ...hasText(locale) ? { locale } : {} }
|
|
2084
|
+
});
|
|
2085
|
+
}
|
|
2086
|
+
/**
|
|
2087
|
+
* Set the binding's normalized (0..1, origin top-left) smart-crop focal point
|
|
2088
|
+
* (plan 18) so `cover`/`crop` variants frame the subject. Per locale for
|
|
2089
|
+
* file-localized asset types; token-based (no byte change). Returns the
|
|
2090
|
+
* refreshed asset entry.
|
|
2091
|
+
*/
|
|
2092
|
+
async setFocalPoint(focalPoint, options = {}) {
|
|
2093
|
+
const locale = this.withLocale(options.locale);
|
|
2094
|
+
return this.httpClient.fetchPayload("/v1/{contentType}/{id}/file", "patch", {
|
|
2095
|
+
path: this.target,
|
|
2096
|
+
body: { focalPoint, ...hasText(locale) ? { locale } : {} }
|
|
2097
|
+
});
|
|
2098
|
+
}
|
|
2099
|
+
/**
|
|
2100
|
+
* Remove the file binding, clean up the underlying bytes, and purge the
|
|
2101
|
+
* gateway cache. The asset entry itself is preserved.
|
|
2102
|
+
*/
|
|
2103
|
+
async delete(options = {}) {
|
|
2104
|
+
const params = {};
|
|
2105
|
+
const locale = this.withLocale(options.locale);
|
|
2106
|
+
if (hasText(locale)) params.locale = locale;
|
|
2107
|
+
await this.httpClient.send("/v1/{contentType}/{id}/file", "delete", {
|
|
2108
|
+
path: this.target,
|
|
2109
|
+
query: params
|
|
2110
|
+
});
|
|
2111
|
+
}
|
|
2112
|
+
/**
|
|
2113
|
+
* Poll the asset entry until its file binding reaches `ready` (resolves with
|
|
2114
|
+
* the ready entry) or `failed` (throws). Times out after `timeoutMs`.
|
|
2115
|
+
*/
|
|
2116
|
+
async waitUntilReady(options = {}) {
|
|
2117
|
+
const timeoutMs = options.timeoutMs ?? 6e4;
|
|
2118
|
+
const pollIntervalMs = options.pollIntervalMs ?? 1500;
|
|
2119
|
+
const locale = this.withLocale(options.locale);
|
|
2120
|
+
const deadline = Date.now() + timeoutMs;
|
|
2121
|
+
for (; ; ) {
|
|
2122
|
+
const builder = new ContentByIdBuilder(
|
|
2123
|
+
this.httpClient,
|
|
2124
|
+
this.assetType,
|
|
2125
|
+
this.entryUid,
|
|
2126
|
+
locale
|
|
2127
|
+
);
|
|
2128
|
+
if (hasText(locale)) builder.locale(locale);
|
|
2129
|
+
const entry = await builder.execute();
|
|
2130
|
+
const status = entry?.file?.status;
|
|
2131
|
+
if (entry && status === "ready") return entry;
|
|
2132
|
+
if (status === "failed") {
|
|
2133
|
+
const message = entry?.file?.processing?.error?.message ?? "File processing failed";
|
|
2134
|
+
throw new Error(`Asset ${this.assetType}/${this.entryUid}: ${message}`);
|
|
2135
|
+
}
|
|
2136
|
+
if (Date.now() >= deadline) {
|
|
2137
|
+
throw new Error(
|
|
2138
|
+
`Timed out after ${timeoutMs}ms waiting for ${this.assetType}/${this.entryUid} (status: ${status ?? "unknown"})`
|
|
2139
|
+
);
|
|
2140
|
+
}
|
|
2141
|
+
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
2142
|
+
}
|
|
2143
|
+
}
|
|
2144
|
+
};
|
|
2145
|
+
chunkMWMSFIWN_cjs.__name(_AssetFileBuilder, "AssetFileBuilder");
|
|
2146
|
+
var AssetFileBuilder = _AssetFileBuilder;
|
|
2147
|
+
var _AssetUploadBuilder = class _AssetUploadBuilder {
|
|
2148
|
+
constructor(httpClient, assetType, file, defaultLocale) {
|
|
2149
|
+
this.httpClient = httpClient;
|
|
2150
|
+
this.assetType = assetType;
|
|
2151
|
+
this.file = file;
|
|
2152
|
+
this.defaultLocale = defaultLocale;
|
|
2153
|
+
chunkMWMSFIWN_cjs.__publicField(this, "_fileName");
|
|
2154
|
+
chunkMWMSFIWN_cjs.__publicField(this, "_visibility");
|
|
2155
|
+
chunkMWMSFIWN_cjs.__publicField(this, "_locale");
|
|
2156
|
+
chunkMWMSFIWN_cjs.__publicField(this, "_publish");
|
|
2157
|
+
chunkMWMSFIWN_cjs.__publicField(this, "_content", {});
|
|
2158
|
+
chunkMWMSFIWN_cjs.__publicField(this, "_slug");
|
|
2159
|
+
chunkMWMSFIWN_cjs.__publicField(this, "_waitForReady", false);
|
|
2160
|
+
chunkMWMSFIWN_cjs.__publicField(this, "_waitOptions", {});
|
|
2161
|
+
chunkMWMSFIWN_cjs.__publicField(this, "_onProgress");
|
|
2162
|
+
chunkMWMSFIWN_cjs.__publicField(this, "_validate");
|
|
2163
|
+
chunkMWMSFIWN_cjs.__publicField(this, "_maxPartRetries", 3);
|
|
2164
|
+
}
|
|
2165
|
+
/** Subscribe to client → storage upload progress (single PUT + multipart). */
|
|
2166
|
+
onProgress(callback) {
|
|
2167
|
+
this._onProgress = callback;
|
|
2168
|
+
return this;
|
|
2169
|
+
}
|
|
2170
|
+
/**
|
|
2171
|
+
* Validate the file client-side (UX only) against explicit constraints before
|
|
2172
|
+
* any bytes leave the machine. Rejects instantly with a {@link FileValidationError}.
|
|
2173
|
+
*/
|
|
2174
|
+
validate(constraints) {
|
|
2175
|
+
this._validate = constraints;
|
|
2176
|
+
return this;
|
|
2177
|
+
}
|
|
2178
|
+
/** Bounded per-part retry attempts for multipart (default 3). */
|
|
2179
|
+
maxPartRetries(attempts) {
|
|
2180
|
+
this._maxPartRetries = Math.max(0, attempts);
|
|
2181
|
+
return this;
|
|
2182
|
+
}
|
|
2183
|
+
/** Set the original file name (defaults to `file.name` for `File` inputs). */
|
|
2184
|
+
filename(name) {
|
|
2185
|
+
this._fileName = name;
|
|
2186
|
+
return this;
|
|
2187
|
+
}
|
|
2188
|
+
/** Set the initial delivery visibility (default `public`). */
|
|
2189
|
+
visibility(visibility) {
|
|
2190
|
+
this._visibility = visibility;
|
|
2191
|
+
return this;
|
|
2192
|
+
}
|
|
2193
|
+
/** Target locale for file-localized asset types. */
|
|
2194
|
+
locale(locale) {
|
|
2195
|
+
this._locale = locale;
|
|
2196
|
+
return this;
|
|
2197
|
+
}
|
|
2198
|
+
/** Suppress (or enable) auto-publish on ready. */
|
|
2199
|
+
publish(publish) {
|
|
2200
|
+
this._publish = publish;
|
|
2201
|
+
return this;
|
|
2202
|
+
}
|
|
2203
|
+
/** Set the asset's metadata field values (e.g. `{ alt: '...' }`). */
|
|
2204
|
+
content(content) {
|
|
2205
|
+
this._content = content;
|
|
2206
|
+
return this;
|
|
2207
|
+
}
|
|
2208
|
+
/** Set an explicit slug for the new entry. */
|
|
2209
|
+
slug(slug) {
|
|
2210
|
+
this._slug = slug;
|
|
2211
|
+
return this;
|
|
2212
|
+
}
|
|
2213
|
+
/** Wait until the file binding becomes `ready` before resolving. */
|
|
2214
|
+
waitForReady(options = true) {
|
|
2215
|
+
if (options === false) {
|
|
2216
|
+
this._waitForReady = false;
|
|
2217
|
+
} else {
|
|
2218
|
+
this._waitForReady = true;
|
|
2219
|
+
if (typeof options === "object") this._waitOptions = options;
|
|
2220
|
+
}
|
|
2221
|
+
return this;
|
|
2222
|
+
}
|
|
2223
|
+
/** Execute the full upload flow and resolve with the asset entry. */
|
|
2224
|
+
async execute() {
|
|
2225
|
+
const fileName = this._fileName ?? this.file.name;
|
|
2226
|
+
if (!fileName) {
|
|
2227
|
+
throw new Error('A file name is required: pass a File, or call .filename("name.ext")');
|
|
2228
|
+
}
|
|
2229
|
+
const locale = this._locale ?? this.defaultLocale;
|
|
2230
|
+
if (this._validate) {
|
|
2231
|
+
const violations = validateFile(this.file, this._validate);
|
|
2232
|
+
if (violations.length > 0) {
|
|
2233
|
+
throw new FileValidationError(
|
|
2234
|
+
`File validation failed: ${violations.map((v) => v.message).join("; ")}`,
|
|
2235
|
+
violations
|
|
2236
|
+
);
|
|
2237
|
+
}
|
|
2238
|
+
}
|
|
2239
|
+
const hash = await computeSha256Hex(this.file);
|
|
2240
|
+
const client = new AssetClient(this.httpClient, this.assetType, this.defaultLocale);
|
|
2241
|
+
const created = await client.create(this._content, {
|
|
2242
|
+
...hasText(this._slug) ? { slug: this._slug } : {},
|
|
2243
|
+
...hasText(locale) ? { locale } : {},
|
|
2244
|
+
fileName,
|
|
2245
|
+
...this._visibility !== void 0 ? { visibility: this._visibility } : {},
|
|
2246
|
+
...this._publish != null ? { publish: this._publish } : {}
|
|
2247
|
+
});
|
|
2248
|
+
const entryUid = created._metadata.id;
|
|
2249
|
+
const fileBuilder = client.file(entryUid);
|
|
2250
|
+
const presigned = await fileBuilder.presign({
|
|
2251
|
+
fileName,
|
|
2252
|
+
...hasText(this.file.type) ? { contentType: this.file.type } : {},
|
|
2253
|
+
...this.file.size > 0 ? { size: this.file.size } : {},
|
|
2254
|
+
...hasText(locale) ? { locale } : {},
|
|
2255
|
+
...hasText(hash) ? { hash } : {}
|
|
2256
|
+
});
|
|
2257
|
+
if (presigned.mode === "multipart") {
|
|
2258
|
+
await this.uploadMultipart(fileBuilder, presigned, entryUid, locale);
|
|
2259
|
+
} else {
|
|
2260
|
+
await this.uploadSingle(presigned, entryUid);
|
|
2261
|
+
}
|
|
2262
|
+
await fileBuilder.confirm({
|
|
2263
|
+
...hasText(locale) ? { locale } : {},
|
|
2264
|
+
...hasText(hash) ? { hash } : {}
|
|
2265
|
+
});
|
|
2266
|
+
if (this._waitForReady) {
|
|
2267
|
+
return fileBuilder.waitUntilReady({
|
|
2268
|
+
...this._waitOptions,
|
|
2269
|
+
...hasText(locale) ? { locale } : {}
|
|
2270
|
+
});
|
|
2271
|
+
}
|
|
2272
|
+
return created;
|
|
2273
|
+
}
|
|
2274
|
+
/** Single presigned PUT (≤ 50 MB / Stream POST). */
|
|
2275
|
+
async uploadSingle(presigned, entryUid) {
|
|
2276
|
+
if (!hasText(presigned.uploadUrl)) {
|
|
2277
|
+
throw new FileUploadError("INTERNAL", "Presign did not return an upload URL", {});
|
|
2278
|
+
}
|
|
2279
|
+
const total = this.file.size;
|
|
2280
|
+
this.emitProgress(0, total);
|
|
2281
|
+
const uploadResponse = await fetch(presigned.uploadUrl, {
|
|
2282
|
+
method: presigned.method,
|
|
2283
|
+
headers: presigned.headers,
|
|
2284
|
+
body: this.file
|
|
2285
|
+
});
|
|
2286
|
+
if (!uploadResponse.ok) {
|
|
2287
|
+
throw new FileUploadError(
|
|
2288
|
+
"INTERNAL",
|
|
2289
|
+
`Upload failed (${uploadResponse.status} ${uploadResponse.statusText}) for ${this.assetType}/${entryUid}`,
|
|
2290
|
+
{ retriable: uploadResponse.status >= 500, statusCode: uploadResponse.status }
|
|
2291
|
+
);
|
|
2292
|
+
}
|
|
2293
|
+
this.emitProgress(total, total);
|
|
2294
|
+
}
|
|
2295
|
+
/**
|
|
2296
|
+
* Resumable R2 multipart: slice into `partSize` chunks, skip already-uploaded
|
|
2297
|
+
* parts (resume via `list-parts`), PUT each remaining part with bounded retry,
|
|
2298
|
+
* then `complete`. Aborts the session on unrecoverable failure.
|
|
2299
|
+
*/
|
|
2300
|
+
async uploadMultipart(fileBuilder, presigned, entryUid, locale) {
|
|
2301
|
+
const { uploadId, objectKey, partSize } = presigned;
|
|
2302
|
+
if (!hasText(uploadId) || !hasText(objectKey) || partSize == null || partSize <= 0) {
|
|
2303
|
+
throw new FileUploadError("INTERNAL", "Incomplete multipart session from presign", {});
|
|
2304
|
+
}
|
|
2305
|
+
const target = { uploadId, objectKey, ...hasText(locale) ? { locale } : {} };
|
|
2306
|
+
const total = this.file.size;
|
|
2307
|
+
const partCount = Math.max(1, Math.ceil(total / partSize));
|
|
2308
|
+
const existing = /* @__PURE__ */ new Map();
|
|
2309
|
+
try {
|
|
2310
|
+
for (const part of await fileBuilder.listParts(target)) existing.set(part.partNumber, part);
|
|
2311
|
+
} catch {
|
|
2312
|
+
}
|
|
2313
|
+
const parts = [];
|
|
2314
|
+
let uploaded = [...existing.values()].reduce((sum, p) => sum + (p.size ?? 0), 0);
|
|
2315
|
+
this.emitProgress(uploaded, total);
|
|
2316
|
+
try {
|
|
2317
|
+
for (let partNumber = 1; partNumber <= partCount; partNumber += 1) {
|
|
2318
|
+
const already = existing.get(partNumber);
|
|
2319
|
+
if (already) {
|
|
2320
|
+
parts.push(already);
|
|
2321
|
+
continue;
|
|
2322
|
+
}
|
|
2323
|
+
const start = (partNumber - 1) * partSize;
|
|
2324
|
+
const blob = this.file.slice(start, Math.min(start + partSize, total));
|
|
2325
|
+
const etag = await this.putPartWithRetry(fileBuilder, target, partNumber, blob);
|
|
2326
|
+
parts.push({ partNumber, etag, size: blob.size });
|
|
2327
|
+
uploaded += blob.size;
|
|
2328
|
+
this.emitProgress(uploaded, total);
|
|
2329
|
+
}
|
|
2330
|
+
await fileBuilder.completeMultipart({ ...target, parts });
|
|
2331
|
+
} catch (error) {
|
|
2332
|
+
await fileBuilder.abortMultipart(target).catch(() => {
|
|
2333
|
+
});
|
|
2334
|
+
if (error instanceof FileUploadError) throw error;
|
|
2335
|
+
throw new FileUploadError(
|
|
2336
|
+
"INTERNAL",
|
|
2337
|
+
`Multipart upload failed for ${this.assetType}/${entryUid}: ${error instanceof Error ? error.message : String(error)}`,
|
|
2338
|
+
{ retriable: true, cause: error instanceof Error ? error : void 0 }
|
|
2339
|
+
);
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
/** PUT a single part, retrying transient failures up to `_maxPartRetries`. */
|
|
2343
|
+
async putPartWithRetry(fileBuilder, target, partNumber, blob) {
|
|
2344
|
+
let lastError;
|
|
2345
|
+
for (let attempt = 0; attempt <= this._maxPartRetries; attempt += 1) {
|
|
2346
|
+
try {
|
|
2347
|
+
const { url } = await fileBuilder.signPart({ ...target, partNumber });
|
|
2348
|
+
const response = await fetch(url, { method: "PUT", body: blob });
|
|
2349
|
+
if (!response.ok) {
|
|
2350
|
+
const retriable = response.status >= 500 || response.status === 408 || response.status === 429;
|
|
2351
|
+
if (!retriable) {
|
|
2352
|
+
throw new FileUploadError(
|
|
2353
|
+
"INTERNAL",
|
|
2354
|
+
`Part ${partNumber} upload failed (${response.status} ${response.statusText})`,
|
|
2355
|
+
{ statusCode: response.status }
|
|
2356
|
+
);
|
|
2357
|
+
}
|
|
2358
|
+
lastError = new Error(`part ${partNumber} -> ${response.status}`);
|
|
2359
|
+
} else {
|
|
2360
|
+
const etag = response.headers.get("etag") ?? response.headers.get("ETag");
|
|
2361
|
+
if (etag == null || etag === "") {
|
|
2362
|
+
throw new FileUploadError(
|
|
2363
|
+
"INTERNAL",
|
|
2364
|
+
`Part ${partNumber} response is missing an ETag header`,
|
|
2365
|
+
{}
|
|
2366
|
+
);
|
|
2367
|
+
}
|
|
2368
|
+
return etag;
|
|
2369
|
+
}
|
|
2370
|
+
} catch (error) {
|
|
2371
|
+
if (error instanceof FileUploadError && !error.retryable) throw error;
|
|
2372
|
+
lastError = error;
|
|
2373
|
+
}
|
|
2374
|
+
if (attempt < this._maxPartRetries) await new Promise((r) => setTimeout(r, 200 * 2 ** attempt));
|
|
2375
|
+
}
|
|
2376
|
+
throw new FileUploadError(
|
|
2377
|
+
"INTERNAL",
|
|
2378
|
+
`Part ${partNumber} failed after ${this._maxPartRetries + 1} attempts: ${lastError instanceof Error ? lastError.message : String(lastError)}`,
|
|
2379
|
+
{ retriable: true, cause: lastError instanceof Error ? lastError : void 0 }
|
|
2380
|
+
);
|
|
2381
|
+
}
|
|
2382
|
+
emitProgress(loaded, total) {
|
|
2383
|
+
if (!this._onProgress) return;
|
|
2384
|
+
const progress = total > 0 ? Math.max(0, Math.min(100, Math.round(loaded / total * 100))) : 0;
|
|
2385
|
+
this._onProgress({ phase: "upload", loaded, total, progress });
|
|
2386
|
+
}
|
|
2387
|
+
};
|
|
2388
|
+
chunkMWMSFIWN_cjs.__name(_AssetUploadBuilder, "AssetUploadBuilder");
|
|
2389
|
+
var AssetUploadBuilder = _AssetUploadBuilder;
|
|
2390
|
+
var _AssetClient = class _AssetClient {
|
|
2391
|
+
constructor(httpClient, assetType, defaultLocale) {
|
|
2392
|
+
this.httpClient = httpClient;
|
|
2393
|
+
this.assetType = assetType;
|
|
2394
|
+
this.defaultLocale = defaultLocale;
|
|
2395
|
+
}
|
|
2396
|
+
/** Create a query builder for this asset type. */
|
|
2397
|
+
query() {
|
|
2398
|
+
return new ContentQueryBuilder(
|
|
2399
|
+
this.httpClient,
|
|
2400
|
+
this.assetType,
|
|
2401
|
+
this.defaultLocale
|
|
2402
|
+
);
|
|
2403
|
+
}
|
|
2404
|
+
/** Find an asset entry by id (or slug) with fluent options. */
|
|
2405
|
+
findById(id) {
|
|
2406
|
+
return new ContentByIdBuilder(
|
|
2407
|
+
this.httpClient,
|
|
2408
|
+
this.assetType,
|
|
2409
|
+
id,
|
|
2410
|
+
this.defaultLocale
|
|
2411
|
+
);
|
|
2412
|
+
}
|
|
2413
|
+
/** Fetch a single asset entry by id (or slug), or `null` if not found. */
|
|
2414
|
+
async fetchOne(id, options) {
|
|
2415
|
+
const builder = this.findById(id);
|
|
2416
|
+
if (options?.include != null) builder.include(options.include);
|
|
2417
|
+
if (options?.locale != null && options.locale !== "") builder.locale(options.locale);
|
|
2418
|
+
return builder.execute();
|
|
2419
|
+
}
|
|
2420
|
+
/** Fetch the first asset entry matching the conditions, or `null`. */
|
|
2421
|
+
async fetchFirst(where, locale) {
|
|
2422
|
+
const builder = this.query();
|
|
2423
|
+
if (where != null) builder.where(where);
|
|
2424
|
+
if (locale != null && locale !== "") builder.locale(locale);
|
|
2425
|
+
return builder.first();
|
|
2426
|
+
}
|
|
2427
|
+
/**
|
|
2428
|
+
* Create a new asset entry as a draft. The binary is uploaded separately via
|
|
2429
|
+
* `file(uid)` (or use the high-level `upload()` helper), and the entry
|
|
2430
|
+
* auto-publishes when its file becomes ready (unless `publish: false`).
|
|
2431
|
+
*/
|
|
2432
|
+
async create(data, options) {
|
|
2433
|
+
const { idempotencyKey, ...rest } = options ?? {};
|
|
2434
|
+
return this.httpClient.fetchPayload(
|
|
2435
|
+
"/v1/{contentType}",
|
|
2436
|
+
"post",
|
|
2437
|
+
{
|
|
2438
|
+
path: { contentType: this.assetType },
|
|
2439
|
+
body: { content: data, ...rest },
|
|
2440
|
+
headers: writeHeaders({ idempotencyKey })
|
|
2441
|
+
}
|
|
2442
|
+
);
|
|
2443
|
+
}
|
|
2444
|
+
/** Update an asset entry's metadata fields (and/or slug). */
|
|
2445
|
+
async update(id, data, options) {
|
|
2446
|
+
const { idempotencyKey, ifMatch, ...rest } = options ?? {};
|
|
2447
|
+
return this.httpClient.fetchPayload(
|
|
2448
|
+
"/v1/{contentType}/{id}",
|
|
2449
|
+
"patch",
|
|
2450
|
+
{
|
|
2451
|
+
path: { contentType: this.assetType, id },
|
|
2452
|
+
body: { content: data, ...rest },
|
|
2453
|
+
headers: writeHeaders({ idempotencyKey, ifMatch })
|
|
2454
|
+
}
|
|
2455
|
+
);
|
|
2456
|
+
}
|
|
2457
|
+
/** Delete an asset entry (cascades file-byte cleanup + gateway purge). */
|
|
2458
|
+
async delete(id, options) {
|
|
2459
|
+
await this.httpClient.send("/v1/{contentType}/{id}", "delete", {
|
|
2460
|
+
path: { contentType: this.assetType, id },
|
|
2461
|
+
headers: writeHeaders(options)
|
|
2462
|
+
});
|
|
2463
|
+
}
|
|
2464
|
+
/** Get the file sub-resource builder for a specific asset entry. */
|
|
2465
|
+
file(entryUid) {
|
|
2466
|
+
return new AssetFileBuilder(this.httpClient, this.assetType, entryUid, this.defaultLocale);
|
|
2467
|
+
}
|
|
2468
|
+
/** Start a high-level upload (create → presign → PUT → confirm). */
|
|
2469
|
+
upload(file) {
|
|
2470
|
+
return new AssetUploadBuilder(this.httpClient, this.assetType, file, this.defaultLocale);
|
|
2471
|
+
}
|
|
2472
|
+
/** Get the asset type id. */
|
|
2473
|
+
getAssetType() {
|
|
2474
|
+
return this.assetType;
|
|
2475
|
+
}
|
|
2476
|
+
/** Get the current default locale. */
|
|
2477
|
+
getDefaultLocale() {
|
|
2478
|
+
return this.defaultLocale;
|
|
2479
|
+
}
|
|
2480
|
+
/** Create a new AssetClient for this type with a different default locale. */
|
|
2481
|
+
withLocale(locale) {
|
|
2482
|
+
return new _AssetClient(this.httpClient, this.assetType, locale);
|
|
2483
|
+
}
|
|
2484
|
+
};
|
|
2485
|
+
chunkMWMSFIWN_cjs.__name(_AssetClient, "AssetClient");
|
|
2486
|
+
var AssetClient = _AssetClient;
|
|
2487
|
+
function isAssetEntry(entry) {
|
|
2488
|
+
if (entry == null || typeof entry !== "object") return false;
|
|
2489
|
+
const metadata = entry._metadata;
|
|
2490
|
+
return metadata?.entryType === "asset";
|
|
2491
|
+
}
|
|
2492
|
+
chunkMWMSFIWN_cjs.__name(isAssetEntry, "isAssetEntry");
|
|
2493
|
+
|
|
2494
|
+
// src/content/singleton-client.ts
|
|
2495
|
+
var _SingletonClient = class _SingletonClient {
|
|
2496
|
+
constructor(httpClient, singletonKey) {
|
|
2497
|
+
this.httpClient = httpClient;
|
|
2498
|
+
this.singletonKey = singletonKey;
|
|
2499
|
+
chunkMWMSFIWN_cjs.__publicField(this, "includeFields");
|
|
2500
|
+
chunkMWMSFIWN_cjs.__publicField(this, "localeValue");
|
|
2501
|
+
}
|
|
2502
|
+
/**
|
|
2503
|
+
* Include reference fields in the response
|
|
2504
|
+
* @param fields - Reference field names to populate
|
|
2505
|
+
*/
|
|
2506
|
+
include(fields) {
|
|
2507
|
+
this.includeFields = fields;
|
|
2508
|
+
return this;
|
|
2509
|
+
}
|
|
2510
|
+
/**
|
|
2511
|
+
* Set locale for localized content
|
|
2512
|
+
* @param locale - Locale code (e.g., 'en', 'de', 'fr')
|
|
2513
|
+
*/
|
|
2514
|
+
locale(locale) {
|
|
2515
|
+
this.localeValue = locale;
|
|
2516
|
+
return this;
|
|
2517
|
+
}
|
|
2518
|
+
/**
|
|
2519
|
+
* Fetch the singleton content
|
|
2520
|
+
* @param options - Optional fetch options (alternative to fluent methods)
|
|
2521
|
+
*/
|
|
2522
|
+
async fetch(options) {
|
|
2523
|
+
const includeValue = options?.include ?? this.includeFields;
|
|
2524
|
+
options?.locale ?? this.localeValue;
|
|
2525
|
+
if (includeValue) {
|
|
2526
|
+
includeValue.join(",");
|
|
2527
|
+
}
|
|
2528
|
+
try {
|
|
2529
|
+
return (await this.fetchWithTag(options)).content;
|
|
2530
|
+
} catch (error) {
|
|
2531
|
+
if (isResourceNotFound(error)) return null;
|
|
2532
|
+
throw error;
|
|
2533
|
+
}
|
|
2534
|
+
}
|
|
2535
|
+
/**
|
|
2536
|
+
* Fetch the singleton **and the entity tag that read served**, which is what
|
|
2537
|
+
* `update`'s `ifMatch` takes (`INV-API-CONDITIONAL-WRITE-IS-REACHABLE`).
|
|
2538
|
+
*
|
|
2539
|
+
* `entityTag` is absent on a cache HIT: a cached body cannot know the current
|
|
2540
|
+
* version, and a stale tag would refuse the very next write.
|
|
2541
|
+
*/
|
|
2542
|
+
async fetchWithTag(options) {
|
|
2543
|
+
const query = {};
|
|
2544
|
+
const includeValue = options?.include ?? this.includeFields;
|
|
2545
|
+
const localeValueFinal = options?.locale ?? this.localeValue;
|
|
2546
|
+
if (includeValue) query.include = includeValue.join(",");
|
|
2547
|
+
if (localeValueFinal != null && localeValueFinal !== "") query.locale = localeValueFinal;
|
|
2548
|
+
const response = await this.httpClient.send(
|
|
2549
|
+
"/v1/singletons/{key}",
|
|
2550
|
+
"get",
|
|
2551
|
+
{ path: { key: this.singletonKey }, query }
|
|
2552
|
+
);
|
|
2553
|
+
return { content: readPayload(response.body), entityTag: entityTagOf(response) };
|
|
2554
|
+
}
|
|
2555
|
+
/**
|
|
2556
|
+
* Update singleton content. The API auto-publishes on PATCH — there is no
|
|
2557
|
+
* separate publish route; calling `update()` writes and publishes.
|
|
2558
|
+
* @param data - The content fields to update
|
|
2559
|
+
* @param locale - Optional locale for localized content (overrides fluent locale)
|
|
2560
|
+
* @param options - `If-Match` / `Idempotency-Key`, both declared on this route
|
|
2561
|
+
*/
|
|
2562
|
+
async update(data, locale, options) {
|
|
2563
|
+
const finalLocale = locale ?? this.localeValue;
|
|
2564
|
+
return this.httpClient.fetchPayload(
|
|
2565
|
+
"/v1/singletons/{key}",
|
|
2566
|
+
"patch",
|
|
2567
|
+
{
|
|
2568
|
+
path: { key: this.singletonKey },
|
|
2569
|
+
body: {
|
|
2570
|
+
content: data,
|
|
2571
|
+
...finalLocale != null && finalLocale !== "" && { locale: finalLocale }
|
|
2572
|
+
},
|
|
2573
|
+
headers: writeHeaders(options)
|
|
2574
|
+
}
|
|
2575
|
+
);
|
|
2576
|
+
}
|
|
2577
|
+
/**
|
|
2578
|
+
* Check if singleton exists
|
|
2579
|
+
*/
|
|
2580
|
+
async exists() {
|
|
2581
|
+
try {
|
|
2582
|
+
const result = await this.fetch();
|
|
2583
|
+
return result !== null;
|
|
2584
|
+
} catch {
|
|
2585
|
+
return false;
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2588
|
+
/**
|
|
2589
|
+
* Get version history for one locale of the singleton.
|
|
2590
|
+
*
|
|
2591
|
+
* Versions are numbered per locale, so this is one locale's timeline: `locale` defaults to the
|
|
2592
|
+
* schema default, no fallback is applied, and a locale with no history returns an empty array
|
|
2593
|
+
* rather than another locale's rows. Every row names the locale it belongs to.
|
|
2594
|
+
*/
|
|
2595
|
+
async versions(options) {
|
|
2596
|
+
const payload = await this.httpClient.fetchPayload("/v1/singletons/{key}/versions", "get", {
|
|
2597
|
+
path: { key: this.singletonKey },
|
|
2598
|
+
query: {
|
|
2599
|
+
limit: options?.limit ?? 10,
|
|
2600
|
+
...options?.locale === void 0 ? {} : { locale: options.locale }
|
|
2601
|
+
}
|
|
2602
|
+
});
|
|
2603
|
+
return payload.versions;
|
|
2604
|
+
}
|
|
2605
|
+
/**
|
|
2606
|
+
* Get a specific version of the singleton.
|
|
2607
|
+
*
|
|
2608
|
+
* `locale` defaults to the schema default. Version numbers are per locale, so version 2 is
|
|
2609
|
+
* different content in each one.
|
|
2610
|
+
*/
|
|
2611
|
+
async version(versionNumber, options) {
|
|
2612
|
+
try {
|
|
2613
|
+
return await this.httpClient.fetchPayload("/v1/singletons/{key}/versions/{versionNumber}", "get", {
|
|
2614
|
+
path: { key: this.singletonKey, versionNumber: String(versionNumber) },
|
|
2615
|
+
query: options?.locale === void 0 ? void 0 : { locale: options.locale }
|
|
2616
|
+
});
|
|
2617
|
+
} catch (error) {
|
|
2618
|
+
if (isResourceNotFound(error)) return null;
|
|
2619
|
+
throw error;
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2622
|
+
/**
|
|
2623
|
+
* Get the singleton key
|
|
2624
|
+
*/
|
|
2625
|
+
getSingletonKey() {
|
|
2626
|
+
return this.singletonKey;
|
|
2627
|
+
}
|
|
2628
|
+
/**
|
|
2629
|
+
* Reset fluent options for reuse
|
|
2630
|
+
*/
|
|
2631
|
+
reset() {
|
|
2632
|
+
this.includeFields = void 0;
|
|
2633
|
+
this.localeValue = void 0;
|
|
2634
|
+
return this;
|
|
2635
|
+
}
|
|
2636
|
+
};
|
|
2637
|
+
chunkMWMSFIWN_cjs.__name(_SingletonClient, "SingletonClient");
|
|
2638
|
+
var SingletonClient = _SingletonClient;
|
|
2639
|
+
|
|
2640
|
+
// src/content/settings-client.ts
|
|
2641
|
+
var _SettingsClient = class _SettingsClient {
|
|
2642
|
+
constructor(httpClient, groupId) {
|
|
2643
|
+
this.httpClient = httpClient;
|
|
2644
|
+
this.groupId = groupId;
|
|
2645
|
+
chunkMWMSFIWN_cjs.__publicField(this, "includeFields");
|
|
2646
|
+
chunkMWMSFIWN_cjs.__publicField(this, "localeValue");
|
|
2647
|
+
}
|
|
2648
|
+
/**
|
|
2649
|
+
* Include reference fields in the response
|
|
2650
|
+
* @param fields - Reference field names to populate
|
|
2651
|
+
*/
|
|
2652
|
+
include(fields) {
|
|
2653
|
+
this.includeFields = fields;
|
|
2654
|
+
return this;
|
|
2655
|
+
}
|
|
2656
|
+
/**
|
|
2657
|
+
* Set locale for localized settings
|
|
2658
|
+
* @param locale - Locale code (e.g., 'en', 'de', 'fr')
|
|
2659
|
+
*/
|
|
2660
|
+
locale(locale) {
|
|
2661
|
+
this.localeValue = locale;
|
|
2662
|
+
return this;
|
|
2663
|
+
}
|
|
2664
|
+
/**
|
|
2665
|
+
* Fetch the settings group
|
|
2666
|
+
* @param options - Optional fetch options (alternative to fluent methods)
|
|
2667
|
+
*/
|
|
2668
|
+
async fetch(options) {
|
|
2669
|
+
const includeValue = options?.include ?? this.includeFields;
|
|
2670
|
+
options?.locale ?? this.localeValue;
|
|
2671
|
+
if (includeValue) {
|
|
2672
|
+
includeValue.join(",");
|
|
2673
|
+
}
|
|
2674
|
+
try {
|
|
2675
|
+
return (await this.fetchWithTag(options)).settings;
|
|
2676
|
+
} catch (error) {
|
|
2677
|
+
if (isResourceNotFound(error)) return null;
|
|
2678
|
+
throw error;
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
/**
|
|
2682
|
+
* Update settings group
|
|
2683
|
+
* @param settings - The settings fields to update
|
|
2684
|
+
* @param locale - Optional locale for localized settings (overrides fluent locale)
|
|
2685
|
+
*/
|
|
2686
|
+
async update(settings, locale, options) {
|
|
2687
|
+
const finalLocale = locale ?? this.localeValue;
|
|
2688
|
+
const body = finalLocale != null && finalLocale !== "" ? { ...settings, locale: finalLocale } : settings;
|
|
2689
|
+
return this.httpClient.fetchPayload(
|
|
2690
|
+
"/v1/settings/{groupId}",
|
|
2691
|
+
"patch",
|
|
2692
|
+
{ path: { groupId: this.groupId }, body, headers: writeHeaders(options) }
|
|
2693
|
+
);
|
|
2694
|
+
}
|
|
2695
|
+
/**
|
|
2696
|
+
* Fetch the group **and the entity tag that read served**, which is what
|
|
2697
|
+
* `update`'s and `reset`'s `ifMatch` take
|
|
2698
|
+
* (`INV-API-CONDITIONAL-WRITE-IS-REACHABLE`).
|
|
2699
|
+
*/
|
|
2700
|
+
async fetchWithTag(options) {
|
|
2701
|
+
const query = {};
|
|
2702
|
+
const includeValue = options?.include ?? this.includeFields;
|
|
2703
|
+
const localeValueFinal = options?.locale ?? this.localeValue;
|
|
2704
|
+
if (includeValue) query.include = includeValue.join(",");
|
|
2705
|
+
if (localeValueFinal != null && localeValueFinal !== "") query.locale = localeValueFinal;
|
|
2706
|
+
const response = await this.httpClient.send(
|
|
2707
|
+
"/v1/settings/{groupId}",
|
|
2708
|
+
"get",
|
|
2709
|
+
{ path: { groupId: this.groupId }, query }
|
|
2710
|
+
);
|
|
2711
|
+
return { settings: readPayload(response.body), entityTag: entityTagOf(response) };
|
|
2712
|
+
}
|
|
2713
|
+
/**
|
|
2714
|
+
* Reset settings to default values
|
|
2715
|
+
* @param locale - Optional locale for localized settings
|
|
2716
|
+
*/
|
|
2717
|
+
async reset(locale, options) {
|
|
2718
|
+
const finalLocale = locale ?? this.localeValue;
|
|
2719
|
+
return this.httpClient.fetchPayload(
|
|
2720
|
+
"/v1/settings/{groupId}/reset",
|
|
2721
|
+
"post",
|
|
2722
|
+
{
|
|
2723
|
+
path: { groupId: this.groupId },
|
|
2724
|
+
body: finalLocale != null && finalLocale !== "" ? { locale: finalLocale } : void 0,
|
|
2725
|
+
headers: writeHeaders(options)
|
|
2726
|
+
}
|
|
2727
|
+
);
|
|
2728
|
+
}
|
|
2729
|
+
/**
|
|
2730
|
+
* Get default settings for this group
|
|
2731
|
+
*/
|
|
2732
|
+
async getDefaults() {
|
|
2733
|
+
return this.httpClient.fetchPayload(
|
|
2734
|
+
"/v1/settings/{groupId}/defaults",
|
|
2735
|
+
"get",
|
|
2736
|
+
{ path: { groupId: this.groupId } }
|
|
2737
|
+
);
|
|
2738
|
+
}
|
|
2739
|
+
/**
|
|
2740
|
+
* Check if settings group exists
|
|
2741
|
+
*/
|
|
2742
|
+
async exists() {
|
|
2743
|
+
try {
|
|
2744
|
+
const result = await this.fetch();
|
|
2745
|
+
return result !== null;
|
|
2746
|
+
} catch (error) {
|
|
2747
|
+
if (isResourceNotFound(error)) return false;
|
|
2748
|
+
throw error;
|
|
2749
|
+
}
|
|
2750
|
+
}
|
|
2751
|
+
/**
|
|
2752
|
+
* Get version history for one locale of the settings group.
|
|
2753
|
+
*
|
|
2754
|
+
* Versions are numbered per locale, so this is one locale's timeline: `locale` defaults to the
|
|
2755
|
+
* schema default, no fallback is applied, and a locale with no history returns an empty array
|
|
2756
|
+
* rather than another locale's rows. Every row names the locale it belongs to.
|
|
2757
|
+
*/
|
|
2758
|
+
async versions(options) {
|
|
2759
|
+
const payload = await this.httpClient.fetchPayload("/v1/settings/{groupId}/versions", "get", {
|
|
2760
|
+
path: { groupId: this.groupId },
|
|
2761
|
+
query: {
|
|
2762
|
+
limit: options?.limit ?? 10,
|
|
2763
|
+
...options?.locale === void 0 ? {} : { locale: options.locale }
|
|
2764
|
+
}
|
|
2765
|
+
});
|
|
2766
|
+
return payload.versions;
|
|
2767
|
+
}
|
|
2768
|
+
/**
|
|
2769
|
+
* Get a specific version of the settings.
|
|
2770
|
+
*
|
|
2771
|
+
* `locale` defaults to the schema default. Version numbers are per locale, so version 2 is
|
|
2772
|
+
* different content in each one.
|
|
2773
|
+
*/
|
|
2774
|
+
async version(versionNumber, options) {
|
|
2775
|
+
try {
|
|
2776
|
+
return await this.httpClient.fetchPayload("/v1/settings/{groupId}/versions/{versionNumber}", "get", {
|
|
2777
|
+
path: { groupId: this.groupId, versionNumber: String(versionNumber) },
|
|
2778
|
+
query: options?.locale === void 0 ? void 0 : { locale: options.locale }
|
|
2779
|
+
});
|
|
2780
|
+
} catch (error) {
|
|
2781
|
+
if (isResourceNotFound(error)) return null;
|
|
2782
|
+
throw error;
|
|
2783
|
+
}
|
|
2784
|
+
}
|
|
2785
|
+
/**
|
|
2786
|
+
* Get the settings group ID
|
|
2787
|
+
*/
|
|
2788
|
+
getGroupId() {
|
|
2789
|
+
return this.groupId;
|
|
2790
|
+
}
|
|
2791
|
+
/**
|
|
2792
|
+
* Reset fluent options for reuse
|
|
2793
|
+
*/
|
|
2794
|
+
resetOptions() {
|
|
2795
|
+
this.includeFields = void 0;
|
|
2796
|
+
this.localeValue = void 0;
|
|
2797
|
+
return this;
|
|
2798
|
+
}
|
|
2799
|
+
};
|
|
2800
|
+
chunkMWMSFIWN_cjs.__name(_SettingsClient, "SettingsClient");
|
|
2801
|
+
var SettingsClient = _SettingsClient;
|
|
2802
|
+
|
|
2803
|
+
// src/search/search-client.ts
|
|
2804
|
+
var _SearchBuilder = class _SearchBuilder {
|
|
2805
|
+
constructor(httpClient, query) {
|
|
2806
|
+
this.httpClient = httpClient;
|
|
2807
|
+
chunkMWMSFIWN_cjs.__publicField(this, "query");
|
|
2808
|
+
chunkMWMSFIWN_cjs.__publicField(this, "searchOptions", {});
|
|
2809
|
+
this.query = query;
|
|
2810
|
+
}
|
|
2811
|
+
/**
|
|
2812
|
+
* Search within a single content type
|
|
2813
|
+
* @param contentType - Content type to search in
|
|
2814
|
+
*/
|
|
2815
|
+
in(contentType) {
|
|
2816
|
+
this.searchOptions.contentType = contentType;
|
|
2817
|
+
this.searchOptions.contentTypes = void 0;
|
|
2818
|
+
return this;
|
|
2819
|
+
}
|
|
2820
|
+
/**
|
|
2821
|
+
* Search across multiple content types
|
|
2822
|
+
* @param contentTypes - Array of content types or '*' for all
|
|
2823
|
+
*/
|
|
2824
|
+
across(contentTypes) {
|
|
2825
|
+
this.searchOptions.contentTypes = contentTypes;
|
|
2826
|
+
this.searchOptions.contentType = void 0;
|
|
2827
|
+
return this;
|
|
2828
|
+
}
|
|
2829
|
+
/**
|
|
2830
|
+
* Add filter conditions
|
|
2831
|
+
* @param conditions - Filter conditions
|
|
2832
|
+
*/
|
|
2833
|
+
where(conditions) {
|
|
2834
|
+
this.searchOptions.where = {
|
|
2835
|
+
...this.searchOptions.where,
|
|
2836
|
+
...conditions
|
|
2837
|
+
};
|
|
2838
|
+
return this;
|
|
2839
|
+
}
|
|
2840
|
+
/**
|
|
2841
|
+
* Specify which fields to search in
|
|
2842
|
+
* @param fields - Field names to search
|
|
2843
|
+
*/
|
|
2844
|
+
searchFields(fields) {
|
|
2845
|
+
this.searchOptions.searchFields = fields;
|
|
2846
|
+
return this;
|
|
2847
|
+
}
|
|
2848
|
+
/**
|
|
2849
|
+
* Project specific fields in results
|
|
2850
|
+
* @param projection - Field mapping
|
|
2851
|
+
*/
|
|
2852
|
+
fields(projection) {
|
|
2853
|
+
this.searchOptions.fields = projection;
|
|
2854
|
+
return this;
|
|
2855
|
+
}
|
|
2856
|
+
/**
|
|
2857
|
+
* Limit number of results
|
|
2858
|
+
* @param count - Maximum results
|
|
2859
|
+
*/
|
|
2860
|
+
limit(count) {
|
|
2861
|
+
this.searchOptions.limit = count;
|
|
2862
|
+
return this;
|
|
2863
|
+
}
|
|
2864
|
+
/**
|
|
2865
|
+
* Skip results for pagination
|
|
2866
|
+
* @param count - Number to skip
|
|
2867
|
+
*/
|
|
2868
|
+
offset(count) {
|
|
2869
|
+
this.searchOptions.offset = count;
|
|
2870
|
+
return this;
|
|
2871
|
+
}
|
|
2872
|
+
/**
|
|
2873
|
+
* Include relevance scores in results
|
|
2874
|
+
*/
|
|
2875
|
+
includeScore() {
|
|
2876
|
+
this.searchOptions.includeScore = true;
|
|
2877
|
+
return this;
|
|
2878
|
+
}
|
|
2879
|
+
/**
|
|
2880
|
+
* Include score breakdown explanation
|
|
2881
|
+
*/
|
|
2882
|
+
explain() {
|
|
2883
|
+
this.searchOptions.explain = true;
|
|
2884
|
+
return this;
|
|
2885
|
+
}
|
|
2886
|
+
/**
|
|
2887
|
+
* Set locale for localized content
|
|
2888
|
+
* @param locale - Locale code
|
|
2889
|
+
*/
|
|
2890
|
+
locale(locale) {
|
|
2891
|
+
this.searchOptions.locale = locale;
|
|
2892
|
+
return this;
|
|
2893
|
+
}
|
|
2894
|
+
/**
|
|
2895
|
+
* Execute the search query
|
|
2896
|
+
*/
|
|
2897
|
+
async execute() {
|
|
2898
|
+
const body = {
|
|
2899
|
+
q: this.query
|
|
2900
|
+
};
|
|
2901
|
+
if (this.searchOptions.contentType != null && this.searchOptions.contentType !== "") {
|
|
2902
|
+
body.contentType = this.searchOptions.contentType;
|
|
2903
|
+
}
|
|
2904
|
+
if (this.searchOptions.contentTypes != null) {
|
|
2905
|
+
body.contentTypes = this.searchOptions.contentTypes;
|
|
2906
|
+
}
|
|
2907
|
+
if (this.searchOptions.where && Object.keys(this.searchOptions.where).length > 0) {
|
|
2908
|
+
body.where = this.searchOptions.where;
|
|
2909
|
+
}
|
|
2910
|
+
if (this.searchOptions.searchFields) {
|
|
2911
|
+
body.searchFields = this.searchOptions.searchFields;
|
|
2912
|
+
}
|
|
2913
|
+
if (this.searchOptions.fields) {
|
|
2914
|
+
body.fields = this.searchOptions.fields;
|
|
2915
|
+
}
|
|
2916
|
+
if (this.searchOptions.limit !== void 0) {
|
|
2917
|
+
body.limit = this.searchOptions.limit;
|
|
2918
|
+
}
|
|
2919
|
+
if (this.searchOptions.offset !== void 0) {
|
|
2920
|
+
body.offset = this.searchOptions.offset;
|
|
2921
|
+
}
|
|
2922
|
+
if (this.searchOptions.includeScore === true) {
|
|
2923
|
+
body.includeScore = true;
|
|
2924
|
+
}
|
|
2925
|
+
if (this.searchOptions.explain === true) {
|
|
2926
|
+
body.explain = true;
|
|
2927
|
+
}
|
|
2928
|
+
if (this.searchOptions.locale != null && this.searchOptions.locale !== "") {
|
|
2929
|
+
body.locale = this.searchOptions.locale;
|
|
2930
|
+
}
|
|
2931
|
+
const response = await this.httpClient.send(
|
|
2932
|
+
"/v1/search",
|
|
2933
|
+
"post",
|
|
2934
|
+
{ body }
|
|
2935
|
+
);
|
|
2936
|
+
return response.body;
|
|
2937
|
+
}
|
|
2938
|
+
/**
|
|
2939
|
+
* Execute and return just the items (convenience method)
|
|
2940
|
+
*/
|
|
2941
|
+
async items() {
|
|
2942
|
+
return readPayload(await this.execute());
|
|
2943
|
+
}
|
|
2944
|
+
/**
|
|
2945
|
+
* Get the first result
|
|
2946
|
+
*/
|
|
2947
|
+
async first() {
|
|
2948
|
+
return readPayload(await this.limit(1).execute())[0] ?? null;
|
|
2949
|
+
}
|
|
2950
|
+
};
|
|
2951
|
+
chunkMWMSFIWN_cjs.__name(_SearchBuilder, "SearchBuilder");
|
|
2952
|
+
var SearchBuilder = _SearchBuilder;
|
|
2953
|
+
var _AutocompleteBuilder = class _AutocompleteBuilder {
|
|
2954
|
+
constructor(httpClient, query) {
|
|
2955
|
+
this.httpClient = httpClient;
|
|
2956
|
+
chunkMWMSFIWN_cjs.__publicField(this, "query");
|
|
2957
|
+
chunkMWMSFIWN_cjs.__publicField(this, "autocompleteOptions", {});
|
|
2958
|
+
this.query = query;
|
|
2959
|
+
}
|
|
2960
|
+
/**
|
|
2961
|
+
* Autocomplete within a content type
|
|
2962
|
+
* @param contentType - Content type to search
|
|
2963
|
+
*/
|
|
2964
|
+
in(contentType) {
|
|
2965
|
+
this.autocompleteOptions.contentType = contentType;
|
|
2966
|
+
return this;
|
|
2967
|
+
}
|
|
2968
|
+
/**
|
|
2969
|
+
* Field to autocomplete on
|
|
2970
|
+
* @param fieldName - Field name (supports nested like 'category.name')
|
|
2971
|
+
*/
|
|
2972
|
+
field(fieldName) {
|
|
2973
|
+
this.autocompleteOptions.field = fieldName;
|
|
2974
|
+
return this;
|
|
2975
|
+
}
|
|
2976
|
+
/**
|
|
2977
|
+
* Enable fuzzy matching for typo tolerance
|
|
2978
|
+
*/
|
|
2979
|
+
fuzzy() {
|
|
2980
|
+
this.autocompleteOptions.fuzzy = true;
|
|
2981
|
+
return this;
|
|
2982
|
+
}
|
|
2983
|
+
/**
|
|
2984
|
+
* Add filter conditions
|
|
2985
|
+
* @param conditions - Filter conditions
|
|
2986
|
+
*/
|
|
2987
|
+
where(conditions) {
|
|
2988
|
+
this.autocompleteOptions.where = {
|
|
2989
|
+
...this.autocompleteOptions.where,
|
|
2990
|
+
...conditions
|
|
2991
|
+
};
|
|
2992
|
+
return this;
|
|
2993
|
+
}
|
|
2994
|
+
/**
|
|
2995
|
+
* Limit number of suggestions
|
|
2996
|
+
* @param count - Maximum suggestions
|
|
2997
|
+
*/
|
|
2998
|
+
limit(count) {
|
|
2999
|
+
this.autocompleteOptions.limit = count;
|
|
3000
|
+
return this;
|
|
3001
|
+
}
|
|
3002
|
+
/**
|
|
3003
|
+
* Set locale for localized content
|
|
3004
|
+
* @param locale - Locale code
|
|
3005
|
+
*/
|
|
3006
|
+
locale(locale) {
|
|
3007
|
+
this.autocompleteOptions.locale = locale;
|
|
3008
|
+
return this;
|
|
3009
|
+
}
|
|
3010
|
+
/**
|
|
3011
|
+
* Execute the autocomplete query
|
|
3012
|
+
*/
|
|
3013
|
+
async execute() {
|
|
3014
|
+
if (this.autocompleteOptions.contentType == null || this.autocompleteOptions.contentType === "") {
|
|
3015
|
+
throw new Error('Content type is required for autocomplete. Use .in("contentType")');
|
|
3016
|
+
}
|
|
3017
|
+
if (this.autocompleteOptions.field == null || this.autocompleteOptions.field === "") {
|
|
3018
|
+
throw new Error('Field is required for autocomplete. Use .field("fieldName")');
|
|
3019
|
+
}
|
|
3020
|
+
const body = {
|
|
3021
|
+
q: this.query,
|
|
3022
|
+
contentType: this.autocompleteOptions.contentType,
|
|
3023
|
+
field: this.autocompleteOptions.field
|
|
3024
|
+
};
|
|
3025
|
+
if (this.autocompleteOptions.fuzzy === true) {
|
|
3026
|
+
body.fuzzy = true;
|
|
3027
|
+
}
|
|
3028
|
+
if (this.autocompleteOptions.where && Object.keys(this.autocompleteOptions.where).length > 0) {
|
|
3029
|
+
body.where = this.autocompleteOptions.where;
|
|
3030
|
+
}
|
|
3031
|
+
if (this.autocompleteOptions.limit !== void 0) {
|
|
3032
|
+
body.limit = this.autocompleteOptions.limit;
|
|
3033
|
+
}
|
|
3034
|
+
if (this.autocompleteOptions.locale != null && this.autocompleteOptions.locale !== "") {
|
|
3035
|
+
body.locale = this.autocompleteOptions.locale;
|
|
3036
|
+
}
|
|
3037
|
+
const response = await this.httpClient.send("/v1/search/autocomplete", "post", { body });
|
|
3038
|
+
return response.body;
|
|
3039
|
+
}
|
|
3040
|
+
/**
|
|
3041
|
+
* Get just the suggestion texts
|
|
3042
|
+
*/
|
|
3043
|
+
async texts() {
|
|
3044
|
+
return readPayload(await this.execute()).map((suggestion) => suggestion.text);
|
|
3045
|
+
}
|
|
3046
|
+
};
|
|
3047
|
+
chunkMWMSFIWN_cjs.__name(_AutocompleteBuilder, "AutocompleteBuilder");
|
|
3048
|
+
var AutocompleteBuilder = _AutocompleteBuilder;
|
|
3049
|
+
var _SimilarBuilder = class _SimilarBuilder {
|
|
3050
|
+
constructor(httpClient, sourceId) {
|
|
3051
|
+
this.httpClient = httpClient;
|
|
3052
|
+
chunkMWMSFIWN_cjs.__publicField(this, "sourceId");
|
|
3053
|
+
chunkMWMSFIWN_cjs.__publicField(this, "similarOptions", {});
|
|
3054
|
+
this.sourceId = sourceId;
|
|
3055
|
+
}
|
|
3056
|
+
/**
|
|
3057
|
+
* Find similar content within a content type
|
|
3058
|
+
* @param contentType - Content type to search
|
|
3059
|
+
*/
|
|
3060
|
+
in(contentType) {
|
|
3061
|
+
this.similarOptions.contentType = contentType;
|
|
3062
|
+
return this;
|
|
3063
|
+
}
|
|
3064
|
+
/**
|
|
3065
|
+
* Add filter conditions
|
|
3066
|
+
* @param conditions - Filter conditions
|
|
3067
|
+
*/
|
|
3068
|
+
where(conditions) {
|
|
3069
|
+
this.similarOptions.where = {
|
|
3070
|
+
...this.similarOptions.where,
|
|
3071
|
+
...conditions
|
|
3072
|
+
};
|
|
3073
|
+
return this;
|
|
3074
|
+
}
|
|
3075
|
+
/**
|
|
3076
|
+
* Limit number of results
|
|
3077
|
+
* @param count - Maximum results
|
|
3078
|
+
*/
|
|
3079
|
+
limit(count) {
|
|
3080
|
+
this.similarOptions.limit = count;
|
|
3081
|
+
return this;
|
|
3082
|
+
}
|
|
3083
|
+
/**
|
|
3084
|
+
* Set locale for localized content
|
|
3085
|
+
* @param locale - Locale code
|
|
3086
|
+
*/
|
|
3087
|
+
locale(locale) {
|
|
3088
|
+
this.similarOptions.locale = locale;
|
|
3089
|
+
return this;
|
|
3090
|
+
}
|
|
3091
|
+
/**
|
|
3092
|
+
* Execute the similar query
|
|
3093
|
+
*/
|
|
3094
|
+
async execute() {
|
|
3095
|
+
if (this.similarOptions.contentType == null || this.similarOptions.contentType === "") {
|
|
3096
|
+
throw new Error('Content type is required for similar search. Use .in("contentType")');
|
|
3097
|
+
}
|
|
3098
|
+
const body = {
|
|
3099
|
+
source: this.sourceId,
|
|
3100
|
+
contentType: this.similarOptions.contentType
|
|
3101
|
+
};
|
|
3102
|
+
if (this.similarOptions.where && Object.keys(this.similarOptions.where).length > 0) {
|
|
3103
|
+
body.where = this.similarOptions.where;
|
|
3104
|
+
}
|
|
3105
|
+
if (this.similarOptions.limit !== void 0) {
|
|
3106
|
+
body.limit = this.similarOptions.limit;
|
|
3107
|
+
}
|
|
3108
|
+
if (this.similarOptions.locale != null && this.similarOptions.locale !== "") {
|
|
3109
|
+
body.locale = this.similarOptions.locale;
|
|
3110
|
+
}
|
|
3111
|
+
const response = await this.httpClient.send(
|
|
3112
|
+
"/v1/search/similar",
|
|
3113
|
+
"post",
|
|
3114
|
+
{ body }
|
|
3115
|
+
);
|
|
3116
|
+
return response.body;
|
|
3117
|
+
}
|
|
3118
|
+
/**
|
|
3119
|
+
* Get just the similar items
|
|
3120
|
+
*/
|
|
3121
|
+
async items() {
|
|
3122
|
+
return readPayload(await this.execute());
|
|
3123
|
+
}
|
|
3124
|
+
/**
|
|
3125
|
+
* Get the most similar item
|
|
3126
|
+
*/
|
|
3127
|
+
async first() {
|
|
3128
|
+
return readPayload(await this.limit(1).execute())[0] ?? null;
|
|
3129
|
+
}
|
|
3130
|
+
};
|
|
3131
|
+
chunkMWMSFIWN_cjs.__name(_SimilarBuilder, "SimilarBuilder");
|
|
3132
|
+
var SimilarBuilder = _SimilarBuilder;
|
|
3133
|
+
var _SearchClient = class _SearchClient {
|
|
3134
|
+
constructor(httpClient) {
|
|
3135
|
+
this.httpClient = httpClient;
|
|
3136
|
+
}
|
|
3137
|
+
/**
|
|
3138
|
+
* Start a search query
|
|
3139
|
+
* @param query - Search query string
|
|
3140
|
+
*/
|
|
3141
|
+
search(query) {
|
|
3142
|
+
return new SearchBuilder(this.httpClient, query);
|
|
3143
|
+
}
|
|
3144
|
+
/**
|
|
3145
|
+
* Start an autocomplete query
|
|
3146
|
+
* @param query - Partial query to autocomplete
|
|
3147
|
+
*/
|
|
3148
|
+
autocomplete(query) {
|
|
3149
|
+
return new AutocompleteBuilder(this.httpClient, query);
|
|
3150
|
+
}
|
|
3151
|
+
/**
|
|
3152
|
+
* Find content similar to a given entry
|
|
3153
|
+
* @param sourceId - Entry ID to find similar content for
|
|
3154
|
+
*/
|
|
3155
|
+
similar(sourceId) {
|
|
3156
|
+
return new SimilarBuilder(this.httpClient, sourceId);
|
|
3157
|
+
}
|
|
3158
|
+
/**
|
|
3159
|
+
* Quick search helper - searches all content types
|
|
3160
|
+
* @param query - Search query
|
|
3161
|
+
* @param limit - Max results (default 20)
|
|
3162
|
+
*/
|
|
3163
|
+
async global(query, limit = 20) {
|
|
3164
|
+
return this.search(query).across("*").limit(limit).execute();
|
|
3165
|
+
}
|
|
3166
|
+
};
|
|
3167
|
+
chunkMWMSFIWN_cjs.__name(_SearchClient, "SearchClient");
|
|
3168
|
+
var SearchClient = _SearchClient;
|
|
3169
|
+
|
|
3170
|
+
// src/batch/batch-client.ts
|
|
3171
|
+
var _BatchBuilder = class _BatchBuilder {
|
|
3172
|
+
constructor(httpClient) {
|
|
3173
|
+
this.httpClient = httpClient;
|
|
3174
|
+
chunkMWMSFIWN_cjs.__publicField(this, "operations", []);
|
|
3175
|
+
chunkMWMSFIWN_cjs.__publicField(this, "defaultOptions", {
|
|
3176
|
+
atomic: false,
|
|
3177
|
+
dryRun: false,
|
|
3178
|
+
returnData: true,
|
|
3179
|
+
continueOnError: true
|
|
3180
|
+
});
|
|
3181
|
+
}
|
|
3182
|
+
// ==========================================================================
|
|
3183
|
+
// ENTRY OPERATIONS
|
|
3184
|
+
// ==========================================================================
|
|
3185
|
+
/**
|
|
3186
|
+
* Add a create operation
|
|
3187
|
+
* @param contentType - Content type to create
|
|
3188
|
+
* @param data - Entry data with content field
|
|
3189
|
+
* @param id - Optional operation ID for tracking
|
|
3190
|
+
*/
|
|
3191
|
+
create(contentType, data, id) {
|
|
3192
|
+
this.operations.push({
|
|
3193
|
+
id,
|
|
3194
|
+
method: "POST",
|
|
3195
|
+
path: `/${contentType}`,
|
|
3196
|
+
body: data
|
|
3197
|
+
});
|
|
3198
|
+
return this;
|
|
3199
|
+
}
|
|
3200
|
+
/**
|
|
3201
|
+
* Add an update operation (partial update).
|
|
3202
|
+
*
|
|
3203
|
+
* The submitted fields are merged into the entry and the rest are left as they are. The merge is
|
|
3204
|
+
* field-level rather than deep: sending an object-typed field replaces that field's whole value.
|
|
3205
|
+
*
|
|
3206
|
+
* @param contentType - Content type
|
|
3207
|
+
* @param entryId - Entry ID to update
|
|
3208
|
+
* @param data - Fields to update
|
|
3209
|
+
* @param id - Optional operation ID for tracking
|
|
3210
|
+
*/
|
|
3211
|
+
update(contentType, entryId, data, id) {
|
|
3212
|
+
this.operations.push({
|
|
3213
|
+
id,
|
|
3214
|
+
method: "PATCH",
|
|
3215
|
+
path: `/${contentType}/${entryId}`,
|
|
3216
|
+
body: data
|
|
3217
|
+
});
|
|
3218
|
+
return this;
|
|
3219
|
+
}
|
|
3220
|
+
/**
|
|
3221
|
+
* Add a replace operation (full replacement).
|
|
3222
|
+
*
|
|
3223
|
+
* The payload becomes the entry's content for the addressed locale: fields you do not send are
|
|
3224
|
+
* cleared. Send the whole entry, or use {@link BatchClient.update} to change part of one.
|
|
3225
|
+
*
|
|
3226
|
+
* @param contentType - Content type
|
|
3227
|
+
* @param entryId - Entry ID to replace
|
|
3228
|
+
* @param data - Complete entry data
|
|
3229
|
+
* @param id - Optional operation ID for tracking
|
|
3230
|
+
*/
|
|
3231
|
+
replace(contentType, entryId, data, id) {
|
|
3232
|
+
this.operations.push({
|
|
3233
|
+
id,
|
|
3234
|
+
method: "PUT",
|
|
3235
|
+
path: `/${contentType}/${entryId}`,
|
|
3236
|
+
body: data
|
|
3237
|
+
});
|
|
3238
|
+
return this;
|
|
3239
|
+
}
|
|
3240
|
+
/**
|
|
3241
|
+
* Add a delete operation
|
|
3242
|
+
* @param contentType - Content type
|
|
3243
|
+
* @param entryId - Entry ID to delete
|
|
3244
|
+
* @param id - Optional operation ID for tracking
|
|
3245
|
+
*/
|
|
3246
|
+
delete(contentType, entryId, id) {
|
|
3247
|
+
this.operations.push({
|
|
3248
|
+
id,
|
|
3249
|
+
method: "DELETE",
|
|
3250
|
+
path: `/${contentType}/${entryId}`
|
|
3251
|
+
});
|
|
3252
|
+
return this;
|
|
3253
|
+
}
|
|
3254
|
+
// ==========================================================================
|
|
3255
|
+
// SINGLETON OPERATIONS
|
|
3256
|
+
// ==========================================================================
|
|
3257
|
+
/**
|
|
3258
|
+
* Add a singleton upsert operation
|
|
3259
|
+
* @param singletonKey - Singleton key
|
|
3260
|
+
* @param data - Singleton content
|
|
3261
|
+
* @param id - Optional operation ID for tracking
|
|
3262
|
+
*/
|
|
3263
|
+
upsertSingleton(singletonKey, data, id) {
|
|
3264
|
+
this.operations.push({
|
|
3265
|
+
id,
|
|
3266
|
+
method: "PATCH",
|
|
3267
|
+
path: `/_singletons/${singletonKey}`,
|
|
3268
|
+
body: data
|
|
3269
|
+
});
|
|
3270
|
+
return this;
|
|
3271
|
+
}
|
|
3272
|
+
/**
|
|
3273
|
+
* Add a singleton create operation (alias for upsert with POST)
|
|
3274
|
+
* @param singletonKey - Singleton key
|
|
3275
|
+
* @param data - Singleton content
|
|
3276
|
+
* @param id - Optional operation ID for tracking
|
|
3277
|
+
*/
|
|
3278
|
+
createSingleton(singletonKey, data, id) {
|
|
3279
|
+
this.operations.push({
|
|
3280
|
+
id,
|
|
3281
|
+
method: "POST",
|
|
3282
|
+
path: `/_singletons/${singletonKey}`,
|
|
3283
|
+
body: data
|
|
3284
|
+
});
|
|
3285
|
+
return this;
|
|
3286
|
+
}
|
|
3287
|
+
// ==========================================================================
|
|
3288
|
+
// RAW OPERATIONS
|
|
3289
|
+
// ==========================================================================
|
|
3290
|
+
/**
|
|
3291
|
+
* Add a raw operation (for advanced use cases)
|
|
3292
|
+
* @param operation - The operation to add
|
|
3293
|
+
*/
|
|
3294
|
+
raw(operation) {
|
|
3295
|
+
this.operations.push(operation);
|
|
3296
|
+
return this;
|
|
3297
|
+
}
|
|
3298
|
+
/**
|
|
3299
|
+
* Add multiple raw operations
|
|
3300
|
+
* @param operations - Operations to add
|
|
3301
|
+
*/
|
|
3302
|
+
rawMany(operations) {
|
|
3303
|
+
this.operations.push(...operations);
|
|
3304
|
+
return this;
|
|
3305
|
+
}
|
|
3306
|
+
// ==========================================================================
|
|
3307
|
+
// BULK HELPERS
|
|
3308
|
+
// ==========================================================================
|
|
3309
|
+
/**
|
|
3310
|
+
* Bulk create multiple entries of the same content type
|
|
3311
|
+
* @param contentType - Content type
|
|
3312
|
+
* @param items - Array of entry data
|
|
3313
|
+
*/
|
|
3314
|
+
bulkCreate(contentType, items) {
|
|
3315
|
+
for (const item of items) {
|
|
3316
|
+
const { id, ...data } = item;
|
|
3317
|
+
this.create(contentType, data, id);
|
|
3318
|
+
}
|
|
3319
|
+
return this;
|
|
3320
|
+
}
|
|
3321
|
+
/**
|
|
3322
|
+
* Bulk update multiple entries
|
|
3323
|
+
* @param contentType - Content type
|
|
3324
|
+
* @param updates - Array of { entryId, data } pairs
|
|
3325
|
+
*/
|
|
3326
|
+
bulkUpdate(contentType, updates) {
|
|
3327
|
+
for (const update of updates) {
|
|
3328
|
+
this.update(contentType, update.entryId, update.data, update.id);
|
|
3329
|
+
}
|
|
3330
|
+
return this;
|
|
3331
|
+
}
|
|
3332
|
+
/**
|
|
3333
|
+
* Bulk delete multiple entries
|
|
3334
|
+
* @param contentType - Content type
|
|
3335
|
+
* @param entryIds - Entry IDs to delete
|
|
3336
|
+
*/
|
|
3337
|
+
bulkDelete(contentType, entryIds) {
|
|
3338
|
+
for (const entryId of entryIds) {
|
|
3339
|
+
this.delete(contentType, entryId);
|
|
3340
|
+
}
|
|
3341
|
+
return this;
|
|
3342
|
+
}
|
|
3343
|
+
// ==========================================================================
|
|
3344
|
+
// CONFIGURATION
|
|
3345
|
+
// ==========================================================================
|
|
3346
|
+
/**
|
|
3347
|
+
* Configure default options for execution
|
|
3348
|
+
* @param options - Batch options
|
|
3349
|
+
*/
|
|
3350
|
+
configure(options) {
|
|
3351
|
+
this.defaultOptions = { ...this.defaultOptions, ...options };
|
|
3352
|
+
return this;
|
|
3353
|
+
}
|
|
3354
|
+
/**
|
|
3355
|
+
* Enable atomic mode (all-or-nothing)
|
|
3356
|
+
*/
|
|
3357
|
+
atomic() {
|
|
3358
|
+
this.defaultOptions.atomic = true;
|
|
3359
|
+
return this;
|
|
3360
|
+
}
|
|
3361
|
+
/**
|
|
3362
|
+
* Enable dry-run mode (validate without executing)
|
|
3363
|
+
*/
|
|
3364
|
+
dryRun() {
|
|
3365
|
+
this.defaultOptions.dryRun = true;
|
|
3366
|
+
return this;
|
|
3367
|
+
}
|
|
3368
|
+
/**
|
|
3369
|
+
* Disable returning full data in response
|
|
3370
|
+
*/
|
|
3371
|
+
noData() {
|
|
3372
|
+
this.defaultOptions.returnData = false;
|
|
3373
|
+
return this;
|
|
3374
|
+
}
|
|
3375
|
+
/**
|
|
3376
|
+
* Stop on first error (non-atomic mode)
|
|
3377
|
+
*/
|
|
3378
|
+
stopOnError() {
|
|
3379
|
+
this.defaultOptions.continueOnError = false;
|
|
3380
|
+
return this;
|
|
3381
|
+
}
|
|
3382
|
+
// ==========================================================================
|
|
3383
|
+
// EXECUTION
|
|
3384
|
+
// ==========================================================================
|
|
3385
|
+
/**
|
|
3386
|
+
* Get the current list of operations
|
|
3387
|
+
*/
|
|
3388
|
+
getOperations() {
|
|
3389
|
+
return [...this.operations];
|
|
3390
|
+
}
|
|
3391
|
+
/**
|
|
3392
|
+
* Get the number of operations
|
|
3393
|
+
*/
|
|
3394
|
+
size() {
|
|
3395
|
+
return this.operations.length;
|
|
3396
|
+
}
|
|
3397
|
+
/**
|
|
3398
|
+
* Clear all operations
|
|
3399
|
+
*/
|
|
3400
|
+
clear() {
|
|
3401
|
+
this.operations = [];
|
|
3402
|
+
return this;
|
|
3403
|
+
}
|
|
3404
|
+
/**
|
|
3405
|
+
* Clone the builder
|
|
3406
|
+
*/
|
|
3407
|
+
clone() {
|
|
3408
|
+
const cloned = new _BatchBuilder(this.httpClient);
|
|
3409
|
+
cloned.operations = [...this.operations];
|
|
3410
|
+
cloned.defaultOptions = { ...this.defaultOptions };
|
|
3411
|
+
return cloned;
|
|
3412
|
+
}
|
|
3413
|
+
/**
|
|
3414
|
+
* Execute the batch operations
|
|
3415
|
+
* @param options - Override default options
|
|
3416
|
+
*/
|
|
3417
|
+
async execute(options) {
|
|
3418
|
+
if (this.operations.length === 0) {
|
|
3419
|
+
throw new Error(
|
|
3420
|
+
"No operations to execute. Add operations using .create(), .update(), .delete(), etc."
|
|
3421
|
+
);
|
|
3422
|
+
}
|
|
3423
|
+
if (this.operations.length > 100) {
|
|
3424
|
+
throw new Error("Maximum 100 operations per batch. Split into multiple batches.");
|
|
3425
|
+
}
|
|
3426
|
+
const finalOptions = { ...this.defaultOptions, ...options };
|
|
3427
|
+
return this.httpClient.fetchPayload("/v1/batch", "post", {
|
|
3428
|
+
body: { operations: this.operations, options: finalOptions }
|
|
3429
|
+
});
|
|
3430
|
+
}
|
|
3431
|
+
/**
|
|
3432
|
+
* Validate operations without executing (convenience for dry-run)
|
|
3433
|
+
*/
|
|
3434
|
+
async validate() {
|
|
3435
|
+
return this.execute({ dryRun: true });
|
|
3436
|
+
}
|
|
3437
|
+
/**
|
|
3438
|
+
* Execute and throw if any operation failed
|
|
3439
|
+
*/
|
|
3440
|
+
async executeOrThrow(options) {
|
|
3441
|
+
const response = await this.execute(options);
|
|
3442
|
+
if (response.summary.failed > 0) {
|
|
3443
|
+
const failedOps = response.results.filter(
|
|
3444
|
+
(result) => "error" in result
|
|
3445
|
+
);
|
|
3446
|
+
const errorMessages = failedOps.map(
|
|
3447
|
+
(op) => `Operation ${op.index}${op.id != null && op.id !== "" ? ` (${op.id})` : ""}: ${op.error.code} \u2014 ${op.error.messages.join(" ")}`
|
|
3448
|
+
).join("; ");
|
|
3449
|
+
throw new Error(
|
|
3450
|
+
`Batch execution failed: ${response.summary.failed} of ${response.summary.total} operations failed. ${errorMessages}`
|
|
3451
|
+
);
|
|
3452
|
+
}
|
|
3453
|
+
return response;
|
|
3454
|
+
}
|
|
3455
|
+
};
|
|
3456
|
+
chunkMWMSFIWN_cjs.__name(_BatchBuilder, "BatchBuilder");
|
|
3457
|
+
var BatchBuilder = _BatchBuilder;
|
|
3458
|
+
var _BatchClient = class _BatchClient {
|
|
3459
|
+
constructor(httpClient) {
|
|
3460
|
+
this.httpClient = httpClient;
|
|
3461
|
+
}
|
|
3462
|
+
/**
|
|
3463
|
+
* Create a new batch builder
|
|
3464
|
+
*/
|
|
3465
|
+
create() {
|
|
3466
|
+
return new BatchBuilder(this.httpClient);
|
|
3467
|
+
}
|
|
3468
|
+
/**
|
|
3469
|
+
* Quick bulk create entries
|
|
3470
|
+
* @param contentType - Content type
|
|
3471
|
+
* @param items - Array of entry data
|
|
3472
|
+
* @param options - Batch options
|
|
3473
|
+
*/
|
|
3474
|
+
async bulkCreate(contentType, items, options) {
|
|
3475
|
+
return this.create().bulkCreate(contentType, items).execute(options);
|
|
3476
|
+
}
|
|
3477
|
+
/**
|
|
3478
|
+
* Quick bulk update entries
|
|
3479
|
+
* @param contentType - Content type
|
|
3480
|
+
* @param updates - Array of updates
|
|
3481
|
+
* @param options - Batch options
|
|
3482
|
+
*/
|
|
3483
|
+
async bulkUpdate(contentType, updates, options) {
|
|
3484
|
+
return this.create().bulkUpdate(contentType, updates).execute(options);
|
|
3485
|
+
}
|
|
3486
|
+
/**
|
|
3487
|
+
* Quick bulk delete entries
|
|
3488
|
+
* @param contentType - Content type
|
|
3489
|
+
* @param entryIds - Entry IDs to delete
|
|
3490
|
+
* @param options - Batch options
|
|
3491
|
+
*/
|
|
3492
|
+
async bulkDelete(contentType, entryIds, options) {
|
|
3493
|
+
return this.create().bulkDelete(contentType, entryIds).execute(options);
|
|
3494
|
+
}
|
|
3495
|
+
/**
|
|
3496
|
+
* Execute raw batch operations
|
|
3497
|
+
* @param operations - Array of operations
|
|
3498
|
+
* @param options - Batch options
|
|
3499
|
+
*/
|
|
3500
|
+
async execute(operations, options) {
|
|
3501
|
+
return this.create().rawMany(operations).execute(options);
|
|
3502
|
+
}
|
|
3503
|
+
};
|
|
3504
|
+
chunkMWMSFIWN_cjs.__name(_BatchClient, "BatchClient");
|
|
3505
|
+
var BatchClient = _BatchClient;
|
|
3506
|
+
|
|
3507
|
+
// src/queries/adhoc-query-client.ts
|
|
3508
|
+
var _AdhocQueryBuilder = class _AdhocQueryBuilder {
|
|
3509
|
+
constructor(httpClient) {
|
|
3510
|
+
this.httpClient = httpClient;
|
|
3511
|
+
chunkMWMSFIWN_cjs.__publicField(this, "queryDef", {});
|
|
3512
|
+
chunkMWMSFIWN_cjs.__publicField(this, "parameters", {});
|
|
3513
|
+
chunkMWMSFIWN_cjs.__publicField(this, "options", {});
|
|
3514
|
+
}
|
|
3515
|
+
// ==========================================================================
|
|
3516
|
+
// QUERY BLOCKS
|
|
3517
|
+
// ==========================================================================
|
|
3518
|
+
/**
|
|
3519
|
+
* Add a fetch block to retrieve content
|
|
3520
|
+
* @param config - Fetch configuration
|
|
3521
|
+
*/
|
|
3522
|
+
fetch(config) {
|
|
3523
|
+
this.queryDef.fetch = config;
|
|
3524
|
+
return this;
|
|
3525
|
+
}
|
|
3526
|
+
/**
|
|
3527
|
+
* Fetch from a content type
|
|
3528
|
+
* @param contentType - Content type to fetch
|
|
3529
|
+
*/
|
|
3530
|
+
from(contentType) {
|
|
3531
|
+
this.queryDef.fetch = { ...this.queryDef.fetch, contentType };
|
|
3532
|
+
return this;
|
|
3533
|
+
}
|
|
3534
|
+
/**
|
|
3535
|
+
* Fetch a singleton
|
|
3536
|
+
* @param singletonKey - Singleton key
|
|
3537
|
+
*/
|
|
3538
|
+
singleton(singletonKey) {
|
|
3539
|
+
this.queryDef.fetch = { ...this.queryDef.fetch, singleton: singletonKey };
|
|
3540
|
+
return this;
|
|
3541
|
+
}
|
|
3542
|
+
/**
|
|
3543
|
+
* Add aggregate block
|
|
3544
|
+
* @param config - Aggregate configuration
|
|
3545
|
+
*/
|
|
3546
|
+
aggregate(config) {
|
|
3547
|
+
this.queryDef.aggregate = config;
|
|
3548
|
+
return this;
|
|
3549
|
+
}
|
|
3550
|
+
/**
|
|
3551
|
+
* Add search block
|
|
3552
|
+
* @param config - Search configuration
|
|
3553
|
+
*/
|
|
3554
|
+
search(config) {
|
|
3555
|
+
this.queryDef.search = config;
|
|
3556
|
+
return this;
|
|
3557
|
+
}
|
|
3558
|
+
/**
|
|
3559
|
+
* Add similar block
|
|
3560
|
+
* @param config - Similar configuration
|
|
3561
|
+
*/
|
|
3562
|
+
similar(config) {
|
|
3563
|
+
this.queryDef.similar = config;
|
|
3564
|
+
return this;
|
|
3565
|
+
}
|
|
3566
|
+
// ==========================================================================
|
|
3567
|
+
// FETCH MODIFIERS
|
|
3568
|
+
// ==========================================================================
|
|
3569
|
+
/**
|
|
3570
|
+
* Add where conditions to fetch
|
|
3571
|
+
* @param conditions - Filter conditions
|
|
3572
|
+
*/
|
|
3573
|
+
where(conditions) {
|
|
3574
|
+
this.queryDef.fetch = {
|
|
3575
|
+
...this.queryDef.fetch,
|
|
3576
|
+
where: { ...this.queryDef.fetch?.where, ...conditions }
|
|
3577
|
+
};
|
|
3578
|
+
return this;
|
|
3579
|
+
}
|
|
3580
|
+
/**
|
|
3581
|
+
* Add include for reference population
|
|
3582
|
+
* @param fields - Reference fields to include
|
|
3583
|
+
*/
|
|
3584
|
+
include(fields) {
|
|
3585
|
+
this.queryDef.fetch = {
|
|
3586
|
+
...this.queryDef.fetch,
|
|
3587
|
+
include: fields
|
|
3588
|
+
};
|
|
3589
|
+
return this;
|
|
3590
|
+
}
|
|
3591
|
+
/**
|
|
3592
|
+
* Add sort specification
|
|
3593
|
+
* @param sort - Sort configuration
|
|
3594
|
+
*/
|
|
3595
|
+
sort(sort) {
|
|
3596
|
+
this.queryDef.fetch = {
|
|
3597
|
+
...this.queryDef.fetch,
|
|
3598
|
+
sort: { ...this.queryDef.fetch?.sort, ...sort }
|
|
3599
|
+
};
|
|
3600
|
+
return this;
|
|
3601
|
+
}
|
|
3602
|
+
/**
|
|
3603
|
+
* Sort by field ascending
|
|
3604
|
+
* @param field - Field to sort by
|
|
3605
|
+
*/
|
|
3606
|
+
sortAsc(field) {
|
|
3607
|
+
return this.sort({ [field]: "asc" });
|
|
3608
|
+
}
|
|
3609
|
+
/**
|
|
3610
|
+
* Sort by field descending
|
|
3611
|
+
* @param field - Field to sort by
|
|
3612
|
+
*/
|
|
3613
|
+
sortDesc(field) {
|
|
3614
|
+
return this.sort({ [field]: "desc" });
|
|
3615
|
+
}
|
|
3616
|
+
/**
|
|
3617
|
+
* Limit results
|
|
3618
|
+
* @param count - Maximum results
|
|
3619
|
+
*/
|
|
3620
|
+
limit(count) {
|
|
3621
|
+
this.queryDef.fetch = {
|
|
3622
|
+
...this.queryDef.fetch,
|
|
3623
|
+
limit: count
|
|
3624
|
+
};
|
|
3625
|
+
return this;
|
|
3626
|
+
}
|
|
3627
|
+
/**
|
|
3628
|
+
* Skip results
|
|
3629
|
+
* @param count - Number to skip
|
|
3630
|
+
*/
|
|
3631
|
+
offset(count) {
|
|
3632
|
+
this.queryDef.fetch = {
|
|
3633
|
+
...this.queryDef.fetch,
|
|
3634
|
+
offset: count
|
|
3635
|
+
};
|
|
3636
|
+
return this;
|
|
3637
|
+
}
|
|
3638
|
+
// ==========================================================================
|
|
3639
|
+
// PROJECTIONS & FILTERS
|
|
3640
|
+
// ==========================================================================
|
|
3641
|
+
/**
|
|
3642
|
+
* Select and transform fields in output
|
|
3643
|
+
* @param projection - Field mapping (outputField: sourceField)
|
|
3644
|
+
*/
|
|
3645
|
+
select(projection) {
|
|
3646
|
+
this.queryDef.select = { ...this.queryDef.select, ...projection };
|
|
3647
|
+
return this;
|
|
3648
|
+
}
|
|
3649
|
+
/**
|
|
3650
|
+
* Add a post-fetch filter (supports ${param} syntax)
|
|
3651
|
+
* @param field - Field path to filter
|
|
3652
|
+
* @param condition - Filter condition
|
|
3653
|
+
*/
|
|
3654
|
+
filter(field, condition) {
|
|
3655
|
+
this.queryDef.filters = { ...this.queryDef.filters, [field]: condition };
|
|
3656
|
+
return this;
|
|
3657
|
+
}
|
|
3658
|
+
/**
|
|
3659
|
+
* Add full-text search
|
|
3660
|
+
* @param config - Full-text configuration
|
|
3661
|
+
*/
|
|
3662
|
+
fullText(config) {
|
|
3663
|
+
this.queryDef.fullText = config;
|
|
3664
|
+
return this;
|
|
3665
|
+
}
|
|
3666
|
+
// ==========================================================================
|
|
3667
|
+
// PARAMETERS
|
|
3668
|
+
// ==========================================================================
|
|
3669
|
+
/**
|
|
3670
|
+
* Set query parameters
|
|
3671
|
+
* @param params - Parameter values
|
|
3672
|
+
*/
|
|
3673
|
+
params(params) {
|
|
3674
|
+
this.parameters = { ...this.parameters, ...params };
|
|
3675
|
+
return this;
|
|
3676
|
+
}
|
|
3677
|
+
/**
|
|
3678
|
+
* Set a single parameter
|
|
3679
|
+
* @param name - Parameter name
|
|
3680
|
+
* @param value - Parameter value
|
|
3681
|
+
*/
|
|
3682
|
+
param(name, value) {
|
|
3683
|
+
this.parameters[name] = value;
|
|
3684
|
+
return this;
|
|
3685
|
+
}
|
|
3686
|
+
// ==========================================================================
|
|
3687
|
+
// OPTIONS
|
|
3688
|
+
// ==========================================================================
|
|
3689
|
+
/**
|
|
3690
|
+
* Enable debug mode
|
|
3691
|
+
*/
|
|
3692
|
+
debug() {
|
|
3693
|
+
this.options.debug = true;
|
|
3694
|
+
return this;
|
|
3695
|
+
}
|
|
3696
|
+
/**
|
|
3697
|
+
* Skip cache for this request
|
|
3698
|
+
*/
|
|
3699
|
+
skipCache() {
|
|
3700
|
+
this.options.skipCache = true;
|
|
3701
|
+
return this;
|
|
3702
|
+
}
|
|
3703
|
+
/**
|
|
3704
|
+
* Set custom timeout
|
|
3705
|
+
* @param ms - Timeout in milliseconds (1000-60000)
|
|
3706
|
+
*/
|
|
3707
|
+
timeout(ms) {
|
|
3708
|
+
if (ms < 1e3 || ms > 6e4) {
|
|
3709
|
+
throw new Error("Timeout must be between 1000 and 60000 milliseconds");
|
|
3710
|
+
}
|
|
3711
|
+
this.options.timeout = ms;
|
|
3712
|
+
return this;
|
|
3713
|
+
}
|
|
3714
|
+
/**
|
|
3715
|
+
* Set cache policy
|
|
3716
|
+
* @param ttl - Cache TTL in seconds
|
|
3717
|
+
* @param strategy - Cache strategy
|
|
3718
|
+
*/
|
|
3719
|
+
cache(ttl, strategy) {
|
|
3720
|
+
this.queryDef.cache = { ttl, strategy };
|
|
3721
|
+
return this;
|
|
3722
|
+
}
|
|
3723
|
+
// ==========================================================================
|
|
3724
|
+
// EXECUTION
|
|
3725
|
+
// ==========================================================================
|
|
3726
|
+
/**
|
|
3727
|
+
* Get the query definition (for debugging)
|
|
3728
|
+
*/
|
|
3729
|
+
getDefinition() {
|
|
3730
|
+
return { ...this.queryDef };
|
|
3731
|
+
}
|
|
3732
|
+
/**
|
|
3733
|
+
* Get the parameters (for debugging)
|
|
3734
|
+
*/
|
|
3735
|
+
getParameters() {
|
|
3736
|
+
return { ...this.parameters };
|
|
3737
|
+
}
|
|
3738
|
+
/**
|
|
3739
|
+
* Execute the query
|
|
3740
|
+
*/
|
|
3741
|
+
async execute() {
|
|
3742
|
+
if (!this.queryDef.fetch && !this.queryDef.aggregate && !this.queryDef.search && !this.queryDef.similar) {
|
|
3743
|
+
throw new Error(
|
|
3744
|
+
"Query must have at least one of: fetch, aggregate, search, or similar. Use .from(), .aggregate(), .search(), or .similar()"
|
|
3745
|
+
);
|
|
3746
|
+
}
|
|
3747
|
+
const body = {
|
|
3748
|
+
query: this.queryDef
|
|
3749
|
+
};
|
|
3750
|
+
if (Object.keys(this.parameters).length > 0) {
|
|
3751
|
+
body.parameters = this.parameters;
|
|
3752
|
+
}
|
|
3753
|
+
if (Object.keys(this.options).length > 0) {
|
|
3754
|
+
body.options = this.options;
|
|
3755
|
+
}
|
|
3756
|
+
return this.httpClient.fetchPayload(
|
|
3757
|
+
"/v1/query",
|
|
3758
|
+
"post",
|
|
3759
|
+
{ body }
|
|
3760
|
+
);
|
|
3761
|
+
}
|
|
3762
|
+
/**
|
|
3763
|
+
* Execute and return the records as an array (for list queries).
|
|
3764
|
+
*/
|
|
3765
|
+
async items() {
|
|
3766
|
+
const { result } = await this.execute();
|
|
3767
|
+
if (result === void 0) return [];
|
|
3768
|
+
return Array.isArray(result) ? result : [result];
|
|
3769
|
+
}
|
|
3770
|
+
/**
|
|
3771
|
+
* Execute and return the single record (for singleton queries).
|
|
3772
|
+
*/
|
|
3773
|
+
async data() {
|
|
3774
|
+
const { result } = await this.execute();
|
|
3775
|
+
if (Array.isArray(result)) return result[0];
|
|
3776
|
+
return result;
|
|
3777
|
+
}
|
|
3778
|
+
/**
|
|
3779
|
+
* Clone the builder
|
|
3780
|
+
*/
|
|
3781
|
+
clone() {
|
|
3782
|
+
const cloned = new _AdhocQueryBuilder(this.httpClient);
|
|
3783
|
+
cloned.queryDef = JSON.parse(JSON.stringify(this.queryDef));
|
|
3784
|
+
cloned.parameters = { ...this.parameters };
|
|
3785
|
+
cloned.options = { ...this.options };
|
|
3786
|
+
return cloned;
|
|
3787
|
+
}
|
|
3788
|
+
};
|
|
3789
|
+
chunkMWMSFIWN_cjs.__name(_AdhocQueryBuilder, "AdhocQueryBuilder");
|
|
3790
|
+
var AdhocQueryBuilder = _AdhocQueryBuilder;
|
|
3791
|
+
var _AdhocQueryClient = class _AdhocQueryClient {
|
|
3792
|
+
constructor(httpClient) {
|
|
3793
|
+
this.httpClient = httpClient;
|
|
3794
|
+
}
|
|
3795
|
+
/**
|
|
3796
|
+
* Create a new query builder
|
|
3797
|
+
*/
|
|
3798
|
+
build() {
|
|
3799
|
+
return new AdhocQueryBuilder(this.httpClient);
|
|
3800
|
+
}
|
|
3801
|
+
/**
|
|
3802
|
+
* Execute a query definition directly
|
|
3803
|
+
* @param definition - Query definition
|
|
3804
|
+
* @param parameters - Parameter values
|
|
3805
|
+
* @param options - Execution options
|
|
3806
|
+
*/
|
|
3807
|
+
async execute(definition, parameters, options) {
|
|
3808
|
+
const body = {
|
|
3809
|
+
query: definition
|
|
3810
|
+
};
|
|
3811
|
+
if (parameters && Object.keys(parameters).length > 0) {
|
|
3812
|
+
body.parameters = parameters;
|
|
3813
|
+
}
|
|
3814
|
+
if (options && Object.keys(options).length > 0) {
|
|
3815
|
+
body.options = options;
|
|
3816
|
+
}
|
|
3817
|
+
return this.httpClient.fetchPayload(
|
|
3818
|
+
"/v1/query",
|
|
3819
|
+
"post",
|
|
3820
|
+
{ body }
|
|
3821
|
+
);
|
|
3822
|
+
}
|
|
3823
|
+
/**
|
|
3824
|
+
* Quick fetch helper
|
|
3825
|
+
* @param contentType - Content type
|
|
3826
|
+
* @param options - Fetch options
|
|
3827
|
+
*/
|
|
3828
|
+
async fetch(contentType, options) {
|
|
3829
|
+
const { result } = await this.execute({ fetch: { contentType, ...options } });
|
|
3830
|
+
if (result === void 0) return [];
|
|
3831
|
+
return Array.isArray(result) ? result : [result];
|
|
3832
|
+
}
|
|
3833
|
+
/**
|
|
3834
|
+
* Quick singleton fetch helper
|
|
3835
|
+
* @param singletonKey - Singleton key
|
|
3836
|
+
*/
|
|
3837
|
+
async fetchSingleton(singletonKey) {
|
|
3838
|
+
const { result } = await this.execute({ fetch: { singleton: singletonKey } });
|
|
3839
|
+
if (Array.isArray(result)) return result[0];
|
|
3840
|
+
return result;
|
|
3841
|
+
}
|
|
3842
|
+
/**
|
|
3843
|
+
* Quick aggregate helper
|
|
3844
|
+
* @param source - Source content type
|
|
3845
|
+
* @param metrics - Metrics to compute
|
|
3846
|
+
* @param groupBy - Optional grouping field
|
|
3847
|
+
*/
|
|
3848
|
+
async aggregate(source, metrics, groupBy) {
|
|
3849
|
+
const { result } = await this.execute({ aggregate: { source, metrics, by: groupBy } });
|
|
3850
|
+
if (result === void 0) return [];
|
|
3851
|
+
return Array.isArray(result) ? result : [result];
|
|
3852
|
+
}
|
|
3853
|
+
};
|
|
3854
|
+
chunkMWMSFIWN_cjs.__name(_AdhocQueryClient, "AdhocQueryClient");
|
|
3855
|
+
var AdhocQueryClient = _AdhocQueryClient;
|
|
3856
|
+
|
|
3857
|
+
// src/config/dev-detection.ts
|
|
3858
|
+
function getProcess() {
|
|
3859
|
+
return typeof process !== "undefined" ? process : void 0;
|
|
3860
|
+
}
|
|
3861
|
+
chunkMWMSFIWN_cjs.__name(getProcess, "getProcess");
|
|
3862
|
+
function getBuiltin(proc, id) {
|
|
3863
|
+
if (proc == null || typeof proc.getBuiltinModule !== "function") return null;
|
|
3864
|
+
try {
|
|
3865
|
+
return proc.getBuiltinModule(id);
|
|
3866
|
+
} catch {
|
|
3867
|
+
return null;
|
|
3868
|
+
}
|
|
3869
|
+
}
|
|
3870
|
+
chunkMWMSFIWN_cjs.__name(getBuiltin, "getBuiltin");
|
|
3871
|
+
function isProduction(proc) {
|
|
3872
|
+
return proc?.env?.NODE_ENV === "production";
|
|
3873
|
+
}
|
|
3874
|
+
chunkMWMSFIWN_cjs.__name(isProduction, "isProduction");
|
|
3875
|
+
function resolveFromEnv(proc) {
|
|
3876
|
+
const dataset = proc?.env?.RELYIO_DEV_DATASET;
|
|
3877
|
+
const apiKey = proc?.env?.RELYIO_DEV_API_KEY;
|
|
3878
|
+
if (dataset != null && dataset !== "" && apiKey != null && apiKey !== "") {
|
|
3879
|
+
return { dataset, apiKey, source: "env" };
|
|
3880
|
+
}
|
|
3881
|
+
return null;
|
|
3882
|
+
}
|
|
3883
|
+
chunkMWMSFIWN_cjs.__name(resolveFromEnv, "resolveFromEnv");
|
|
3884
|
+
function resolveFromFile(proc) {
|
|
3885
|
+
if (proc == null || typeof proc.cwd !== "function") return null;
|
|
3886
|
+
const fs = getBuiltin(proc, "node:fs");
|
|
3887
|
+
const path = getBuiltin(proc, "node:path");
|
|
3888
|
+
if (fs == null || path == null) return null;
|
|
3889
|
+
let dir = proc.cwd();
|
|
3890
|
+
for (; ; ) {
|
|
3891
|
+
const candidate = path.join(dir, ".relyio", "dev.json");
|
|
3892
|
+
try {
|
|
3893
|
+
if (fs.existsSync(candidate)) {
|
|
3894
|
+
const parsed = JSON.parse(fs.readFileSync(candidate, "utf8"));
|
|
3895
|
+
if (parsed.datasetId != null && parsed.datasetId !== "" && parsed.previewApiKey != null && parsed.previewApiKey !== "") {
|
|
3896
|
+
if (parsed.expiresAt != null && Number.isFinite(new Date(parsed.expiresAt).getTime()) && new Date(parsed.expiresAt).getTime() < Date.now()) {
|
|
3897
|
+
return null;
|
|
3898
|
+
}
|
|
3899
|
+
return { dataset: parsed.datasetId, apiKey: parsed.previewApiKey, source: "file" };
|
|
3900
|
+
}
|
|
3901
|
+
}
|
|
3902
|
+
} catch {
|
|
3903
|
+
}
|
|
3904
|
+
const parent = path.dirname(dir);
|
|
3905
|
+
if (parent === dir) break;
|
|
3906
|
+
dir = parent;
|
|
3907
|
+
}
|
|
3908
|
+
return null;
|
|
3909
|
+
}
|
|
3910
|
+
chunkMWMSFIWN_cjs.__name(resolveFromFile, "resolveFromFile");
|
|
3911
|
+
function resolveDevOverride(dev) {
|
|
3912
|
+
if (dev === false) return null;
|
|
3913
|
+
const proc = getProcess();
|
|
3914
|
+
if (dev !== true && isProduction(proc)) return null;
|
|
3915
|
+
return resolveFromEnv(proc) ?? resolveFromFile(proc);
|
|
3916
|
+
}
|
|
3917
|
+
chunkMWMSFIWN_cjs.__name(resolveDevOverride, "resolveDevOverride");
|
|
3918
|
+
|
|
3919
|
+
// src/config/types.ts
|
|
3920
|
+
var DEFAULT_CONFIG = {
|
|
3921
|
+
baseUrl: "https://api.relyio.com",
|
|
3922
|
+
timeout: 3e4,
|
|
3923
|
+
retryAttempts: 3,
|
|
3924
|
+
useSsl: true,
|
|
3925
|
+
environment: "production",
|
|
3926
|
+
headers: {},
|
|
3927
|
+
browser: {
|
|
3928
|
+
includeCredentials: true,
|
|
3929
|
+
useRelativeUrls: false
|
|
3930
|
+
},
|
|
3931
|
+
cache: {
|
|
3932
|
+
enabled: true,
|
|
3933
|
+
ttl: 5 * 60 * 1e3,
|
|
3934
|
+
// 5 minutes
|
|
3935
|
+
maxEntries: 1e3,
|
|
3936
|
+
maxSize: 50 * 1024 * 1024,
|
|
3937
|
+
// 50MB
|
|
3938
|
+
strategies: ["memory", "stale-while-revalidate"]
|
|
3939
|
+
},
|
|
3940
|
+
realtime: {
|
|
3941
|
+
enabled: true,
|
|
3942
|
+
maxReconnectAttempts: 10,
|
|
3943
|
+
reconnectDelay: 1e3,
|
|
3944
|
+
heartbeatInterval: 3e4
|
|
3945
|
+
},
|
|
3946
|
+
debug: {
|
|
3947
|
+
enabled: false,
|
|
3948
|
+
logRequests: false,
|
|
3949
|
+
logCache: false,
|
|
3950
|
+
logWebSocket: false
|
|
3951
|
+
}
|
|
3952
|
+
};
|
|
3953
|
+
function validateConfig(config) {
|
|
3954
|
+
if (!config.apiKey || config.apiKey.length === 0) {
|
|
3955
|
+
throw new Error("API key is required");
|
|
3956
|
+
}
|
|
3957
|
+
if (typeof config.apiKey !== "string") {
|
|
3958
|
+
throw new Error("API key must be a string");
|
|
3959
|
+
}
|
|
3960
|
+
if (config.baseUrl != null && typeof config.baseUrl !== "string") {
|
|
3961
|
+
throw new Error("Base URL must be a string");
|
|
3962
|
+
}
|
|
3963
|
+
if (config.timeout != null && (typeof config.timeout !== "number" || config.timeout < 0)) {
|
|
3964
|
+
throw new Error("Timeout must be a positive number");
|
|
3965
|
+
}
|
|
3966
|
+
if (config.retryAttempts != null && (typeof config.retryAttempts !== "number" || config.retryAttempts < 0)) {
|
|
3967
|
+
throw new Error("Retry attempts must be a positive number");
|
|
3968
|
+
}
|
|
3969
|
+
}
|
|
3970
|
+
chunkMWMSFIWN_cjs.__name(validateConfig, "validateConfig");
|
|
3971
|
+
var devOverrideNotified = false;
|
|
3972
|
+
function mergeConfig(userConfig) {
|
|
3973
|
+
validateConfig(userConfig);
|
|
3974
|
+
const merged = {
|
|
3975
|
+
...DEFAULT_CONFIG,
|
|
3976
|
+
...userConfig,
|
|
3977
|
+
headers: {
|
|
3978
|
+
...DEFAULT_CONFIG.headers,
|
|
3979
|
+
...userConfig.headers
|
|
3980
|
+
},
|
|
3981
|
+
browser: {
|
|
3982
|
+
...DEFAULT_CONFIG.browser,
|
|
3983
|
+
...userConfig.browser
|
|
3984
|
+
},
|
|
3985
|
+
cache: {
|
|
3986
|
+
...DEFAULT_CONFIG.cache,
|
|
3987
|
+
...userConfig.cache
|
|
3988
|
+
},
|
|
3989
|
+
realtime: {
|
|
3990
|
+
...DEFAULT_CONFIG.realtime,
|
|
3991
|
+
...userConfig.realtime
|
|
3992
|
+
},
|
|
3993
|
+
debug: {
|
|
3994
|
+
...DEFAULT_CONFIG.debug,
|
|
3995
|
+
...userConfig.debug
|
|
3996
|
+
}
|
|
3997
|
+
};
|
|
3998
|
+
const devOverride = resolveDevOverride(userConfig.dev);
|
|
3999
|
+
if (devOverride != null) {
|
|
4000
|
+
merged.apiKey = devOverride.apiKey;
|
|
4001
|
+
merged.dataset = devOverride.dataset;
|
|
4002
|
+
if (!devOverrideNotified) {
|
|
4003
|
+
devOverrideNotified = true;
|
|
4004
|
+
console.info(
|
|
4005
|
+
`[RelyIO] Sandbox dev mode active (${devOverride.source}) \u2014 targeting dataset "${devOverride.dataset}" with a scoped preview key.`
|
|
4006
|
+
);
|
|
4007
|
+
}
|
|
4008
|
+
}
|
|
4009
|
+
return merged;
|
|
4010
|
+
}
|
|
4011
|
+
chunkMWMSFIWN_cjs.__name(mergeConfig, "mergeConfig");
|
|
4012
|
+
|
|
4013
|
+
// src/client/create-client.ts
|
|
4014
|
+
var API_VERSION_SEGMENT = "v1";
|
|
4015
|
+
var _TypedRelyIOClient = class _TypedRelyIOClient {
|
|
4016
|
+
constructor(config) {
|
|
4017
|
+
chunkMWMSFIWN_cjs.__publicField(this, "httpClient");
|
|
4018
|
+
chunkMWMSFIWN_cjs.__publicField(this, "config");
|
|
4019
|
+
chunkMWMSFIWN_cjs.__publicField(this, "_search");
|
|
4020
|
+
chunkMWMSFIWN_cjs.__publicField(this, "_batch");
|
|
4021
|
+
chunkMWMSFIWN_cjs.__publicField(this, "_adhocQuery");
|
|
4022
|
+
chunkMWMSFIWN_cjs.__publicField(this, "_schema");
|
|
4023
|
+
const { schema, ...clientConfig } = config;
|
|
4024
|
+
this._schema = schema;
|
|
4025
|
+
this.config = mergeConfig(clientConfig);
|
|
4026
|
+
this.httpClient = new HttpClient(this.config);
|
|
4027
|
+
this._search = new SearchClient(this.httpClient);
|
|
4028
|
+
this._batch = new BatchClient(this.httpClient);
|
|
4029
|
+
this._adhocQuery = new AdhocQueryClient(this.httpClient);
|
|
4030
|
+
}
|
|
4031
|
+
// ============================================================================
|
|
4032
|
+
// CORE CLIENTS
|
|
4033
|
+
// ============================================================================
|
|
4034
|
+
/**
|
|
4035
|
+
* Get the schema definition
|
|
4036
|
+
*/
|
|
4037
|
+
get schema() {
|
|
4038
|
+
return this._schema;
|
|
4039
|
+
}
|
|
4040
|
+
/**
|
|
4041
|
+
* Get the ad-hoc query client
|
|
4042
|
+
*/
|
|
4043
|
+
get query() {
|
|
4044
|
+
return this._adhocQuery;
|
|
4045
|
+
}
|
|
4046
|
+
// ============================================================================
|
|
4047
|
+
// CONTENT OPERATIONS
|
|
4048
|
+
// ============================================================================
|
|
4049
|
+
/**
|
|
4050
|
+
* Get a content client for a specific content type
|
|
4051
|
+
* Type-safe when schema is provided: autocompletes content type names AND field names
|
|
4052
|
+
*
|
|
4053
|
+
* @example
|
|
4054
|
+
* ```typescript
|
|
4055
|
+
* const products = await relyio.content('product')
|
|
4056
|
+
* .query()
|
|
4057
|
+
* .where({ status: 'active' }) // ← Field names autocomplete with schema!
|
|
4058
|
+
* .locale('en-US')
|
|
4059
|
+
* .limit(20)
|
|
4060
|
+
* .execute()
|
|
4061
|
+
* ```
|
|
4062
|
+
*/
|
|
4063
|
+
content(contentType) {
|
|
4064
|
+
return new ContentClient(this.httpClient, contentType, this.config.locale);
|
|
4065
|
+
}
|
|
4066
|
+
/**
|
|
4067
|
+
* Get an asset client for a specific asset type.
|
|
4068
|
+
*
|
|
4069
|
+
* Mirrors `content()` but targets `entryType: 'asset'` entries on the same
|
|
4070
|
+
* dynamic `/{assetTypeId}` routes, and adds the `file(entryUid)` sub-resource
|
|
4071
|
+
* builder + a high-level `upload()` helper. Type-safe when a schema is
|
|
4072
|
+
* provided: autocompletes asset type names AND metadata field names.
|
|
4073
|
+
*
|
|
4074
|
+
* @example
|
|
4075
|
+
* ```typescript
|
|
4076
|
+
* const photo = await relyio.asset('productPhoto').upload(file)
|
|
4077
|
+
* .filename('shoe.jpg')
|
|
4078
|
+
* .visibility('public')
|
|
4079
|
+
* .execute()
|
|
4080
|
+
*
|
|
4081
|
+
* const heroUrl = await relyio.asset('productPhoto').file(photo._metadata.id).url({ variantId: 'hero' })
|
|
4082
|
+
* ```
|
|
4083
|
+
*/
|
|
4084
|
+
asset(assetType) {
|
|
4085
|
+
return new AssetClient(this.httpClient, assetType, this.config.locale);
|
|
4086
|
+
}
|
|
4087
|
+
/**
|
|
4088
|
+
* Get a singleton client for a specific singleton key
|
|
4089
|
+
* Type-safe when schema is provided: autocompletes singleton names AND field names
|
|
4090
|
+
*
|
|
4091
|
+
* @example
|
|
4092
|
+
* ```typescript
|
|
4093
|
+
* const homepage = await relyio.singleton('homepage')
|
|
4094
|
+
* .include(['featuredProducts'])
|
|
4095
|
+
* .locale('en')
|
|
4096
|
+
* .fetch()
|
|
4097
|
+
* ```
|
|
4098
|
+
*/
|
|
4099
|
+
singleton(key) {
|
|
4100
|
+
return new SingletonClient(this.httpClient, key);
|
|
4101
|
+
}
|
|
4102
|
+
/**
|
|
4103
|
+
* Get a settings client for a specific settings group
|
|
4104
|
+
* Type-safe when schema is provided: autocompletes settings group names AND field names
|
|
4105
|
+
*
|
|
4106
|
+
* @example
|
|
4107
|
+
* ```typescript
|
|
4108
|
+
* const emailConfig = await relyio.settings('email-config')
|
|
4109
|
+
* .include(['defaultTemplate'])
|
|
4110
|
+
* .fetch()
|
|
4111
|
+
* ```
|
|
4112
|
+
*/
|
|
4113
|
+
settings(groupId) {
|
|
4114
|
+
return new SettingsClient(this.httpClient, groupId);
|
|
4115
|
+
}
|
|
4116
|
+
// ============================================================================
|
|
4117
|
+
// SEARCH OPERATIONS
|
|
4118
|
+
// ============================================================================
|
|
4119
|
+
/**
|
|
4120
|
+
* Start a search query with full-text, semantic, or hybrid search
|
|
4121
|
+
*
|
|
4122
|
+
* @example
|
|
4123
|
+
* ```typescript
|
|
4124
|
+
* const results = await relyio.search('wireless headphones')
|
|
4125
|
+
* .in('product')
|
|
4126
|
+
* .where({ status: 'active' })
|
|
4127
|
+
* .limit(20)
|
|
4128
|
+
* .includeScore()
|
|
4129
|
+
* .execute()
|
|
4130
|
+
* ```
|
|
4131
|
+
*/
|
|
4132
|
+
search(query) {
|
|
4133
|
+
return this._search.search(query);
|
|
4134
|
+
}
|
|
4135
|
+
/**
|
|
4136
|
+
* Start an autocomplete query for search suggestions
|
|
4137
|
+
*
|
|
4138
|
+
* @example
|
|
4139
|
+
* ```typescript
|
|
4140
|
+
* const suggestions = await relyio.autocomplete('wire')
|
|
4141
|
+
* .in('product')
|
|
4142
|
+
* .field('title')
|
|
4143
|
+
* .fuzzy()
|
|
4144
|
+
* .limit(10)
|
|
4145
|
+
* .execute()
|
|
4146
|
+
* ```
|
|
4147
|
+
*/
|
|
4148
|
+
autocomplete(query) {
|
|
4149
|
+
return this._search.autocomplete(query);
|
|
4150
|
+
}
|
|
4151
|
+
/**
|
|
4152
|
+
* Find content similar to a given entry using vector search
|
|
4153
|
+
*
|
|
4154
|
+
* @example
|
|
4155
|
+
* ```typescript
|
|
4156
|
+
* const similar = await relyio.similar('entry_abc123')
|
|
4157
|
+
* .in('product')
|
|
4158
|
+
* .locale('fr-FR')
|
|
4159
|
+
* .limit(5)
|
|
4160
|
+
* .execute()
|
|
4161
|
+
* ```
|
|
4162
|
+
*/
|
|
4163
|
+
similar(sourceId) {
|
|
4164
|
+
return this._search.similar(sourceId);
|
|
4165
|
+
}
|
|
4166
|
+
// ============================================================================
|
|
4167
|
+
// BATCH OPERATIONS
|
|
4168
|
+
// ============================================================================
|
|
4169
|
+
/**
|
|
4170
|
+
* Start a batch operation for bulk create/update/delete
|
|
4171
|
+
*
|
|
4172
|
+
* @example
|
|
4173
|
+
* ```typescript
|
|
4174
|
+
* const result = await relyio.batch()
|
|
4175
|
+
* .create('product', { content: { title: 'iPhone', price: 999 } })
|
|
4176
|
+
* .update('product', 'entry_abc', { content: { price: 899 } })
|
|
4177
|
+
* .delete('product', 'entry_xyz')
|
|
4178
|
+
* .upsertSingleton('homepage', { content: { hero: 'Welcome!' } })
|
|
4179
|
+
* .execute({ atomic: true })
|
|
4180
|
+
*
|
|
4181
|
+
* console.log(result.summary.succeeded)
|
|
4182
|
+
* ```
|
|
4183
|
+
*/
|
|
4184
|
+
batch() {
|
|
4185
|
+
return this._batch.create();
|
|
4186
|
+
}
|
|
4187
|
+
// ============================================================================
|
|
4188
|
+
// QUERY OPERATIONS
|
|
4189
|
+
// ============================================================================
|
|
4190
|
+
/**
|
|
4191
|
+
* Build and execute an ad-hoc query dynamically
|
|
4192
|
+
*
|
|
4193
|
+
* @example
|
|
4194
|
+
* ```typescript
|
|
4195
|
+
* const result = await relyio.adhoc()
|
|
4196
|
+
* .from('product')
|
|
4197
|
+
* .where({ status: 'active' })
|
|
4198
|
+
* .include(['brand', 'categories'])
|
|
4199
|
+
* .select({
|
|
4200
|
+
* id: 'id',
|
|
4201
|
+
* title: 'title',
|
|
4202
|
+
* price: 'pricing.basePrice'
|
|
4203
|
+
* })
|
|
4204
|
+
* .limit(20)
|
|
4205
|
+
* .execute()
|
|
4206
|
+
* ```
|
|
4207
|
+
*/
|
|
4208
|
+
adhoc() {
|
|
4209
|
+
return this._adhocQuery.build();
|
|
4210
|
+
}
|
|
4211
|
+
/**
|
|
4212
|
+
* Execute a predefined query by name (from schema)
|
|
4213
|
+
* Type-safe when schema is provided: autocompletes query names
|
|
4214
|
+
*
|
|
4215
|
+
* @example
|
|
4216
|
+
* ```typescript
|
|
4217
|
+
* const featured = await relyio.executeQuery('featured-products', {
|
|
4218
|
+
* limit: 10,
|
|
4219
|
+
* category: 'electronics'
|
|
4220
|
+
* })
|
|
4221
|
+
* ```
|
|
4222
|
+
*/
|
|
4223
|
+
async executeQuery(queryName, params) {
|
|
4224
|
+
const response = await this.httpClient.request({
|
|
4225
|
+
method: "GET",
|
|
4226
|
+
url: `/${API_VERSION_SEGMENT}/${String(queryName)}`,
|
|
4227
|
+
params: params ?? {}
|
|
4228
|
+
});
|
|
4229
|
+
const payload = readPayload(response.body);
|
|
4230
|
+
return Array.isArray(payload) ? payload : [payload];
|
|
4231
|
+
}
|
|
4232
|
+
/**
|
|
4233
|
+
* Get schema information from the API
|
|
4234
|
+
*/
|
|
4235
|
+
async getSchemaInfo(contentType) {
|
|
4236
|
+
return contentType == null ? this.httpClient.fetchPayload("/v1/meta/schema", "get", {}) : this.httpClient.fetchPayload("/v1/meta/schema/{contentType}", "get", {
|
|
4237
|
+
path: { contentType }
|
|
4238
|
+
});
|
|
4239
|
+
}
|
|
4240
|
+
/**
|
|
4241
|
+
* List all content types from the API
|
|
4242
|
+
*/
|
|
4243
|
+
async listContentTypes() {
|
|
4244
|
+
return this.httpClient.fetchPayload("/v1/meta/content-types", "get", {});
|
|
4245
|
+
}
|
|
4246
|
+
/**
|
|
4247
|
+
* Get API health status
|
|
4248
|
+
*/
|
|
4249
|
+
async health() {
|
|
4250
|
+
return this.httpClient.fetchPayload("/health", "get", {});
|
|
4251
|
+
}
|
|
4252
|
+
/**
|
|
4253
|
+
* Check if the API is ready
|
|
4254
|
+
*/
|
|
4255
|
+
async ready() {
|
|
4256
|
+
try {
|
|
4257
|
+
const payload = await this.httpClient.fetchPayload("/health/ready", "get", {});
|
|
4258
|
+
return payload.status === "ok";
|
|
4259
|
+
} catch {
|
|
4260
|
+
return false;
|
|
4261
|
+
}
|
|
4262
|
+
}
|
|
4263
|
+
/**
|
|
4264
|
+
* Test the connection to the API
|
|
4265
|
+
*/
|
|
4266
|
+
async testConnection() {
|
|
4267
|
+
try {
|
|
4268
|
+
await this.health();
|
|
4269
|
+
return true;
|
|
4270
|
+
} catch {
|
|
4271
|
+
return false;
|
|
4272
|
+
}
|
|
4273
|
+
}
|
|
4274
|
+
/**
|
|
4275
|
+
* Update the configuration and reinitialize all clients
|
|
4276
|
+
*/
|
|
4277
|
+
updateConfig(newConfig) {
|
|
4278
|
+
this.config = mergeConfig({ ...this.config, ...newConfig });
|
|
4279
|
+
this.httpClient = new HttpClient(this.config);
|
|
4280
|
+
this._search = new SearchClient(this.httpClient);
|
|
4281
|
+
this._batch = new BatchClient(this.httpClient);
|
|
4282
|
+
this._adhocQuery = new AdhocQueryClient(this.httpClient);
|
|
4283
|
+
}
|
|
4284
|
+
/**
|
|
4285
|
+
* Get the current configuration
|
|
4286
|
+
*/
|
|
4287
|
+
getConfig() {
|
|
4288
|
+
return { ...this.config };
|
|
4289
|
+
}
|
|
4290
|
+
/**
|
|
4291
|
+
* Set the API key
|
|
4292
|
+
*/
|
|
4293
|
+
setApiKey(apiKey) {
|
|
4294
|
+
this.config.apiKey = apiKey;
|
|
4295
|
+
this.httpClient.setAuthToken(apiKey);
|
|
4296
|
+
}
|
|
4297
|
+
/**
|
|
4298
|
+
* Get the HTTP client instance (for advanced usage)
|
|
4299
|
+
*/
|
|
4300
|
+
getHttpClient() {
|
|
4301
|
+
return this.httpClient;
|
|
4302
|
+
}
|
|
4303
|
+
// ============================================================================
|
|
4304
|
+
// LOCALE OPERATIONS
|
|
4305
|
+
// ============================================================================
|
|
4306
|
+
/**
|
|
4307
|
+
* Create a locale-scoped client
|
|
4308
|
+
* All content operations on the returned client will use the specified locale
|
|
4309
|
+
*
|
|
4310
|
+
* Type-safe when schema is provided: autocompletes valid locale codes!
|
|
4311
|
+
*
|
|
4312
|
+
* @example
|
|
4313
|
+
* ```typescript
|
|
4314
|
+
* const spanish = relyio.withLocale('es-ES') // ← Autocompletes with schema!
|
|
4315
|
+
*
|
|
4316
|
+
* // All operations use Spanish
|
|
4317
|
+
* const products = await spanish.content('product').query().execute()
|
|
4318
|
+
* const homepage = await spanish.singleton('homepage').fetch()
|
|
4319
|
+
*
|
|
4320
|
+
* // Original client unchanged
|
|
4321
|
+
* const englishProducts = await relyio.content('product').query().execute()
|
|
4322
|
+
* ```
|
|
4323
|
+
*/
|
|
4324
|
+
withLocale(locale) {
|
|
4325
|
+
const newConfig = {
|
|
4326
|
+
...this.getConfig(),
|
|
4327
|
+
locale,
|
|
4328
|
+
schema: this._schema
|
|
4329
|
+
};
|
|
4330
|
+
return new _TypedRelyIOClient(newConfig);
|
|
4331
|
+
}
|
|
4332
|
+
/**
|
|
4333
|
+
* Get the current default locale
|
|
4334
|
+
*/
|
|
4335
|
+
getLocale() {
|
|
4336
|
+
return this.config.locale;
|
|
4337
|
+
}
|
|
4338
|
+
/**
|
|
4339
|
+
* Get available locales from the API
|
|
4340
|
+
*
|
|
4341
|
+
* @example
|
|
4342
|
+
* ```typescript
|
|
4343
|
+
* const { locales, defaultLocale } = await relyio.getLocales()
|
|
4344
|
+
*
|
|
4345
|
+
* // Build language switcher
|
|
4346
|
+
* const options = locales.map(l => ({
|
|
4347
|
+
* code: l.code,
|
|
4348
|
+
* label: l.displayName,
|
|
4349
|
+
* isDefault: l.default,
|
|
4350
|
+
* }))
|
|
4351
|
+
* ```
|
|
4352
|
+
*/
|
|
4353
|
+
async getLocales(search) {
|
|
4354
|
+
const params = {};
|
|
4355
|
+
if (search !== void 0 && search !== "") {
|
|
4356
|
+
params.search = search;
|
|
4357
|
+
}
|
|
4358
|
+
return this.httpClient.fetchPayload("/v1/meta/locales", "get", { query: params });
|
|
4359
|
+
}
|
|
4360
|
+
/**
|
|
4361
|
+
* Get the default locale from the API
|
|
4362
|
+
*/
|
|
4363
|
+
async getDefaultLocale() {
|
|
4364
|
+
const { defaultLocale } = await this.getLocales();
|
|
4365
|
+
return defaultLocale;
|
|
4366
|
+
}
|
|
4367
|
+
};
|
|
4368
|
+
chunkMWMSFIWN_cjs.__name(_TypedRelyIOClient, "TypedRelyIOClient");
|
|
4369
|
+
var TypedRelyIOClient = _TypedRelyIOClient;
|
|
4370
|
+
function createClient(config) {
|
|
4371
|
+
return new TypedRelyIOClient(config);
|
|
4372
|
+
}
|
|
4373
|
+
chunkMWMSFIWN_cjs.__name(createClient, "createClient");
|
|
4374
|
+
|
|
4375
|
+
exports.AdhocQueryBuilder = AdhocQueryBuilder;
|
|
4376
|
+
exports.AdhocQueryClient = AdhocQueryClient;
|
|
4377
|
+
exports.AssetClient = AssetClient;
|
|
4378
|
+
exports.AssetFileBuilder = AssetFileBuilder;
|
|
4379
|
+
exports.AssetUploadBuilder = AssetUploadBuilder;
|
|
4380
|
+
exports.AuthenticationError = AuthenticationError;
|
|
4381
|
+
exports.AuthorizationError = AuthorizationError;
|
|
4382
|
+
exports.AutocompleteBuilder = AutocompleteBuilder;
|
|
4383
|
+
exports.BaseValidationError = BaseValidationError;
|
|
4384
|
+
exports.BatchBuilder = BatchBuilder;
|
|
4385
|
+
exports.BatchClient = BatchClient;
|
|
4386
|
+
exports.ConnectionError = ConnectionError;
|
|
4387
|
+
exports.ContentByIdBuilder = ContentByIdBuilder;
|
|
4388
|
+
exports.ContentClient = ContentClient;
|
|
4389
|
+
exports.ContentQueryBuilder = ContentQueryBuilder;
|
|
4390
|
+
exports.ErrorMapper = ErrorMapper;
|
|
4391
|
+
exports.FileUploadError = FileUploadError;
|
|
4392
|
+
exports.FileValidationError = FileValidationError;
|
|
4393
|
+
exports.HttpClient = HttpClient;
|
|
4394
|
+
exports.InvalidApiKeyError = InvalidApiKeyError;
|
|
4395
|
+
exports.InvalidFieldTypeError = InvalidFieldTypeError;
|
|
4396
|
+
exports.InvalidPayloadError = InvalidPayloadError;
|
|
4397
|
+
exports.MethodNotAllowedError = MethodNotAllowedError;
|
|
4398
|
+
exports.MissingApiKeyError = MissingApiKeyError;
|
|
4399
|
+
exports.NetworkError = NetworkError;
|
|
4400
|
+
exports.PayloadTooLargeError = PayloadTooLargeError;
|
|
4401
|
+
exports.PlanFeatureUnavailableError = PlanFeatureUnavailableError;
|
|
4402
|
+
exports.RateLimitError = RateLimitError;
|
|
4403
|
+
exports.ReadOnlyKeyError = ReadOnlyKeyError;
|
|
4404
|
+
exports.RequiredFieldError = RequiredFieldError;
|
|
4405
|
+
exports.ResourceConflictError = ResourceConflictError;
|
|
4406
|
+
exports.ResourceExpiredError = ResourceExpiredError;
|
|
4407
|
+
exports.ResourceNotFoundError = ResourceNotFoundError;
|
|
4408
|
+
exports.SDKError = SDKError;
|
|
4409
|
+
exports.SchemaValidationError = SchemaValidationError;
|
|
4410
|
+
exports.SearchBuilder = SearchBuilder;
|
|
4411
|
+
exports.SearchClient = SearchClient;
|
|
4412
|
+
exports.ServerError = ServerError;
|
|
4413
|
+
exports.ServiceIndeterminateError = ServiceIndeterminateError;
|
|
4414
|
+
exports.SettingsClient = SettingsClient;
|
|
4415
|
+
exports.SimilarBuilder = SimilarBuilder;
|
|
4416
|
+
exports.SingletonClient = SingletonClient;
|
|
4417
|
+
exports.TimeoutError = TimeoutError;
|
|
4418
|
+
exports.TransactionRolledBackError = TransactionRolledBackError;
|
|
4419
|
+
exports.TypedRelyIOClient = TypedRelyIOClient;
|
|
4420
|
+
exports.UnknownContractError = UnknownContractError;
|
|
4421
|
+
exports.UsageQuotaExceededError = UsageQuotaExceededError;
|
|
4422
|
+
exports.ValidationError = ValidationError;
|
|
4423
|
+
exports.createClient = createClient;
|
|
4424
|
+
exports.entityTagOf = entityTagOf;
|
|
4425
|
+
exports.isAssetEntry = isAssetEntry;
|
|
4426
|
+
exports.isResourceNotFound = isResourceNotFound;
|
|
4427
|
+
exports.validateFile = validateFile;
|
|
4428
|
+
//# sourceMappingURL=chunk-7W4MJXQ4.cjs.map
|
|
4429
|
+
//# sourceMappingURL=chunk-7W4MJXQ4.cjs.map
|