zone4code-sdk 1.0.1 → 1.0.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.
- package/README.md +94 -16
- package/dist/index.cjs +327 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +127 -2
- package/dist/index.d.ts +127 -2
- package/dist/index.js +327 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Official TypeScript & JavaScript Client SDK for the **Zone4Code Platform** & **Singulary**.
|
|
4
4
|
|
|
5
|
-
Provides a type-safe, chainable interface (similar to Supabase / Prisma) for **Keycloak Authentication**, **Adaptive Object-Model (AOM) Dynamic Schemas**,
|
|
5
|
+
Provides a type-safe, chainable interface (similar to Supabase / Prisma) for **Keycloak Authentication**, **Adaptive Object-Model (AOM) Dynamic Schemas**, **JSONB Querying**, **Audit Revisions**, **Graph Relations**, and **Workflow Plugins** over the Unified API Gateway.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
@@ -22,11 +22,12 @@ pnpm add zone4code-sdk
|
|
|
22
22
|
import { createClient } from 'zone4code-sdk';
|
|
23
23
|
|
|
24
24
|
const client = createClient({
|
|
25
|
-
gatewayUrl: 'http://localhost:8080', // Or your
|
|
26
|
-
tenantId: 'my-workspace-id'
|
|
25
|
+
gatewayUrl: 'http://localhost:8080', // Or your production server: https://api.yourdomain.com
|
|
26
|
+
tenantId: 'my-workspace-id',
|
|
27
|
+
// storageKey: 'stayz.tokens' // Optional custom storage key name
|
|
27
28
|
});
|
|
28
29
|
|
|
29
|
-
//
|
|
30
|
+
// Check Gateway Health
|
|
30
31
|
const health = await client.health();
|
|
31
32
|
console.log('Connected to Gateway:', health.licenseTier);
|
|
32
33
|
```
|
|
@@ -35,25 +36,60 @@ console.log('Connected to Gateway:', health.licenseTier);
|
|
|
35
36
|
|
|
36
37
|
## 🔐 Authentication
|
|
37
38
|
|
|
39
|
+
### 1. Registration & Auto-Login
|
|
40
|
+
Registers a user in Keycloak (`POST /auth/:tenantId/user`) and automatically signs them in:
|
|
38
41
|
```typescript
|
|
39
|
-
|
|
40
|
-
await client.auth.register({
|
|
42
|
+
const { token, user } = await client.auth.register({
|
|
41
43
|
email: 'alice@example.com',
|
|
42
44
|
password: 'SecurePassword123!',
|
|
43
45
|
name: 'Alice Smith'
|
|
44
46
|
});
|
|
47
|
+
```
|
|
45
48
|
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
### 2. Login
|
|
50
|
+
Accepts `email`, `username`, or `login`:
|
|
51
|
+
```typescript
|
|
52
|
+
const { token, refreshToken, user } = await client.auth.login({
|
|
48
53
|
email: 'alice@example.com',
|
|
49
54
|
password: 'SecurePassword123!'
|
|
50
55
|
});
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
### 3. Synchronous User Inspection (`getUser`)
|
|
59
|
+
Decodes the active JWT token instantaneously without any network overhead:
|
|
60
|
+
```typescript
|
|
61
|
+
const user = client.auth.getUser();
|
|
62
|
+
console.log(user?.id, user?.email, user?.roles);
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### 4. Password Recovery & Reset
|
|
66
|
+
```typescript
|
|
67
|
+
// Send recovery email
|
|
68
|
+
await client.auth.forgotPassword('alice@example.com');
|
|
69
|
+
|
|
70
|
+
// Complete reset with action token
|
|
71
|
+
await client.auth.resetPassword({
|
|
72
|
+
token: actionTokenFromEmail,
|
|
73
|
+
newPassword: 'NewSecurePassword123!'
|
|
74
|
+
});
|
|
51
75
|
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
}
|
|
76
|
+
// Change password (while logged in)
|
|
77
|
+
await client.auth.changePassword({
|
|
78
|
+
oldPassword: 'OldPassword123!',
|
|
79
|
+
newPassword: 'NewSecurePassword123!'
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 5. Token Renewal & Google OAuth
|
|
84
|
+
```typescript
|
|
85
|
+
// Silent token renewal
|
|
86
|
+
await client.auth.refreshToken();
|
|
87
|
+
|
|
88
|
+
// Google OAuth login
|
|
89
|
+
await client.auth.loginWithGoogle(googleIdToken);
|
|
90
|
+
|
|
91
|
+
// Update user profile attributes
|
|
92
|
+
await client.auth.updateProfile({ firstName: 'Alice', attributes: { theme: 'dark' } });
|
|
57
93
|
|
|
58
94
|
// Logout
|
|
59
95
|
client.auth.logout();
|
|
@@ -90,14 +126,56 @@ console.log(`Found ${total} orders:`, data);
|
|
|
90
126
|
### 3. Update & Delete
|
|
91
127
|
```typescript
|
|
92
128
|
// Update
|
|
93
|
-
await client.from('order').update(orderId, {
|
|
94
|
-
status: 'paid'
|
|
95
|
-
});
|
|
129
|
+
await client.from('order').update(orderId, { status: 'paid' });
|
|
96
130
|
|
|
97
131
|
// Soft Delete
|
|
98
132
|
await client.from('order').delete(orderId);
|
|
99
133
|
```
|
|
100
134
|
|
|
135
|
+
### 4. Revisions & Audit History
|
|
136
|
+
```typescript
|
|
137
|
+
const revisions = await client.from('order').revisions(orderId);
|
|
138
|
+
console.log('Audit history:', revisions);
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### 5. Graph Relations (Linking Records)
|
|
142
|
+
```typescript
|
|
143
|
+
// Link order to a client record
|
|
144
|
+
await client.from('order').link(orderId, {
|
|
145
|
+
targetTypeName: 'client',
|
|
146
|
+
targetId: clientId,
|
|
147
|
+
relationName: 'belongs_to'
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// Fetch all relations
|
|
151
|
+
const relations = await client.from('order').getRelations(orderId);
|
|
152
|
+
|
|
153
|
+
// Unlink
|
|
154
|
+
await client.from('order').unlink(orderId, 'belongs_to', clientId, 'client');
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### 6. Workflow Plugin Actions
|
|
158
|
+
```typescript
|
|
159
|
+
// Type-level action (e.g. checkout quote)
|
|
160
|
+
await client.from('quote').action('checkout', { quoteId });
|
|
161
|
+
|
|
162
|
+
// Instance-level action (e.g. cancel order)
|
|
163
|
+
await client.from('order').instanceAction(orderId, 'cancel', { reason: 'Customer request' });
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### 7. Export CSV
|
|
167
|
+
```typescript
|
|
168
|
+
// Returns raw CSV string matching active filters
|
|
169
|
+
const csv = await client.from('order').eq('status', 'paid').exportCsv();
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### 8. User Profile & Wallet (`/me`)
|
|
173
|
+
```typescript
|
|
174
|
+
// Returns user profile + loyalty wallet points from generic-api
|
|
175
|
+
const me = await client.getMe();
|
|
176
|
+
console.log(me.name, me.wallet?.available_points);
|
|
177
|
+
```
|
|
178
|
+
|
|
101
179
|
---
|
|
102
180
|
|
|
103
181
|
## 🛠️ Dynamic Schema Alteration
|
package/dist/index.cjs
CHANGED
|
@@ -75,7 +75,9 @@ var AuthClient = class _AuthClient {
|
|
|
75
75
|
tenantId;
|
|
76
76
|
storage;
|
|
77
77
|
storageKey;
|
|
78
|
+
refreshStorageKey;
|
|
78
79
|
token = null;
|
|
80
|
+
refreshTokenValue = null;
|
|
79
81
|
fetchFn;
|
|
80
82
|
static TOKEN_KEY_PREFIX = "z4c_token_";
|
|
81
83
|
constructor(options) {
|
|
@@ -83,6 +85,7 @@ var AuthClient = class _AuthClient {
|
|
|
83
85
|
this.tenantId = options.tenantId;
|
|
84
86
|
this.storage = options.storage;
|
|
85
87
|
this.storageKey = options.storageKey || _AuthClient.TOKEN_KEY_PREFIX + this.tenantId;
|
|
88
|
+
this.refreshStorageKey = this.storageKey + "_refresh";
|
|
86
89
|
this.fetchFn = options.fetchFn || (typeof fetch !== "undefined" ? fetch : globalThis.fetch);
|
|
87
90
|
if (options.initialToken) {
|
|
88
91
|
this.setToken(options.initialToken);
|
|
@@ -91,11 +94,18 @@ var AuthClient = class _AuthClient {
|
|
|
91
94
|
if (stored) this.token = stored;
|
|
92
95
|
}).catch(() => {
|
|
93
96
|
});
|
|
97
|
+
Promise.resolve(this.storage.getItem(this.refreshStorageKey)).then((stored) => {
|
|
98
|
+
if (stored) this.refreshTokenValue = stored;
|
|
99
|
+
}).catch(() => {
|
|
100
|
+
});
|
|
94
101
|
}
|
|
95
102
|
}
|
|
96
103
|
getToken() {
|
|
97
104
|
return this.token;
|
|
98
105
|
}
|
|
106
|
+
getRefreshToken() {
|
|
107
|
+
return this.refreshTokenValue;
|
|
108
|
+
}
|
|
99
109
|
setToken(token) {
|
|
100
110
|
this.token = token;
|
|
101
111
|
if (token) {
|
|
@@ -104,6 +114,14 @@ var AuthClient = class _AuthClient {
|
|
|
104
114
|
this.storage.removeItem(this.storageKey);
|
|
105
115
|
}
|
|
106
116
|
}
|
|
117
|
+
setRefreshToken(token) {
|
|
118
|
+
this.refreshTokenValue = token;
|
|
119
|
+
if (token) {
|
|
120
|
+
this.storage.setItem(this.refreshStorageKey, token);
|
|
121
|
+
} else {
|
|
122
|
+
this.storage.removeItem(this.refreshStorageKey);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
107
125
|
isAuthenticated() {
|
|
108
126
|
return !!this.token;
|
|
109
127
|
}
|
|
@@ -212,6 +230,9 @@ var AuthClient = class _AuthClient {
|
|
|
212
230
|
if (token) {
|
|
213
231
|
this.setToken(token);
|
|
214
232
|
}
|
|
233
|
+
if (json.refresh_token) {
|
|
234
|
+
this.setRefreshToken(json.refresh_token);
|
|
235
|
+
}
|
|
215
236
|
const user = this.getUser() || json.user;
|
|
216
237
|
return {
|
|
217
238
|
token: token || "",
|
|
@@ -220,6 +241,163 @@ var AuthClient = class _AuthClient {
|
|
|
220
241
|
...json
|
|
221
242
|
};
|
|
222
243
|
}
|
|
244
|
+
/**
|
|
245
|
+
* Authenticate via Google OAuth ID token
|
|
246
|
+
*/
|
|
247
|
+
async loginWithGoogle(token) {
|
|
248
|
+
const url = `${this.gatewayUrl}/auth/${this.tenantId}/login/google`;
|
|
249
|
+
const res = await this.fetchFn(url, {
|
|
250
|
+
method: "POST",
|
|
251
|
+
headers: {
|
|
252
|
+
"Content-Type": "application/json",
|
|
253
|
+
"x-tenant-id": this.tenantId
|
|
254
|
+
},
|
|
255
|
+
body: JSON.stringify({ token })
|
|
256
|
+
});
|
|
257
|
+
if (!res.ok) {
|
|
258
|
+
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
259
|
+
throw new Error(err.message || err.error || `Google login failed with HTTP ${res.status}`);
|
|
260
|
+
}
|
|
261
|
+
const json = await res.json();
|
|
262
|
+
const jwt = json.token || json.access_token;
|
|
263
|
+
if (jwt) {
|
|
264
|
+
this.setToken(jwt);
|
|
265
|
+
}
|
|
266
|
+
if (json.refresh_token) {
|
|
267
|
+
this.setRefreshToken(json.refresh_token);
|
|
268
|
+
}
|
|
269
|
+
const user = this.getUser() || json.user;
|
|
270
|
+
return {
|
|
271
|
+
token: jwt || "",
|
|
272
|
+
refreshToken: json.refresh_token,
|
|
273
|
+
user,
|
|
274
|
+
...json
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Renew expired access token using stored or provided refresh token
|
|
279
|
+
*/
|
|
280
|
+
async refreshToken(refreshToken) {
|
|
281
|
+
const tokenToUse = refreshToken || this.getRefreshToken();
|
|
282
|
+
if (!tokenToUse) {
|
|
283
|
+
throw new Error("No refresh token available");
|
|
284
|
+
}
|
|
285
|
+
const url = `${this.gatewayUrl}/auth/${this.tenantId}/refresh`;
|
|
286
|
+
const res = await this.fetchFn(url, {
|
|
287
|
+
method: "POST",
|
|
288
|
+
headers: {
|
|
289
|
+
"Content-Type": "application/json",
|
|
290
|
+
"x-tenant-id": this.tenantId
|
|
291
|
+
},
|
|
292
|
+
body: JSON.stringify({ refresh_token: tokenToUse })
|
|
293
|
+
});
|
|
294
|
+
if (!res.ok) {
|
|
295
|
+
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
296
|
+
throw new Error(err.message || err.error || `Token refresh failed with HTTP ${res.status}`);
|
|
297
|
+
}
|
|
298
|
+
const json = await res.json();
|
|
299
|
+
const token = json.token || json.access_token;
|
|
300
|
+
if (token) {
|
|
301
|
+
this.setToken(token);
|
|
302
|
+
}
|
|
303
|
+
if (json.refresh_token) {
|
|
304
|
+
this.setRefreshToken(json.refresh_token);
|
|
305
|
+
}
|
|
306
|
+
const user = this.getUser() || json.user;
|
|
307
|
+
return {
|
|
308
|
+
token: token || "",
|
|
309
|
+
refreshToken: json.refresh_token,
|
|
310
|
+
user,
|
|
311
|
+
...json
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Request password reset email
|
|
316
|
+
*/
|
|
317
|
+
async forgotPassword(data) {
|
|
318
|
+
const payload = typeof data === "string" ? { email: data } : data;
|
|
319
|
+
const url = `${this.gatewayUrl}/auth/${this.tenantId}/auth/forgot-password`;
|
|
320
|
+
const res = await this.fetchFn(url, {
|
|
321
|
+
method: "POST",
|
|
322
|
+
headers: {
|
|
323
|
+
"Content-Type": "application/json",
|
|
324
|
+
"x-tenant-id": this.tenantId
|
|
325
|
+
},
|
|
326
|
+
body: JSON.stringify(payload)
|
|
327
|
+
});
|
|
328
|
+
const json = await res.json().catch(() => ({}));
|
|
329
|
+
if (!res.ok) {
|
|
330
|
+
throw new Error(json.message || json.error || `Forgot password request failed with HTTP ${res.status}`);
|
|
331
|
+
}
|
|
332
|
+
return json;
|
|
333
|
+
}
|
|
334
|
+
/**
|
|
335
|
+
* Complete password reset using action token
|
|
336
|
+
*/
|
|
337
|
+
async resetPassword(data) {
|
|
338
|
+
const url = `${this.gatewayUrl}/auth/${this.tenantId}/auth/reset-password`;
|
|
339
|
+
const res = await this.fetchFn(url, {
|
|
340
|
+
method: "POST",
|
|
341
|
+
headers: {
|
|
342
|
+
"Content-Type": "application/json",
|
|
343
|
+
"x-tenant-id": this.tenantId
|
|
344
|
+
},
|
|
345
|
+
body: JSON.stringify(data)
|
|
346
|
+
});
|
|
347
|
+
const json = await res.json().catch(() => ({}));
|
|
348
|
+
if (!res.ok) {
|
|
349
|
+
throw new Error(json.message || json.error || `Reset password failed with HTTP ${res.status}`);
|
|
350
|
+
}
|
|
351
|
+
return json;
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Change password for current or specified user
|
|
355
|
+
*/
|
|
356
|
+
async changePassword(data, userId) {
|
|
357
|
+
const targetUserId = userId || this.getUser()?.id;
|
|
358
|
+
if (!targetUserId) {
|
|
359
|
+
throw new Error("User ID required: user is not logged in and no userId was provided");
|
|
360
|
+
}
|
|
361
|
+
const url = `${this.gatewayUrl}/auth/${this.tenantId}/user/${targetUserId}/change-password`;
|
|
362
|
+
const res = await this.fetchFn(url, {
|
|
363
|
+
method: "POST",
|
|
364
|
+
headers: {
|
|
365
|
+
"Content-Type": "application/json",
|
|
366
|
+
"x-tenant-id": this.tenantId,
|
|
367
|
+
...this.token ? { "Authorization": `Bearer ${this.token}` } : {}
|
|
368
|
+
},
|
|
369
|
+
body: JSON.stringify(data)
|
|
370
|
+
});
|
|
371
|
+
if (!res.ok) {
|
|
372
|
+
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
373
|
+
throw new Error(err.message || err.error || `Change password failed with HTTP ${res.status}`);
|
|
374
|
+
}
|
|
375
|
+
return res.json().catch(() => ({ success: true }));
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Update profile attributes for current or specified user
|
|
379
|
+
*/
|
|
380
|
+
async updateProfile(data, userId) {
|
|
381
|
+
const targetUserId = userId || this.getUser()?.id;
|
|
382
|
+
if (!targetUserId) {
|
|
383
|
+
throw new Error("User ID required: user is not logged in and no userId was provided");
|
|
384
|
+
}
|
|
385
|
+
const url = `${this.gatewayUrl}/auth/${this.tenantId}/user/${targetUserId}`;
|
|
386
|
+
const res = await this.fetchFn(url, {
|
|
387
|
+
method: "PUT",
|
|
388
|
+
headers: {
|
|
389
|
+
"Content-Type": "application/json",
|
|
390
|
+
"x-tenant-id": this.tenantId,
|
|
391
|
+
...this.token ? { "Authorization": `Bearer ${this.token}` } : {}
|
|
392
|
+
},
|
|
393
|
+
body: JSON.stringify(data)
|
|
394
|
+
});
|
|
395
|
+
if (!res.ok) {
|
|
396
|
+
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
397
|
+
throw new Error(err.message || err.error || `Update profile failed with HTTP ${res.status}`);
|
|
398
|
+
}
|
|
399
|
+
return res.json();
|
|
400
|
+
}
|
|
223
401
|
/**
|
|
224
402
|
* Fetch current authenticated user profile
|
|
225
403
|
*/
|
|
@@ -245,10 +423,11 @@ var AuthClient = class _AuthClient {
|
|
|
245
423
|
return res.json();
|
|
246
424
|
}
|
|
247
425
|
/**
|
|
248
|
-
* Log out and clear saved
|
|
426
|
+
* Log out and clear saved tokens
|
|
249
427
|
*/
|
|
250
428
|
logout() {
|
|
251
429
|
this.setToken(null);
|
|
430
|
+
this.setRefreshToken(null);
|
|
252
431
|
}
|
|
253
432
|
};
|
|
254
433
|
|
|
@@ -484,6 +663,132 @@ var EntityQueryBuilder = class {
|
|
|
484
663
|
}
|
|
485
664
|
return { success: true };
|
|
486
665
|
}
|
|
666
|
+
/**
|
|
667
|
+
* Fetch revision history for an entity record
|
|
668
|
+
*/
|
|
669
|
+
async revisions(id) {
|
|
670
|
+
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/revisions`;
|
|
671
|
+
const res = await this.fetchFn(url, {
|
|
672
|
+
headers: this.getHeaders()
|
|
673
|
+
});
|
|
674
|
+
if (!res.ok) {
|
|
675
|
+
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
676
|
+
throw new Error(err.message || `Failed to fetch revisions for ${this.typeName}/${id}`);
|
|
677
|
+
}
|
|
678
|
+
return res.json();
|
|
679
|
+
}
|
|
680
|
+
/**
|
|
681
|
+
* Fetch all graph relations for this entity record
|
|
682
|
+
*/
|
|
683
|
+
async getRelations(id, lang) {
|
|
684
|
+
const qs = lang ? `?lang=${encodeURIComponent(lang)}` : "";
|
|
685
|
+
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/relations${qs}`;
|
|
686
|
+
const res = await this.fetchFn(url, {
|
|
687
|
+
headers: this.getHeaders()
|
|
688
|
+
});
|
|
689
|
+
if (!res.ok) {
|
|
690
|
+
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
691
|
+
throw new Error(err.message || `Failed to fetch relations for ${this.typeName}/${id}`);
|
|
692
|
+
}
|
|
693
|
+
return res.json();
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* Link this record to another entity record
|
|
697
|
+
*/
|
|
698
|
+
async link(id, payload) {
|
|
699
|
+
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/relations`;
|
|
700
|
+
const res = await this.fetchFn(url, {
|
|
701
|
+
method: "POST",
|
|
702
|
+
headers: this.getHeaders(),
|
|
703
|
+
body: JSON.stringify(payload)
|
|
704
|
+
});
|
|
705
|
+
if (!res.ok) {
|
|
706
|
+
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
707
|
+
throw new Error(err.message || `Failed to create relation for ${this.typeName}/${id}`);
|
|
708
|
+
}
|
|
709
|
+
return res.json().catch(() => ({ success: true }));
|
|
710
|
+
}
|
|
711
|
+
/**
|
|
712
|
+
* Replace the full set of relations of one relation name
|
|
713
|
+
*/
|
|
714
|
+
async setLinks(id, payload) {
|
|
715
|
+
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/relations`;
|
|
716
|
+
const res = await this.fetchFn(url, {
|
|
717
|
+
method: "PUT",
|
|
718
|
+
headers: this.getHeaders(),
|
|
719
|
+
body: JSON.stringify(payload)
|
|
720
|
+
});
|
|
721
|
+
if (!res.ok) {
|
|
722
|
+
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
723
|
+
throw new Error(err.message || `Failed to replace relations for ${this.typeName}/${id}`);
|
|
724
|
+
}
|
|
725
|
+
return res.json().catch(() => ({ success: true }));
|
|
726
|
+
}
|
|
727
|
+
/**
|
|
728
|
+
* Remove one graph link between two records
|
|
729
|
+
*/
|
|
730
|
+
async unlink(id, relationName, targetId, targetTypeName) {
|
|
731
|
+
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/relations/${encodeURIComponent(relationName)}/${encodeURIComponent(targetId)}?targetTypeName=${encodeURIComponent(targetTypeName)}`;
|
|
732
|
+
const res = await this.fetchFn(url, {
|
|
733
|
+
method: "DELETE",
|
|
734
|
+
headers: this.getHeaders()
|
|
735
|
+
});
|
|
736
|
+
if (!res.ok) {
|
|
737
|
+
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
738
|
+
throw new Error(err.message || `Failed to delete relation ${relationName} for ${this.typeName}/${id}`);
|
|
739
|
+
}
|
|
740
|
+
return res.json().catch(() => ({ success: true }));
|
|
741
|
+
}
|
|
742
|
+
/**
|
|
743
|
+
* Execute a type-level workflow plugin action (e.g. checkout, batch-process)
|
|
744
|
+
*/
|
|
745
|
+
async action(actionName, payload) {
|
|
746
|
+
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/actions/${actionName}`;
|
|
747
|
+
const res = await this.fetchFn(url, {
|
|
748
|
+
method: "POST",
|
|
749
|
+
headers: this.getHeaders(),
|
|
750
|
+
body: payload ? JSON.stringify(payload) : void 0
|
|
751
|
+
});
|
|
752
|
+
if (!res.ok) {
|
|
753
|
+
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
754
|
+
throw new Error(err.message || `Action ${actionName} failed on type ${this.typeName}`);
|
|
755
|
+
}
|
|
756
|
+
return res.json();
|
|
757
|
+
}
|
|
758
|
+
/**
|
|
759
|
+
* Execute an instance-level workflow plugin action (e.g. cancel order, approve)
|
|
760
|
+
*/
|
|
761
|
+
async instanceAction(id, actionName, payload) {
|
|
762
|
+
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/${id}/actions/${actionName}`;
|
|
763
|
+
const res = await this.fetchFn(url, {
|
|
764
|
+
method: "POST",
|
|
765
|
+
headers: this.getHeaders(),
|
|
766
|
+
body: payload ? JSON.stringify(payload) : void 0
|
|
767
|
+
});
|
|
768
|
+
if (!res.ok) {
|
|
769
|
+
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
770
|
+
throw new Error(err.message || `Action ${actionName} failed on ${this.typeName}/${id}`);
|
|
771
|
+
}
|
|
772
|
+
return res.json();
|
|
773
|
+
}
|
|
774
|
+
/**
|
|
775
|
+
* Export matching entity records to CSV format with active filters
|
|
776
|
+
*/
|
|
777
|
+
async exportCsv() {
|
|
778
|
+
const qs = this.queryParams.toString();
|
|
779
|
+
const url = `${this.gatewayUrl}/generic/${this.tenantId}/${this.typeName}/export/csv${qs ? `?${qs}` : ""}`;
|
|
780
|
+
const res = await this.fetchFn(url, {
|
|
781
|
+
headers: {
|
|
782
|
+
...this.getHeaders(),
|
|
783
|
+
"Accept": "text/csv"
|
|
784
|
+
}
|
|
785
|
+
});
|
|
786
|
+
if (!res.ok) {
|
|
787
|
+
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
788
|
+
throw new Error(err.message || `Failed to export CSV for ${this.typeName}`);
|
|
789
|
+
}
|
|
790
|
+
return res.text();
|
|
791
|
+
}
|
|
487
792
|
};
|
|
488
793
|
|
|
489
794
|
// src/client.ts
|
|
@@ -538,6 +843,27 @@ var Zone4CodeClient = class {
|
|
|
538
843
|
entities(typeName) {
|
|
539
844
|
return this.from(typeName);
|
|
540
845
|
}
|
|
846
|
+
/**
|
|
847
|
+
* Get current authenticated user profile and platform wallet
|
|
848
|
+
*/
|
|
849
|
+
async getMe() {
|
|
850
|
+
const token = this.auth.getToken();
|
|
851
|
+
if (!token) {
|
|
852
|
+
throw new Error("Not authenticated: please call login() or setToken() first");
|
|
853
|
+
}
|
|
854
|
+
const res = await this.fetchFn(`${this.gatewayUrl}/generic/${this.tenantId}/me`, {
|
|
855
|
+
headers: {
|
|
856
|
+
"Content-Type": "application/json",
|
|
857
|
+
"Authorization": `Bearer ${token}`,
|
|
858
|
+
"x-tenant-id": this.tenantId
|
|
859
|
+
}
|
|
860
|
+
});
|
|
861
|
+
if (!res.ok) {
|
|
862
|
+
const err = await res.json().catch(() => ({ message: res.statusText }));
|
|
863
|
+
throw new Error(err.message || `Failed to fetch profile & wallet (HTTP ${res.status})`);
|
|
864
|
+
}
|
|
865
|
+
return res.json();
|
|
866
|
+
}
|
|
541
867
|
/**
|
|
542
868
|
* Check gateway connectivity & health
|
|
543
869
|
*/
|