tale-js-sdk 0.1.2 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/acl/index.d.ts +170 -0
  2. package/dist/acl/index.js +747 -0
  3. package/dist/acl/types.d.ts +208 -0
  4. package/dist/acl/types.js +1 -0
  5. package/dist/auth/index.d.ts +2 -134
  6. package/dist/auth/index.js +120 -96
  7. package/dist/auth/types.d.ts +122 -0
  8. package/dist/auth/types.js +1 -0
  9. package/dist/common/types.d.ts +76 -0
  10. package/dist/common/types.js +3 -0
  11. package/dist/errors.js +18 -18
  12. package/dist/index.d.ts +2 -0
  13. package/dist/index.js +2 -0
  14. package/dist/rbac/acl.d.ts +152 -0
  15. package/dist/rbac/acl.js +723 -0
  16. package/dist/rbac/index.d.ts +2 -0
  17. package/dist/rbac/index.js +2 -0
  18. package/dist/rbac/rbac.d.ts +198 -0
  19. package/dist/rbac/rbac.js +984 -0
  20. package/dist/rbac/types.d.ts +356 -0
  21. package/dist/rbac/types.js +1 -0
  22. package/dist/rbac/user-group.d.ts +122 -0
  23. package/dist/rbac/user-group.js +570 -0
  24. package/dist/status.js +3 -3
  25. package/dist/token.d.ts +1 -1
  26. package/dist/token.js +5 -4
  27. package/dist/user/index.d.ts +2 -142
  28. package/dist/user/index.js +60 -59
  29. package/dist/user/types.d.ts +96 -0
  30. package/dist/user/types.js +1 -0
  31. package/dist/user-group/index.d.ts +230 -0
  32. package/dist/user-group/index.js +560 -0
  33. package/dist/user-group/types.d.ts +61 -0
  34. package/dist/user-group/types.js +1 -0
  35. package/package.json +13 -14
  36. package/dist/auth.d.ts +0 -271
  37. package/dist/auth.js +0 -461
  38. package/dist/client.d.ts +0 -20
  39. package/dist/client.js +0 -62
  40. package/dist/info.d.ts +0 -9
  41. package/dist/info.js +0 -18
  42. package/dist/package.json +0 -36
  43. package/dist/src/index.d.ts +0 -1
  44. package/dist/src/index.js +0 -1
  45. package/dist/src/info.d.ts +0 -6
  46. package/dist/src/info.js +0 -4
  47. package/dist/user.d.ts +0 -242
  48. package/dist/user.js +0 -331
@@ -0,0 +1,723 @@
1
+ import { getAppToken } from '../token.js';
2
+ import { ApiError, ConfigurationError, NetworkError } from '../errors.js';
3
+ // ===== ACL Record 管理 =====
4
+ /**
5
+ * Creates a new ACL record in the Tale application.
6
+ *
7
+ * @param recordData - ACL record data to create
8
+ * @param options - Optional configuration for the request
9
+ * @returns Promise resolving to the created ACL record information
10
+ * @throws {ConfigurationError} When required environment variables are missing
11
+ * @throws {ApiError} When API request fails or returns invalid response
12
+ * @throws {NetworkError} When network request fails
13
+ *
14
+ * @example
15
+ * ```typescript
16
+ * import { acl } from '@tale/client';
17
+ *
18
+ * try {
19
+ * const result = await acl.createRecord({
20
+ * template_id: 'template_user_read',
21
+ * subject_type: 'user',
22
+ * subject_id: 'user_123',
23
+ * resource_type: 'document',
24
+ * resource_id: 'doc_456',
25
+ * effect_type: 'allow',
26
+ * priority: 50,
27
+ * description: 'Allow user to read document'
28
+ * });
29
+ * console.log('ACL record created:', result.record.record_id);
30
+ * } catch (error) {
31
+ * console.error('Failed to create ACL record:', error.message);
32
+ * }
33
+ * ```
34
+ */
35
+ export async function createRecord(recordData, options) {
36
+ // Validate required fields
37
+ if (!recordData.template_id || recordData.template_id.trim() === '') {
38
+ throw new ApiError('template_id is required for ACL record creation', 400, '9400');
39
+ }
40
+ if (!recordData.subject_type || !['user', 'role', 'group'].includes(recordData.subject_type)) {
41
+ throw new ApiError('subject_type is required and must be one of: user, role, group', 400, '9400');
42
+ }
43
+ if (!recordData.resource_type || recordData.resource_type.trim() === '') {
44
+ throw new ApiError('resource_type is required for ACL record creation', 400, '9400');
45
+ }
46
+ if (!recordData.subject_id && !recordData.subject_identifier) {
47
+ throw new ApiError('Either subject_id or subject_identifier is required', 400, '9400');
48
+ }
49
+ if (!recordData.resource_id && !recordData.resource_identifier) {
50
+ throw new ApiError('Either resource_id or resource_identifier is required', 400, '9400');
51
+ }
52
+ if (!recordData.effect_type || !['allow', 'deny', 'inherit'].includes(recordData.effect_type)) {
53
+ throw new ApiError('effect_type is required and must be one of: allow, deny, inherit', 400, '9400');
54
+ }
55
+ // Use provided app token or get one from token service
56
+ const token = options?.appToken ?? await getAppToken(options);
57
+ // Determine base URL
58
+ const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
59
+ const base = options?.baseUrl ?? env?.TALE_BASE_URL ?? undefined;
60
+ if (!base) {
61
+ throw new ConfigurationError('Missing required environment variable: TALE_BASE_URL');
62
+ }
63
+ const url = String(base).replace(/\/+$/, '') + '/acl/v1/records';
64
+ // Validate priority range
65
+ if (recordData.priority !== undefined && (recordData.priority < 0 || recordData.priority > 100)) {
66
+ throw new ApiError('priority must be between 0 and 100', 400, '9400');
67
+ }
68
+ // Set default priority if not provided
69
+ const finalRecordData = {
70
+ ...recordData,
71
+ priority: recordData.priority ?? 50
72
+ };
73
+ let response;
74
+ try {
75
+ response = await globalThis.fetch(url, {
76
+ method: 'POST',
77
+ headers: {
78
+ 'Content-Type': 'application/json',
79
+ 'x-t-token': token,
80
+ },
81
+ body: JSON.stringify(finalRecordData),
82
+ });
83
+ }
84
+ catch (error) {
85
+ throw new NetworkError(`Failed to create ACL record: ${error instanceof Error ? error.message : 'Unknown error'}`);
86
+ }
87
+ let json;
88
+ try {
89
+ const responseJson = await response.json();
90
+ json = responseJson;
91
+ }
92
+ catch (error) {
93
+ throw new ApiError(`Failed to parse ACL record creation response: ${error instanceof Error ? error.message : 'Invalid JSON'}`, response.status);
94
+ }
95
+ // Handle API errors
96
+ if (json.code !== 200) {
97
+ const errorMsg = typeof json.msg === 'string' ? json.msg : 'ACL record creation failed';
98
+ throw new ApiError(errorMsg, response.status, json.code);
99
+ }
100
+ // Validate response structure
101
+ if (!json.data || !json.data.record) {
102
+ throw new ApiError('Invalid ACL record creation response: missing record data', response.status);
103
+ }
104
+ return json.data;
105
+ }
106
+ /**
107
+ * Updates an existing ACL record by ID.
108
+ *
109
+ * @param recordId - ACL record ID to update
110
+ * @param updateData - ACL record information to update
111
+ * @param options - Optional configuration for the request
112
+ * @returns Promise resolving to the updated ACL record information
113
+ * @throws {ConfigurationError} When required environment variables are missing
114
+ * @throws {ApiError} When API request fails or returns invalid response
115
+ * @throws {NetworkError} When network request fails
116
+ */
117
+ export async function updateRecord(recordId, updateData, options) {
118
+ // Validate required fields
119
+ if (!recordId || recordId.trim() === '') {
120
+ throw new ApiError('record_id is required for ACL record update', 400, '9400');
121
+ }
122
+ if (updateData.effect_type && !['allow', 'deny', 'inherit'].includes(updateData.effect_type)) {
123
+ throw new ApiError('effect_type must be one of: allow, deny, inherit', 400, '9400');
124
+ }
125
+ // Validate priority range
126
+ if (updateData.priority !== undefined && (updateData.priority < 0 || updateData.priority > 100)) {
127
+ throw new ApiError('priority must be between 0 and 100', 400, '9400');
128
+ }
129
+ // Use provided app token or get one from token service
130
+ const token = options?.appToken ?? await getAppToken(options);
131
+ // Determine base URL
132
+ const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
133
+ const base = options?.baseUrl ?? env?.TALE_BASE_URL ?? undefined;
134
+ if (!base) {
135
+ throw new ConfigurationError('Missing required environment variable: TALE_BASE_URL');
136
+ }
137
+ const url = String(base).replace(/\/+$/, '') + `/acl/v1/records/${encodeURIComponent(recordId)}`;
138
+ let response;
139
+ try {
140
+ response = await globalThis.fetch(url, {
141
+ method: 'PUT',
142
+ headers: {
143
+ 'Content-Type': 'application/json',
144
+ 'x-t-token': token,
145
+ },
146
+ body: JSON.stringify(updateData),
147
+ });
148
+ }
149
+ catch (error) {
150
+ throw new NetworkError(`Failed to update ACL record: ${error instanceof Error ? error.message : 'Unknown error'}`);
151
+ }
152
+ let json;
153
+ try {
154
+ const responseJson = await response.json();
155
+ json = responseJson;
156
+ }
157
+ catch (error) {
158
+ throw new ApiError(`Failed to parse ACL record update response: ${error instanceof Error ? error.message : 'Invalid JSON'}`, response.status);
159
+ }
160
+ // Handle API errors
161
+ if (json.code !== 200) {
162
+ const errorMsg = typeof json.msg === 'string' ? json.msg : 'ACL record update failed';
163
+ throw new ApiError(errorMsg, response.status, json.code);
164
+ }
165
+ // Validate response structure
166
+ if (!json.data || !json.data.record) {
167
+ throw new ApiError('Invalid ACL record update response: missing record data', response.status);
168
+ }
169
+ return json.data;
170
+ }
171
+ /**
172
+ * Deletes an ACL record by ID.
173
+ *
174
+ * @param recordId - ACL record ID to delete
175
+ * @param options - Optional configuration for the request
176
+ * @returns Promise resolving to the deletion result
177
+ * @throws {ConfigurationError} When required environment variables are missing
178
+ * @throws {ApiError} When API request fails or returns invalid response
179
+ * @throws {NetworkError} When network request fails
180
+ */
181
+ export async function deleteRecord(recordId, options) {
182
+ // Validate required fields
183
+ if (!recordId || recordId.trim() === '') {
184
+ throw new ApiError('record_id is required for ACL record deletion', 400, '9400');
185
+ }
186
+ // Use provided app token or get one from token service
187
+ const token = options?.appToken ?? await getAppToken(options);
188
+ // Determine base URL
189
+ const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
190
+ const base = options?.baseUrl ?? env?.TALE_BASE_URL ?? undefined;
191
+ if (!base) {
192
+ throw new ConfigurationError('Missing required environment variable: TALE_BASE_URL');
193
+ }
194
+ const url = String(base).replace(/\/+$/, '') + `/acl/v1/records/${encodeURIComponent(recordId)}`;
195
+ let response;
196
+ try {
197
+ response = await globalThis.fetch(url, {
198
+ method: 'DELETE',
199
+ headers: {
200
+ 'Content-Type': 'application/json',
201
+ 'x-t-token': token,
202
+ },
203
+ });
204
+ }
205
+ catch (error) {
206
+ throw new NetworkError(`Failed to delete ACL record: ${error instanceof Error ? error.message : 'Unknown error'}`);
207
+ }
208
+ let json;
209
+ try {
210
+ const responseJson = await response.json();
211
+ json = responseJson;
212
+ }
213
+ catch (error) {
214
+ throw new ApiError(`Failed to parse ACL record deletion response: ${error instanceof Error ? error.message : 'Invalid JSON'}`, response.status);
215
+ }
216
+ // Handle API errors
217
+ if (json.code !== 200) {
218
+ const errorMsg = typeof json.msg === 'string' ? json.msg : 'ACL record deletion failed';
219
+ throw new ApiError(errorMsg, response.status, json.code);
220
+ }
221
+ // Validate response structure
222
+ if (!json.data || json.data.deleted !== true) {
223
+ throw new ApiError('Invalid ACL record deletion response: deletion not confirmed', response.status);
224
+ }
225
+ return json.data;
226
+ }
227
+ /**
228
+ * Retrieves ACL record information by ID.
229
+ *
230
+ * @param recordId - ACL record ID to query
231
+ * @param options - Optional configuration for the request
232
+ * @returns Promise resolving to the ACL record information
233
+ * @throws {ConfigurationError} When required environment variables are missing
234
+ * @throws {ApiError} When API request fails or returns invalid response
235
+ * @throws {NetworkError} When network request fails
236
+ */
237
+ export async function getRecordById(recordId, options) {
238
+ // Validate required fields
239
+ if (!recordId || recordId.trim() === '') {
240
+ throw new ApiError('record_id is required for ACL record query', 400, '9400');
241
+ }
242
+ // Use provided app token or get one from token service
243
+ const token = options?.appToken ?? await getAppToken(options);
244
+ // Determine base URL
245
+ const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
246
+ const base = options?.baseUrl ?? env?.TALE_BASE_URL ?? undefined;
247
+ if (!base) {
248
+ throw new ConfigurationError('Missing required environment variable: TALE_BASE_URL');
249
+ }
250
+ const url = String(base).replace(/\/+$/, '') + `/acl/v1/records/${encodeURIComponent(recordId)}`;
251
+ let response;
252
+ try {
253
+ response = await globalThis.fetch(url, {
254
+ method: 'GET',
255
+ headers: {
256
+ 'Content-Type': 'application/json',
257
+ 'x-t-token': token,
258
+ },
259
+ });
260
+ }
261
+ catch (error) {
262
+ throw new NetworkError(`Failed to get ACL record: ${error instanceof Error ? error.message : 'Unknown error'}`);
263
+ }
264
+ let json;
265
+ try {
266
+ const responseJson = await response.json();
267
+ json = responseJson;
268
+ }
269
+ catch (error) {
270
+ throw new ApiError(`Failed to parse ACL record response: ${error instanceof Error ? error.message : 'Invalid JSON'}`, response.status);
271
+ }
272
+ // Handle API errors
273
+ if (json.code !== 200) {
274
+ const errorMsg = typeof json.msg === 'string' ? json.msg : 'ACL record retrieval failed';
275
+ throw new ApiError(errorMsg, response.status, json.code);
276
+ }
277
+ // Validate response structure
278
+ if (!json.data || !json.data.record) {
279
+ throw new ApiError('Invalid ACL record response: missing record data', response.status);
280
+ }
281
+ return json.data;
282
+ }
283
+ /**
284
+ * Lists ACL records with pagination and filtering.
285
+ *
286
+ * @param options - Optional parameters for pagination, filtering, and configuration
287
+ * @returns Promise resolving to paginated ACL record list with metadata
288
+ * @throws {ConfigurationError} When required environment variables are missing
289
+ * @throws {ApiError} When API request fails or returns invalid response
290
+ * @throws {NetworkError} When network request fails
291
+ */
292
+ export async function listRecords(options) {
293
+ // Use provided app token or get one from token service
294
+ const token = options?.appToken ?? await getAppToken(options);
295
+ // Determine base URL
296
+ const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
297
+ const base = options?.baseUrl ?? env?.TALE_BASE_URL ?? undefined;
298
+ if (!base) {
299
+ throw new ConfigurationError('Missing required environment variable: TALE_BASE_URL');
300
+ }
301
+ // Build URL with query parameters
302
+ const url = new URL(String(base).replace(/\/+$/, '') + '/acl/v1/records');
303
+ // Add query parameters with defaults
304
+ const queryParams = {
305
+ page: 0,
306
+ size: 20,
307
+ sort_by: 'priority',
308
+ sort_direction: 'desc',
309
+ ...options
310
+ };
311
+ // Add parameters to URL
312
+ if (queryParams.template_id) {
313
+ url.searchParams.append('template_id', queryParams.template_id);
314
+ }
315
+ if (queryParams.subject_type) {
316
+ url.searchParams.append('subject_type', queryParams.subject_type);
317
+ }
318
+ if (queryParams.subject_id) {
319
+ url.searchParams.append('subject_id', queryParams.subject_id);
320
+ }
321
+ if (queryParams.subject_identifier) {
322
+ url.searchParams.append('subject_identifier', queryParams.subject_identifier);
323
+ }
324
+ if (queryParams.resource_type) {
325
+ url.searchParams.append('resource_type', queryParams.resource_type);
326
+ }
327
+ if (queryParams.resource_id) {
328
+ url.searchParams.append('resource_id', queryParams.resource_id);
329
+ }
330
+ if (queryParams.resource_identifier) {
331
+ url.searchParams.append('resource_identifier', queryParams.resource_identifier);
332
+ }
333
+ if (queryParams.page !== undefined) {
334
+ url.searchParams.append('page', String(queryParams.page));
335
+ }
336
+ if (queryParams.size !== undefined) {
337
+ url.searchParams.append('size', String(queryParams.size));
338
+ }
339
+ if (queryParams.sort_by) {
340
+ url.searchParams.append('sort', `${queryParams.sort_by},${queryParams.sort_direction || 'desc'}`);
341
+ }
342
+ let response;
343
+ try {
344
+ response = await globalThis.fetch(url.toString(), {
345
+ method: 'GET',
346
+ headers: {
347
+ 'Content-Type': 'application/json',
348
+ 'x-t-token': token,
349
+ },
350
+ });
351
+ }
352
+ catch (error) {
353
+ throw new NetworkError(`Failed to list ACL records: ${error instanceof Error ? error.message : 'Unknown error'}`);
354
+ }
355
+ let json;
356
+ try {
357
+ const responseJson = await response.json();
358
+ json = responseJson;
359
+ }
360
+ catch (error) {
361
+ throw new ApiError(`Failed to parse ACL records list response: ${error instanceof Error ? error.message : 'Invalid JSON'}`, response.status);
362
+ }
363
+ // Handle API errors
364
+ if (json.code !== 200) {
365
+ const errorMsg = typeof json.msg === 'string' ? json.msg : 'ACL records list retrieval failed';
366
+ throw new ApiError(errorMsg, response.status, json.code);
367
+ }
368
+ // Validate response structure
369
+ if (!json.data || !Array.isArray(json.data.content)) {
370
+ throw new ApiError('Invalid ACL records list response: missing required data', response.status);
371
+ }
372
+ return json.data;
373
+ }
374
+ // ===== ACL Template 管理 =====
375
+ /**
376
+ * Creates a new ACL template in the Tale application.
377
+ *
378
+ * @param templateData - ACL template data to create
379
+ * @param options - Optional configuration for the request
380
+ * @returns Promise resolving to the created ACL template information
381
+ * @throws {ConfigurationError} When required environment variables are missing
382
+ * @throws {ApiError} When API request fails or returns invalid response
383
+ * @throws {NetworkError} When network request fails
384
+ *
385
+ * @example
386
+ * ```typescript
387
+ * import { acl } from '@tale/client';
388
+ *
389
+ * try {
390
+ * const result = await acl.createTemplate({
391
+ * template_name: 'User Document Read Template',
392
+ * template_code: 'user_doc_read',
393
+ * subject_type: 'user',
394
+ * resource_type: 'document',
395
+ * effect_type: 'allow',
396
+ * default_priority: 30,
397
+ * description: 'Template for allowing users to read documents'
398
+ * });
399
+ * console.log('ACL template created:', result.template.template_id);
400
+ * } catch (error) {
401
+ * console.error('Failed to create ACL template:', error.message);
402
+ * }
403
+ * ```
404
+ */
405
+ export async function createTemplate(templateData, options) {
406
+ // Validate required fields
407
+ if (!templateData.template_name || templateData.template_name.trim() === '') {
408
+ throw new ApiError('template_name is required for ACL template creation', 400, '9400');
409
+ }
410
+ if (!templateData.template_code || templateData.template_code.trim() === '') {
411
+ throw new ApiError('template_code is required for ACL template creation', 400, '9400');
412
+ }
413
+ if (templateData.effect_type && !['allow', 'deny', 'inherit'].includes(templateData.effect_type)) {
414
+ throw new ApiError('effect_type must be one of: allow, deny, inherit', 400, '9400');
415
+ }
416
+ // Validate default priority range
417
+ if (templateData.default_priority !== undefined && (templateData.default_priority < 0 || templateData.default_priority > 100)) {
418
+ throw new ApiError('default_priority must be between 0 and 100', 400, '9400');
419
+ }
420
+ // Use provided app token or get one from token service
421
+ const token = options?.appToken ?? await getAppToken(options);
422
+ // Determine base URL
423
+ const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
424
+ const base = options?.baseUrl ?? env?.TALE_BASE_URL ?? undefined;
425
+ if (!base) {
426
+ throw new ConfigurationError('Missing required environment variable: TALE_BASE_URL');
427
+ }
428
+ const url = String(base).replace(/\/+$/, '') + '/acl/v1/templates';
429
+ // Set default values
430
+ const finalTemplateData = {
431
+ ...templateData,
432
+ default_priority: templateData.default_priority ?? 50,
433
+ effect_type: templateData.effect_type ?? 'allow'
434
+ };
435
+ let response;
436
+ try {
437
+ response = await globalThis.fetch(url, {
438
+ method: 'POST',
439
+ headers: {
440
+ 'Content-Type': 'application/json',
441
+ 'x-t-token': token,
442
+ },
443
+ body: JSON.stringify(finalTemplateData),
444
+ });
445
+ }
446
+ catch (error) {
447
+ throw new NetworkError(`Failed to create ACL template: ${error instanceof Error ? error.message : 'Unknown error'}`);
448
+ }
449
+ let json;
450
+ try {
451
+ const responseJson = await response.json();
452
+ json = responseJson;
453
+ }
454
+ catch (error) {
455
+ throw new ApiError(`Failed to parse ACL template creation response: ${error instanceof Error ? error.message : 'Invalid JSON'}`, response.status);
456
+ }
457
+ // Handle API errors
458
+ if (json.code !== 200) {
459
+ const errorMsg = typeof json.msg === 'string' ? json.msg : 'ACL template creation failed';
460
+ throw new ApiError(errorMsg, response.status, json.code);
461
+ }
462
+ // Validate response structure
463
+ if (!json.data || !json.data.template) {
464
+ throw new ApiError('Invalid ACL template creation response: missing template data', response.status);
465
+ }
466
+ return json.data;
467
+ }
468
+ /**
469
+ * Updates an existing ACL template by ID.
470
+ *
471
+ * @param templateId - ACL template ID or code to update
472
+ * @param updateData - ACL template information to update
473
+ * @param options - Optional configuration for the request
474
+ * @returns Promise resolving to the updated ACL template information
475
+ * @throws {ConfigurationError} When required environment variables are missing
476
+ * @throws {ApiError} When API request fails or returns invalid response
477
+ * @throws {NetworkError} When network request fails
478
+ */
479
+ export async function updateTemplate(templateId, updateData, options) {
480
+ // Validate required fields
481
+ if (!templateId || templateId.trim() === '') {
482
+ throw new ApiError('template_id is required for ACL template update', 400, '9400');
483
+ }
484
+ if (updateData.effect_type && !['allow', 'deny', 'inherit'].includes(updateData.effect_type)) {
485
+ throw new ApiError('effect_type must be one of: allow, deny, inherit', 400, '9400');
486
+ }
487
+ // Validate default priority range
488
+ if (updateData.default_priority !== undefined && (updateData.default_priority < 0 || updateData.default_priority > 100)) {
489
+ throw new ApiError('default_priority must be between 0 and 100', 400, '9400');
490
+ }
491
+ // Use provided app token or get one from token service
492
+ const token = options?.appToken ?? await getAppToken(options);
493
+ // Determine base URL
494
+ const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
495
+ const base = options?.baseUrl ?? env?.TALE_BASE_URL ?? undefined;
496
+ if (!base) {
497
+ throw new ConfigurationError('Missing required environment variable: TALE_BASE_URL');
498
+ }
499
+ const url = String(base).replace(/\/+$/, '') + `/acl/v1/templates/${encodeURIComponent(templateId)}`;
500
+ let response;
501
+ try {
502
+ response = await globalThis.fetch(url, {
503
+ method: 'PUT',
504
+ headers: {
505
+ 'Content-Type': 'application/json',
506
+ 'x-t-token': token,
507
+ },
508
+ body: JSON.stringify(updateData),
509
+ });
510
+ }
511
+ catch (error) {
512
+ throw new NetworkError(`Failed to update ACL template: ${error instanceof Error ? error.message : 'Unknown error'}`);
513
+ }
514
+ let json;
515
+ try {
516
+ const responseJson = await response.json();
517
+ json = responseJson;
518
+ }
519
+ catch (error) {
520
+ throw new ApiError(`Failed to parse ACL template update response: ${error instanceof Error ? error.message : 'Invalid JSON'}`, response.status);
521
+ }
522
+ // Handle API errors
523
+ if (json.code !== 200) {
524
+ const errorMsg = typeof json.msg === 'string' ? json.msg : 'ACL template update failed';
525
+ throw new ApiError(errorMsg, response.status, json.code);
526
+ }
527
+ // Validate response structure
528
+ if (!json.data || !json.data.template) {
529
+ throw new ApiError('Invalid ACL template update response: missing template data', response.status);
530
+ }
531
+ return json.data;
532
+ }
533
+ /**
534
+ * Deletes an ACL template by ID.
535
+ *
536
+ * @param templateId - ACL template ID or code to delete
537
+ * @param options - Optional configuration for the request
538
+ * @returns Promise resolving to the deletion result
539
+ * @throws {ConfigurationError} When required environment variables are missing
540
+ * @throws {ApiError} When API request fails or returns invalid response
541
+ * @throws {NetworkError} When network request fails
542
+ */
543
+ export async function deleteTemplate(templateId, options) {
544
+ // Validate required fields
545
+ if (!templateId || templateId.trim() === '') {
546
+ throw new ApiError('template_id is required for ACL template deletion', 400, '9400');
547
+ }
548
+ // Use provided app token or get one from token service
549
+ const token = options?.appToken ?? await getAppToken(options);
550
+ // Determine base URL
551
+ const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
552
+ const base = options?.baseUrl ?? env?.TALE_BASE_URL ?? undefined;
553
+ if (!base) {
554
+ throw new ConfigurationError('Missing required environment variable: TALE_BASE_URL');
555
+ }
556
+ const url = String(base).replace(/\/+$/, '') + `/acl/v1/templates/${encodeURIComponent(templateId)}`;
557
+ let response;
558
+ try {
559
+ response = await globalThis.fetch(url, {
560
+ method: 'DELETE',
561
+ headers: {
562
+ 'Content-Type': 'application/json',
563
+ 'x-t-token': token,
564
+ },
565
+ });
566
+ }
567
+ catch (error) {
568
+ throw new NetworkError(`Failed to delete ACL template: ${error instanceof Error ? error.message : 'Unknown error'}`);
569
+ }
570
+ let json;
571
+ try {
572
+ const responseJson = await response.json();
573
+ json = responseJson;
574
+ }
575
+ catch (error) {
576
+ throw new ApiError(`Failed to parse ACL template deletion response: ${error instanceof Error ? error.message : 'Invalid JSON'}`, response.status);
577
+ }
578
+ // Handle API errors
579
+ if (json.code !== 200) {
580
+ const errorMsg = typeof json.msg === 'string' ? json.msg : 'ACL template deletion failed';
581
+ throw new ApiError(errorMsg, response.status, json.code);
582
+ }
583
+ // Validate response structure
584
+ if (!json.data || json.data.deleted !== true) {
585
+ throw new ApiError('Invalid ACL template deletion response: deletion not confirmed', response.status);
586
+ }
587
+ return json.data;
588
+ }
589
+ /**
590
+ * Retrieves ACL template information by ID or code.
591
+ *
592
+ * @param templateId - ACL template ID or code to query
593
+ * @param options - Optional configuration for the request
594
+ * @returns Promise resolving to the ACL template information
595
+ * @throws {ConfigurationError} When required environment variables are missing
596
+ * @throws {ApiError} When API request fails or returns invalid response
597
+ * @throws {NetworkError} When network request fails
598
+ */
599
+ export async function getTemplateById(templateId, options) {
600
+ // Validate required fields
601
+ if (!templateId || templateId.trim() === '') {
602
+ throw new ApiError('template_id is required for ACL template query', 400, '9400');
603
+ }
604
+ // Use provided app token or get one from token service
605
+ const token = options?.appToken ?? await getAppToken(options);
606
+ // Determine base URL
607
+ const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
608
+ const base = options?.baseUrl ?? env?.TALE_BASE_URL ?? undefined;
609
+ if (!base) {
610
+ throw new ConfigurationError('Missing required environment variable: TALE_BASE_URL');
611
+ }
612
+ const url = String(base).replace(/\/+$/, '') + `/acl/v1/templates/${encodeURIComponent(templateId)}`;
613
+ let response;
614
+ try {
615
+ response = await globalThis.fetch(url, {
616
+ method: 'GET',
617
+ headers: {
618
+ 'Content-Type': 'application/json',
619
+ 'x-t-token': token,
620
+ },
621
+ });
622
+ }
623
+ catch (error) {
624
+ throw new NetworkError(`Failed to get ACL template: ${error instanceof Error ? error.message : 'Unknown error'}`);
625
+ }
626
+ let json;
627
+ try {
628
+ const responseJson = await response.json();
629
+ json = responseJson;
630
+ }
631
+ catch (error) {
632
+ throw new ApiError(`Failed to parse ACL template response: ${error instanceof Error ? error.message : 'Invalid JSON'}`, response.status);
633
+ }
634
+ // Handle API errors
635
+ if (json.code !== 200) {
636
+ const errorMsg = typeof json.msg === 'string' ? json.msg : 'ACL template retrieval failed';
637
+ throw new ApiError(errorMsg, response.status, json.code);
638
+ }
639
+ // Validate response structure
640
+ if (!json.data || !json.data.template) {
641
+ throw new ApiError('Invalid ACL template response: missing template data', response.status);
642
+ }
643
+ return json.data;
644
+ }
645
+ /**
646
+ * Lists ACL templates with pagination and filtering.
647
+ *
648
+ * @param options - Optional parameters for pagination, filtering, and configuration
649
+ * @returns Promise resolving to paginated ACL template list with metadata
650
+ * @throws {ConfigurationError} When required environment variables are missing
651
+ * @throws {ApiError} When API request fails or returns invalid response
652
+ * @throws {NetworkError} When network request fails
653
+ */
654
+ export async function listTemplates(options) {
655
+ // Use provided app token or get one from token service
656
+ const token = options?.appToken ?? await getAppToken(options);
657
+ // Determine base URL
658
+ const env = globalThis?.process?.env ?? import.meta?.env ?? undefined;
659
+ const base = options?.baseUrl ?? env?.TALE_BASE_URL ?? undefined;
660
+ if (!base) {
661
+ throw new ConfigurationError('Missing required environment variable: TALE_BASE_URL');
662
+ }
663
+ // Build URL with query parameters
664
+ const url = new URL(String(base).replace(/\/+$/, '') + '/acl/v1/templates');
665
+ // Add query parameters with defaults
666
+ const queryParams = {
667
+ page: 0,
668
+ size: 20,
669
+ sort_by: 'created_at',
670
+ sort_direction: 'desc',
671
+ ...options
672
+ };
673
+ // Add parameters to URL
674
+ if (queryParams.resource_type) {
675
+ url.searchParams.append('resource_type', queryParams.resource_type);
676
+ }
677
+ if (queryParams.subject_type) {
678
+ url.searchParams.append('subject_type', queryParams.subject_type);
679
+ }
680
+ if (queryParams.page !== undefined) {
681
+ url.searchParams.append('page', String(queryParams.page));
682
+ }
683
+ if (queryParams.size !== undefined) {
684
+ url.searchParams.append('size', String(queryParams.size));
685
+ }
686
+ if (queryParams.sort_by) {
687
+ url.searchParams.append('sort_by', queryParams.sort_by);
688
+ }
689
+ if (queryParams.sort_direction) {
690
+ url.searchParams.append('sort_direction', queryParams.sort_direction);
691
+ }
692
+ let response;
693
+ try {
694
+ response = await globalThis.fetch(url.toString(), {
695
+ method: 'GET',
696
+ headers: {
697
+ 'Content-Type': 'application/json',
698
+ 'x-t-token': token,
699
+ },
700
+ });
701
+ }
702
+ catch (error) {
703
+ throw new NetworkError(`Failed to list ACL templates: ${error instanceof Error ? error.message : 'Unknown error'}`);
704
+ }
705
+ let json;
706
+ try {
707
+ const responseJson = await response.json();
708
+ json = responseJson;
709
+ }
710
+ catch (error) {
711
+ throw new ApiError(`Failed to parse ACL templates list response: ${error instanceof Error ? error.message : 'Invalid JSON'}`, response.status);
712
+ }
713
+ // Handle API errors
714
+ if (json.code !== 200) {
715
+ const errorMsg = typeof json.msg === 'string' ? json.msg : 'ACL templates list retrieval failed';
716
+ throw new ApiError(errorMsg, response.status, json.code);
717
+ }
718
+ // Validate response structure
719
+ if (!json.data || !Array.isArray(json.data.content)) {
720
+ throw new ApiError('Invalid ACL templates list response: missing required data', response.status);
721
+ }
722
+ return json.data;
723
+ }