@gravity-rail/sdk 0.46.0 → 0.49.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/README.md +116 -8
- package/dist/agui.cjs +17 -2
- package/dist/agui.cjs.map +1 -1
- package/dist/agui.d.cts +9 -1
- package/dist/agui.d.ts +9 -1
- package/dist/agui.js +15 -1
- package/dist/agui.js.map +1 -1
- package/dist/{chat-interaction-0MSzmdxz.d.cts → chat-interaction-D4Edo92W.d.ts} +273 -48
- package/dist/{chat-interaction-CO9ecTSn.d.ts → chat-interaction-e5vHKt5j.d.cts} +273 -48
- package/dist/{chunk-72SWNLVD.js → chunk-MMDSZ25F.js} +1 -1
- package/dist/chunk-MMDSZ25F.js.map +1 -0
- package/dist/{chunk-67QXTQVL.js → chunk-TGL72KPS.js} +59 -118
- package/dist/chunk-TGL72KPS.js.map +1 -0
- package/dist/{chunk-I7BMDNPO.js → chunk-XQTPXOBU.js} +249 -50
- package/dist/chunk-XQTPXOBU.js.map +1 -0
- package/dist/helpers.cjs.map +1 -1
- package/dist/helpers.d.cts +4 -4
- package/dist/helpers.d.ts +4 -4
- package/dist/helpers.js +1 -1
- package/dist/index.cjs +307 -164
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -6
- package/dist/index.d.ts +7 -6
- package/dist/index.js +9 -3
- package/dist/index.js.map +1 -1
- package/dist/internal.cjs +307 -164
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +148 -6
- package/dist/internal.d.ts +148 -6
- package/dist/internal.js +9 -3
- package/dist/internal.js.map +1 -1
- package/dist/{member-import-D7U4kDIy.d.cts → member-import-9zHkUjcn.d.cts} +1 -1
- package/dist/{member-import-C2Xt_o5d.d.ts → member-import-ocJ_K474.d.ts} +1 -1
- package/dist/{revision-state-Cc3-CIi5.d.ts → path-CQBykGoj.d.cts} +64 -3
- package/dist/{revision-state-m6Cjyr4M.d.cts → path-DVWLvQS-.d.ts} +64 -3
- package/dist/schemas.cjs +60 -15
- package/dist/schemas.cjs.map +1 -1
- package/dist/schemas.d.cts +34 -3
- package/dist/schemas.d.ts +34 -3
- package/dist/schemas.js +60 -16
- package/dist/schemas.js.map +1 -1
- package/dist/{tasks-Ca6yfsj2.d.cts → tasks-DsNsbco5.d.cts} +66 -14
- package/dist/{tasks-Ca6yfsj2.d.ts → tasks-DsNsbco5.d.ts} +66 -14
- package/package.json +2 -2
- package/dist/chunk-67QXTQVL.js.map +0 -1
- package/dist/chunk-72SWNLVD.js.map +0 -1
- package/dist/chunk-I7BMDNPO.js.map +0 -1
package/dist/internal.cjs
CHANGED
|
@@ -342,6 +342,7 @@ __export(internal_exports, {
|
|
|
342
342
|
getFeaturesByCategory: () => getFeaturesByCategory,
|
|
343
343
|
getFeaturesByVisibility: () => getFeaturesByVisibility,
|
|
344
344
|
getMissingDependencies: () => getMissingDependencies,
|
|
345
|
+
getPathDefinition: () => getPathDefinition,
|
|
345
346
|
getResourceMetadata: () => getResourceMetadata,
|
|
346
347
|
getRevisionState: () => getRevisionState,
|
|
347
348
|
getTTSProvider: () => getTTSProvider,
|
|
@@ -359,6 +360,8 @@ __export(internal_exports, {
|
|
|
359
360
|
isSMSDeliveryFailureStatus: () => isSMSDeliveryFailureStatus,
|
|
360
361
|
parseExperimentStatCount: () => parseExperimentStatCount,
|
|
361
362
|
parseOrgAuthChallenge: () => parseOrgAuthChallenge,
|
|
363
|
+
pathDraftFromStorage: () => pathDraftFromStorage,
|
|
364
|
+
pathDraftToStorage: () => pathDraftToStorage,
|
|
362
365
|
requiresAssistantParticipant: () => requiresAssistantParticipant,
|
|
363
366
|
usesBuiltinAssistant: () => usesBuiltinAssistant,
|
|
364
367
|
validateFeatureDependencies: () => validateFeatureDependencies
|
|
@@ -3090,27 +3093,16 @@ function parseOrgAuthChallenge(wwwAuthenticate) {
|
|
|
3090
3093
|
if (!wwwAuthenticate) return null;
|
|
3091
3094
|
if (!wwwAuthenticate.includes('realm="org"')) return null;
|
|
3092
3095
|
const orgUuidMatch = wwwAuthenticate.match(/org_uuid="([^"]+)"/);
|
|
3093
|
-
const cookieNameMatch = wwwAuthenticate.match(/cookie_name="([^"]+)"/);
|
|
3094
3096
|
const authUrlMatch = wwwAuthenticate.match(/auth_url="([^"]+)"/);
|
|
3095
|
-
if (!orgUuidMatch?.[1] || !
|
|
3097
|
+
if (!orgUuidMatch?.[1] || !authUrlMatch?.[1]) {
|
|
3096
3098
|
console.warn("Invalid org auth challenge header: missing required fields");
|
|
3097
3099
|
return null;
|
|
3098
3100
|
}
|
|
3099
3101
|
return {
|
|
3100
3102
|
orgUuid: orgUuidMatch[1],
|
|
3101
|
-
cookieName: cookieNameMatch[1],
|
|
3102
3103
|
authUrl: authUrlMatch[1]
|
|
3103
3104
|
};
|
|
3104
3105
|
}
|
|
3105
|
-
function getRequestOrgCookieName(cookieName) {
|
|
3106
|
-
if (cookieName.startsWith("orgToken")) {
|
|
3107
|
-
return cookieName.replace(/^orgToken/, "requestOrgToken");
|
|
3108
|
-
}
|
|
3109
|
-
if (cookieName.startsWith("orgSession")) {
|
|
3110
|
-
return cookieName.replace(/^orgSession/, "requestOrgSession");
|
|
3111
|
-
}
|
|
3112
|
-
return null;
|
|
3113
|
-
}
|
|
3114
3106
|
function orgAuthHeaderName(token) {
|
|
3115
3107
|
return token.startsWith("gr_sess_") ? "X-Org-Session" : "X-Org-Token";
|
|
3116
3108
|
}
|
|
@@ -3167,7 +3159,7 @@ var BaseApiClient = class {
|
|
|
3167
3159
|
this.apiKey = newApiKey;
|
|
3168
3160
|
}
|
|
3169
3161
|
/**
|
|
3170
|
-
* Set the opaque org session credential
|
|
3162
|
+
* Set the opaque org session credential a bearer client sends as `X-Org-Session`.
|
|
3171
3163
|
* Opaque gr_sess_ values use X-Org-Session. Legacy orgToken JWE values still
|
|
3172
3164
|
* use X-Org-Token through setOrgToken() during the migration window.
|
|
3173
3165
|
*
|
|
@@ -3187,7 +3179,42 @@ var BaseApiClient = class {
|
|
|
3187
3179
|
setOrgToken(token, orgUuid) {
|
|
3188
3180
|
this.setOrgSession(token, orgUuid);
|
|
3189
3181
|
}
|
|
3190
|
-
|
|
3182
|
+
/**
|
|
3183
|
+
* The `credentials` a request carries: none for a bearer client (the fetch
|
|
3184
|
+
* default), the page's own-origin cookies for a same-origin session.
|
|
3185
|
+
*/
|
|
3186
|
+
credentialsInit() {
|
|
3187
|
+
return this.apiKey ? {} : { credentials: "same-origin" };
|
|
3188
|
+
}
|
|
3189
|
+
/**
|
|
3190
|
+
* Run the org-auth handler once for concurrent challenges. A bearer client's
|
|
3191
|
+
* handler sets the org session with `setOrgSession`; a same-origin session's
|
|
3192
|
+
* handler leaves an httpOnly org-session cookie on the page's origin instead,
|
|
3193
|
+
* which the retry presents. A bearer client whose handler issued nothing has
|
|
3194
|
+
* nothing to retry with.
|
|
3195
|
+
*/
|
|
3196
|
+
async answerOrgAuthChallenge(orgAuthInfo) {
|
|
3197
|
+
const handler = this.orgAuthHandler;
|
|
3198
|
+
if (!handler) return;
|
|
3199
|
+
if (!this.orgAuthInProgress) {
|
|
3200
|
+
this.orgAuthInProgress = (async () => {
|
|
3201
|
+
try {
|
|
3202
|
+
await handler(orgAuthInfo);
|
|
3203
|
+
} finally {
|
|
3204
|
+
this.orgAuthInProgress = null;
|
|
3205
|
+
}
|
|
3206
|
+
})();
|
|
3207
|
+
}
|
|
3208
|
+
await this.orgAuthInProgress;
|
|
3209
|
+
if (this.orgSession) {
|
|
3210
|
+
this.orgSessionOrgUuid = orgAuthInfo.orgUuid;
|
|
3211
|
+
return;
|
|
3212
|
+
}
|
|
3213
|
+
if (this.apiKey) {
|
|
3214
|
+
throw new ApiError("Organization authentication did not issue a session", 401);
|
|
3215
|
+
}
|
|
3216
|
+
}
|
|
3217
|
+
async fetch(endpoint, options, orgAuthRetry = false) {
|
|
3191
3218
|
try {
|
|
3192
3219
|
const headers = new Headers({
|
|
3193
3220
|
// Don't set Content-Type for FormData — the browser must set it
|
|
@@ -3197,17 +3224,12 @@ var BaseApiClient = class {
|
|
|
3197
3224
|
});
|
|
3198
3225
|
if (this.apiKey) {
|
|
3199
3226
|
headers.set("Authorization", `Bearer ${this.apiKey}`);
|
|
3200
|
-
} else if (this.isCrossDomain) {
|
|
3201
|
-
const requestToken = getCookieValue("requestToken");
|
|
3202
|
-
if (requestToken) {
|
|
3203
|
-
headers.set("Authorization", `Bearer ${requestToken}`);
|
|
3204
|
-
}
|
|
3205
3227
|
}
|
|
3206
3228
|
setOrgAuthHeader(headers, this.orgSession);
|
|
3207
3229
|
const fullUrl = `${this.baseUrl}/api/v2/${endpoint}`;
|
|
3208
3230
|
const response = await this.fetchFunction(fullUrl, {
|
|
3209
3231
|
...options,
|
|
3210
|
-
...this.
|
|
3232
|
+
...this.credentialsInit(),
|
|
3211
3233
|
...this.isCrossDomain ? { mode: "cors" } : {},
|
|
3212
3234
|
headers
|
|
3213
3235
|
});
|
|
@@ -3238,48 +3260,14 @@ var BaseApiClient = class {
|
|
|
3238
3260
|
this.orgSession = void 0;
|
|
3239
3261
|
this.orgSessionOrgUuid = void 0;
|
|
3240
3262
|
}
|
|
3241
|
-
if (orgAuthInfo && !this.orgSession) {
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
this.
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
} catch (retryError) {
|
|
3250
|
-
this.orgSession = void 0;
|
|
3251
|
-
this.orgSessionOrgUuid = void 0;
|
|
3252
|
-
throw retryError;
|
|
3253
|
-
}
|
|
3254
|
-
}
|
|
3255
|
-
if (this.orgAuthHandler) {
|
|
3256
|
-
if (!this.orgAuthInProgress) {
|
|
3257
|
-
const handler = this.orgAuthHandler;
|
|
3258
|
-
this.orgAuthInProgress = (async () => {
|
|
3259
|
-
try {
|
|
3260
|
-
await handler(orgAuthInfo);
|
|
3261
|
-
} finally {
|
|
3262
|
-
this.orgAuthInProgress = null;
|
|
3263
|
-
}
|
|
3264
|
-
})();
|
|
3265
|
-
}
|
|
3266
|
-
await this.orgAuthInProgress;
|
|
3267
|
-
const postHandlerSession = (requestCookieName ? getCookieValue(requestCookieName) : void 0) ?? this.orgSession;
|
|
3268
|
-
if (!postHandlerSession) {
|
|
3269
|
-
throw new ApiError(
|
|
3270
|
-
"Organization authentication did not issue a session",
|
|
3271
|
-
401
|
|
3272
|
-
);
|
|
3273
|
-
}
|
|
3274
|
-
this.orgSession = postHandlerSession;
|
|
3275
|
-
this.orgSessionOrgUuid = orgAuthInfo.orgUuid;
|
|
3276
|
-
try {
|
|
3277
|
-
return await this.fetch(endpoint, options);
|
|
3278
|
-
} catch (retryError) {
|
|
3279
|
-
this.orgSession = void 0;
|
|
3280
|
-
this.orgSessionOrgUuid = void 0;
|
|
3281
|
-
throw retryError;
|
|
3282
|
-
}
|
|
3263
|
+
if (orgAuthInfo && !this.orgSession && !orgAuthRetry && this.orgAuthHandler) {
|
|
3264
|
+
await this.answerOrgAuthChallenge(orgAuthInfo);
|
|
3265
|
+
try {
|
|
3266
|
+
return await this.fetch(endpoint, options, true);
|
|
3267
|
+
} catch (retryError) {
|
|
3268
|
+
this.orgSession = void 0;
|
|
3269
|
+
this.orgSessionOrgUuid = void 0;
|
|
3270
|
+
throw retryError;
|
|
3283
3271
|
}
|
|
3284
3272
|
}
|
|
3285
3273
|
}
|
|
@@ -3341,23 +3329,18 @@ ${formattedErrors}`, response.status);
|
|
|
3341
3329
|
throw error;
|
|
3342
3330
|
}
|
|
3343
3331
|
}
|
|
3344
|
-
async fetchRaw(endpoint, options, allowedErrorStatuses = []) {
|
|
3332
|
+
async fetchRaw(endpoint, options, allowedErrorStatuses = [], orgAuthRetry = false) {
|
|
3345
3333
|
try {
|
|
3346
3334
|
const headers = new Headers({
|
|
3347
3335
|
...options?.headers || {}
|
|
3348
3336
|
});
|
|
3349
3337
|
if (this.apiKey) {
|
|
3350
3338
|
headers.set("Authorization", `Bearer ${this.apiKey}`);
|
|
3351
|
-
} else if (this.isCrossDomain) {
|
|
3352
|
-
const requestToken = getCookieValue("requestToken");
|
|
3353
|
-
if (requestToken) {
|
|
3354
|
-
headers.set("Authorization", `Bearer ${requestToken}`);
|
|
3355
|
-
}
|
|
3356
3339
|
}
|
|
3357
3340
|
setOrgAuthHeader(headers, this.orgSession);
|
|
3358
3341
|
const response = await this.fetchFunction(`${this.baseUrl}/api/v2/${endpoint}`, {
|
|
3359
3342
|
...options,
|
|
3360
|
-
...this.
|
|
3343
|
+
...this.credentialsInit(),
|
|
3361
3344
|
...this.isCrossDomain ? { mode: "cors" } : {},
|
|
3362
3345
|
headers
|
|
3363
3346
|
});
|
|
@@ -3388,48 +3371,14 @@ ${formattedErrors}`, response.status);
|
|
|
3388
3371
|
this.orgSession = void 0;
|
|
3389
3372
|
this.orgSessionOrgUuid = void 0;
|
|
3390
3373
|
}
|
|
3391
|
-
if (orgAuthInfo && !this.orgSession) {
|
|
3392
|
-
|
|
3393
|
-
|
|
3394
|
-
|
|
3395
|
-
|
|
3396
|
-
this.
|
|
3397
|
-
|
|
3398
|
-
|
|
3399
|
-
} catch (retryError) {
|
|
3400
|
-
this.orgSession = void 0;
|
|
3401
|
-
this.orgSessionOrgUuid = void 0;
|
|
3402
|
-
throw retryError;
|
|
3403
|
-
}
|
|
3404
|
-
}
|
|
3405
|
-
if (this.orgAuthHandler) {
|
|
3406
|
-
if (!this.orgAuthInProgress) {
|
|
3407
|
-
const handler = this.orgAuthHandler;
|
|
3408
|
-
this.orgAuthInProgress = (async () => {
|
|
3409
|
-
try {
|
|
3410
|
-
await handler(orgAuthInfo);
|
|
3411
|
-
} finally {
|
|
3412
|
-
this.orgAuthInProgress = null;
|
|
3413
|
-
}
|
|
3414
|
-
})();
|
|
3415
|
-
}
|
|
3416
|
-
await this.orgAuthInProgress;
|
|
3417
|
-
const postHandlerSession = (requestCookieName ? getCookieValue(requestCookieName) : void 0) ?? this.orgSession;
|
|
3418
|
-
if (!postHandlerSession) {
|
|
3419
|
-
throw new ApiError(
|
|
3420
|
-
"Organization authentication did not issue a session",
|
|
3421
|
-
401
|
|
3422
|
-
);
|
|
3423
|
-
}
|
|
3424
|
-
this.orgSession = postHandlerSession;
|
|
3425
|
-
this.orgSessionOrgUuid = orgAuthInfo.orgUuid;
|
|
3426
|
-
try {
|
|
3427
|
-
return await this.fetchRaw(endpoint, options, allowedErrorStatuses);
|
|
3428
|
-
} catch (retryError) {
|
|
3429
|
-
this.orgSession = void 0;
|
|
3430
|
-
this.orgSessionOrgUuid = void 0;
|
|
3431
|
-
throw retryError;
|
|
3432
|
-
}
|
|
3374
|
+
if (orgAuthInfo && !this.orgSession && !orgAuthRetry && this.orgAuthHandler) {
|
|
3375
|
+
await this.answerOrgAuthChallenge(orgAuthInfo);
|
|
3376
|
+
try {
|
|
3377
|
+
return await this.fetchRaw(endpoint, options, allowedErrorStatuses, true);
|
|
3378
|
+
} catch (retryError) {
|
|
3379
|
+
this.orgSession = void 0;
|
|
3380
|
+
this.orgSessionOrgUuid = void 0;
|
|
3381
|
+
throw retryError;
|
|
3433
3382
|
}
|
|
3434
3383
|
}
|
|
3435
3384
|
}
|
|
@@ -3457,17 +3406,12 @@ ${formattedErrors}`, response.status);
|
|
|
3457
3406
|
});
|
|
3458
3407
|
if (this.apiKey) {
|
|
3459
3408
|
headers.set("Authorization", `Bearer ${this.apiKey}`);
|
|
3460
|
-
} else if (this.isCrossDomain) {
|
|
3461
|
-
const requestToken = getCookieValue("requestToken");
|
|
3462
|
-
if (requestToken) {
|
|
3463
|
-
headers.set("Authorization", `Bearer ${requestToken}`);
|
|
3464
|
-
}
|
|
3465
3409
|
}
|
|
3466
3410
|
setOrgAuthHeader(headers, this.orgSession);
|
|
3467
3411
|
const fullUrl = `${this.baseUrl}/${path}`;
|
|
3468
3412
|
const response = await this.fetchFunction(fullUrl, {
|
|
3469
3413
|
...options,
|
|
3470
|
-
...this.
|
|
3414
|
+
...this.credentialsInit(),
|
|
3471
3415
|
...this.isCrossDomain ? { mode: "cors" } : {},
|
|
3472
3416
|
headers
|
|
3473
3417
|
});
|
|
@@ -4515,18 +4459,13 @@ var AuthApi = class extends BaseApiClient {
|
|
|
4515
4459
|
}
|
|
4516
4460
|
return await response.json();
|
|
4517
4461
|
}
|
|
4518
|
-
/**
|
|
4462
|
+
/** @deprecated Browser continuity uses top-level /auth/continue navigation. */
|
|
4519
4463
|
async requestCrossDomainToken() {
|
|
4520
|
-
|
|
4521
|
-
method: "POST"
|
|
4522
|
-
});
|
|
4464
|
+
throw new Error("Use top-level /auth/continue navigation. CLI login uses OAuth with PKCE.");
|
|
4523
4465
|
}
|
|
4524
|
-
/**
|
|
4525
|
-
async exchangeToken(
|
|
4526
|
-
|
|
4527
|
-
method: "POST",
|
|
4528
|
-
body: JSON.stringify({ token })
|
|
4529
|
-
});
|
|
4466
|
+
/** @deprecated Browser handoffs redeem only at the destination Next server. */
|
|
4467
|
+
async exchangeToken(_token) {
|
|
4468
|
+
throw new Error("Use top-level /auth/continue navigation. CLI login uses OAuth with PKCE.");
|
|
4530
4469
|
}
|
|
4531
4470
|
/** Exchange a global Account login for an opaque org-scoped session handle. */
|
|
4532
4471
|
async exchangeSession(request) {
|
|
@@ -4707,9 +4646,17 @@ var AuthApi = class extends BaseApiClient {
|
|
|
4707
4646
|
);
|
|
4708
4647
|
}
|
|
4709
4648
|
// ===== ORGANIZATION MEMBERSHIP METHODS =====
|
|
4710
|
-
/**
|
|
4711
|
-
async getMyOrganizations() {
|
|
4712
|
-
|
|
4649
|
+
/** A searchable page of the current account's organizations. */
|
|
4650
|
+
async getMyOrganizations(options) {
|
|
4651
|
+
const params = new URLSearchParams();
|
|
4652
|
+
if (options?.page) params.set("page", String(options.page));
|
|
4653
|
+
if (options?.pageSize) params.set("pageSize", String(options.pageSize));
|
|
4654
|
+
if (options?.canCreateWorkspace) params.set("canCreateWorkspace", "true");
|
|
4655
|
+
if (options?.search) params.set("search", options.search);
|
|
4656
|
+
if (options?.organizationSlug !== void 0)
|
|
4657
|
+
params.set("organizationSlug", options.organizationSlug);
|
|
4658
|
+
for (const uuid of options?.organizationUuids ?? []) params.append("organizationUuids", uuid);
|
|
4659
|
+
return this.fetch(`o/mine?${params}`);
|
|
4713
4660
|
}
|
|
4714
4661
|
// ===== CREDENTIAL CHANGE METHODS =====
|
|
4715
4662
|
/**
|
|
@@ -5906,6 +5853,45 @@ var DashboardsApi = class extends BaseApiClient {
|
|
|
5906
5853
|
|
|
5907
5854
|
// src/api/data-types.ts
|
|
5908
5855
|
var DataTypesApi = class extends BaseApiClient {
|
|
5856
|
+
/** Read an anonymous Form's visitor-writable schema without a Member session. */
|
|
5857
|
+
async getAnonymousFormSchema(wid, formUuid) {
|
|
5858
|
+
return this.fetchPublicAnonymousForm(
|
|
5859
|
+
`w/${encodeURIComponent(wid)}/anonymous-forms/${encodeURIComponent(formUuid)}`
|
|
5860
|
+
);
|
|
5861
|
+
}
|
|
5862
|
+
/** Submit visitor-provided values. No cookie or API credential is sent. */
|
|
5863
|
+
async submitAnonymousForm(wid, formUuid, input) {
|
|
5864
|
+
return this.fetchPublicAnonymousForm(
|
|
5865
|
+
`w/${encodeURIComponent(wid)}/anonymous-forms/${encodeURIComponent(formUuid)}/submissions`,
|
|
5866
|
+
{ method: "POST", body: JSON.stringify(input) }
|
|
5867
|
+
);
|
|
5868
|
+
}
|
|
5869
|
+
async fetchPublicAnonymousForm(endpoint, options = {}) {
|
|
5870
|
+
const headers = new Headers(options.headers);
|
|
5871
|
+
if (options.body !== void 0) headers.set("Content-Type", "application/json");
|
|
5872
|
+
let response;
|
|
5873
|
+
try {
|
|
5874
|
+
response = await this.fetchFunction(`${this.baseUrl}/api/v2/${endpoint}`, {
|
|
5875
|
+
...options,
|
|
5876
|
+
credentials: "omit",
|
|
5877
|
+
headers
|
|
5878
|
+
});
|
|
5879
|
+
} catch {
|
|
5880
|
+
throw new ApiError("Anonymous Form request failed", 0);
|
|
5881
|
+
}
|
|
5882
|
+
if (!response.ok) {
|
|
5883
|
+
let message = `Request failed with status ${response.status}`;
|
|
5884
|
+
try {
|
|
5885
|
+
const body = await response.json();
|
|
5886
|
+
if (typeof body.detail === "string" && body.detail.length > 0) {
|
|
5887
|
+
message = body.detail;
|
|
5888
|
+
}
|
|
5889
|
+
} catch {
|
|
5890
|
+
}
|
|
5891
|
+
throw new ApiError(message, response.status);
|
|
5892
|
+
}
|
|
5893
|
+
return await response.json();
|
|
5894
|
+
}
|
|
5909
5895
|
/** Read the local Form's draft and optimistic publication counters. */
|
|
5910
5896
|
async getDataTypeDraft(wid, id) {
|
|
5911
5897
|
return this.fetch(`w/${wid}/data-types/${id}/schema-draft`);
|
|
@@ -7865,14 +7851,7 @@ var InquiriesApi = class extends BaseApiClient {
|
|
|
7865
7851
|
}
|
|
7866
7852
|
const queryString = params.toString();
|
|
7867
7853
|
const url = queryString ? `inquiries/export/csv?${queryString}` : "inquiries/export/csv";
|
|
7868
|
-
const response = await
|
|
7869
|
-
method: "GET",
|
|
7870
|
-
credentials: "include"
|
|
7871
|
-
});
|
|
7872
|
-
if (!response.ok) {
|
|
7873
|
-
const error = await response.json();
|
|
7874
|
-
throw new Error(error.detail || "Export failed");
|
|
7875
|
-
}
|
|
7854
|
+
const response = await this.fetchRaw(url, { method: "GET" });
|
|
7876
7855
|
return response.blob();
|
|
7877
7856
|
}
|
|
7878
7857
|
};
|
|
@@ -9440,8 +9419,12 @@ var OrgApi = class extends BaseApiClient {
|
|
|
9440
9419
|
* figure — the caps belong to the organization, so `cap - oneWorkspace`
|
|
9441
9420
|
* describes nothing.
|
|
9442
9421
|
*/
|
|
9443
|
-
async getOrgWorkspaceUsage(orgIdOrUuid) {
|
|
9444
|
-
|
|
9422
|
+
async getOrgWorkspaceUsage(orgIdOrUuid, options) {
|
|
9423
|
+
const params = new URLSearchParams();
|
|
9424
|
+
if (options?.page) params.set("page", String(options.page));
|
|
9425
|
+
if (options?.pageSize) params.set("pageSize", String(options.pageSize));
|
|
9426
|
+
if (options?.search) params.set("search", options.search);
|
|
9427
|
+
return this.fetch(`o/${orgIdOrUuid}/usage/workspaces?${params}`);
|
|
9445
9428
|
}
|
|
9446
9429
|
/**
|
|
9447
9430
|
* List members of an organization.
|
|
@@ -9565,8 +9548,14 @@ var OrgApi = class extends BaseApiClient {
|
|
|
9565
9548
|
* counts. Any org member may call it — members get the workspaces they
|
|
9566
9549
|
* belong to, org:read holders get all of them.
|
|
9567
9550
|
*/
|
|
9568
|
-
async getOrganizationWorkspaceSummaries(orgIdOrUuid) {
|
|
9569
|
-
|
|
9551
|
+
async getOrganizationWorkspaceSummaries(orgIdOrUuid, options) {
|
|
9552
|
+
const params = new URLSearchParams();
|
|
9553
|
+
if (options?.page) params.set("page", String(options.page));
|
|
9554
|
+
if (options?.pageSize) params.set("pageSize", String(options.pageSize));
|
|
9555
|
+
if (options?.search) params.set("search", options.search);
|
|
9556
|
+
return this.fetch(
|
|
9557
|
+
`o/${orgIdOrUuid}/workspaces/summary?${params}`
|
|
9558
|
+
);
|
|
9570
9559
|
}
|
|
9571
9560
|
/**
|
|
9572
9561
|
* Workspace-schema aggregates for one card, plus the caller's own member
|
|
@@ -10303,18 +10292,18 @@ function businessProfilePath(orgUuid, legalEntityUuid) {
|
|
|
10303
10292
|
// src/api/oauth.ts
|
|
10304
10293
|
function getOAuthPath(providerSlug, action) {
|
|
10305
10294
|
if (providerSlug === "google_calendar") {
|
|
10306
|
-
return `oauth/google/calendar/${action}`;
|
|
10295
|
+
return `api/v2/oauth/google/calendar/${action}`;
|
|
10307
10296
|
}
|
|
10308
10297
|
if (providerSlug === "gmail") {
|
|
10309
|
-
return `oauth/google/gmail/${action}`;
|
|
10298
|
+
return `api/v2/oauth/google/gmail/${action}`;
|
|
10310
10299
|
}
|
|
10311
10300
|
if (providerSlug === "gdrive") {
|
|
10312
|
-
return `oauth/google/drive/${action}`;
|
|
10301
|
+
return `api/v2/oauth/google/drive/${action}`;
|
|
10313
10302
|
}
|
|
10314
10303
|
if (providerSlug === "gsheets") {
|
|
10315
|
-
return `oauth/google/sheets/${action}`;
|
|
10304
|
+
return `api/v2/oauth/google/sheets/${action}`;
|
|
10316
10305
|
}
|
|
10317
|
-
return `oauth/${providerSlug}/${action}`;
|
|
10306
|
+
return `api/v2/oauth/${providerSlug}/${action}`;
|
|
10318
10307
|
}
|
|
10319
10308
|
function buildAuthorizeQuery(options) {
|
|
10320
10309
|
if (!options) return "";
|
|
@@ -13134,7 +13123,8 @@ var WorkflowsApi = class extends BaseApiClient {
|
|
|
13134
13123
|
/**
|
|
13135
13124
|
* Classify the prompts of a Workflow's draft revision.
|
|
13136
13125
|
*
|
|
13137
|
-
* A decision model reads each Task
|
|
13126
|
+
* A decision model reads each live Task with Workflow or Task instructions
|
|
13127
|
+
* and reports which
|
|
13138
13128
|
* prompt classifications probably apply. Each call asks the model again.
|
|
13139
13129
|
*/
|
|
13140
13130
|
async getWorkflowPromptClassifications(wid, workflowId) {
|
|
@@ -13510,22 +13500,32 @@ function planFields(options) {
|
|
|
13510
13500
|
}
|
|
13511
13501
|
return fields;
|
|
13512
13502
|
}
|
|
13503
|
+
function workspaceDirectoryParams(options = {}) {
|
|
13504
|
+
const params = new URLSearchParams();
|
|
13505
|
+
if (options.page !== void 0) params.set("page", String(options.page));
|
|
13506
|
+
if (options.pageSize !== void 0) params.set("pageSize", String(options.pageSize));
|
|
13507
|
+
if (options.search) params.set("search", options.search);
|
|
13508
|
+
if (options.organizationUuid) params.set("organizationUuid", options.organizationUuid);
|
|
13509
|
+
for (const uuid of options.workspaceUuids ?? []) params.append("workspaceUuids", uuid);
|
|
13510
|
+
for (const id of options.workspaceIds ?? []) params.append("workspaceIds", String(id));
|
|
13511
|
+
return params;
|
|
13512
|
+
}
|
|
13513
13513
|
var WorkspacesApi = class extends BaseApiClient {
|
|
13514
|
-
/**
|
|
13515
|
-
async getWorkspaces() {
|
|
13516
|
-
return this.fetch(
|
|
13514
|
+
/** A bounded page of workspaces accessible to the current account. */
|
|
13515
|
+
async getWorkspaces(options) {
|
|
13516
|
+
return this.fetch(`w/?${workspaceDirectoryParams(options)}`);
|
|
13517
13517
|
}
|
|
13518
|
-
/**
|
|
13519
|
-
|
|
13520
|
-
|
|
13521
|
-
|
|
13522
|
-
|
|
13523
|
-
async getDeviceAdmissibleWorkspaces() {
|
|
13524
|
-
return this.fetch("w/?can_use_device=1");
|
|
13518
|
+
/** A bounded page of companion-device candidates, with admission checked per row. */
|
|
13519
|
+
async getDeviceAdmissibleWorkspaces(options) {
|
|
13520
|
+
const params = workspaceDirectoryParams(options);
|
|
13521
|
+
params.set("can_use_device", "1");
|
|
13522
|
+
return this.fetch(`w/?${params}`);
|
|
13525
13523
|
}
|
|
13526
|
-
/**
|
|
13527
|
-
async getPublicWorkspaces() {
|
|
13528
|
-
return this.fetch(
|
|
13524
|
+
/** A bounded page of publicly visible workspaces. */
|
|
13525
|
+
async getPublicWorkspaces(options) {
|
|
13526
|
+
return this.fetch(
|
|
13527
|
+
`w/public?${workspaceDirectoryParams(options)}`
|
|
13528
|
+
);
|
|
13529
13529
|
}
|
|
13530
13530
|
/** Get a single workspace by ID. */
|
|
13531
13531
|
async getWorkspace(wid) {
|
|
@@ -13808,13 +13808,22 @@ var WorkspacesApi = class extends BaseApiClient {
|
|
|
13808
13808
|
* @param scope - Export scope ("workflows" for all workflows only). Cannot be combined with workflowId.
|
|
13809
13809
|
* @param exclude - Comma-separated entity categories to exclude (e.g. "channels")
|
|
13810
13810
|
*/
|
|
13811
|
-
async exportWorkspace(wid, workflowId, scope, exclude) {
|
|
13811
|
+
async exportWorkspace(wid, workflowId, scope, exclude, options) {
|
|
13812
13812
|
const searchParams = new URLSearchParams();
|
|
13813
13813
|
if (workflowId !== void 0) searchParams.set("workflow_id", String(workflowId));
|
|
13814
13814
|
if (scope !== void 0) searchParams.set("scope", scope);
|
|
13815
13815
|
if (exclude !== void 0) searchParams.set("exclude", exclude);
|
|
13816
|
+
if (options?.revisionId !== void 0)
|
|
13817
|
+
searchParams.set("revision_id", String(options.revisionId));
|
|
13818
|
+
if (options?.revisionMode !== void 0) {
|
|
13819
|
+
searchParams.set("revision_mode", options.revisionMode);
|
|
13820
|
+
} else if (options?.revisionId === void 0) {
|
|
13821
|
+
searchParams.set("revision_mode", "active");
|
|
13822
|
+
}
|
|
13816
13823
|
const qs = searchParams.toString();
|
|
13817
|
-
|
|
13824
|
+
const data = await this.fetch(`w/${wid}/export${qs ? `?${qs}` : ""}`);
|
|
13825
|
+
this.verifyExportSelection(data, workflowId, options);
|
|
13826
|
+
return data;
|
|
13818
13827
|
}
|
|
13819
13828
|
/** Preview the result of importing workspace configuration without applying changes.
|
|
13820
13829
|
*
|
|
@@ -13873,15 +13882,60 @@ var WorkspacesApi = class extends BaseApiClient {
|
|
|
13873
13882
|
* target workspace.
|
|
13874
13883
|
*/
|
|
13875
13884
|
/** Download a workspace export ZIP (``export.json`` + file blobs). */
|
|
13876
|
-
async exportWorkspaceZip(wid, workflowId, scope, exclude) {
|
|
13885
|
+
async exportWorkspaceZip(wid, workflowId, scope, exclude, options) {
|
|
13877
13886
|
const searchParams = new URLSearchParams();
|
|
13878
13887
|
if (workflowId !== void 0) searchParams.set("workflow_id", String(workflowId));
|
|
13879
13888
|
if (scope !== void 0) searchParams.set("scope", scope);
|
|
13880
13889
|
if (exclude !== void 0) searchParams.set("exclude", exclude);
|
|
13890
|
+
if (options?.revisionId !== void 0)
|
|
13891
|
+
searchParams.set("revision_id", String(options.revisionId));
|
|
13892
|
+
if (options?.revisionMode !== void 0) {
|
|
13893
|
+
searchParams.set("revision_mode", options.revisionMode);
|
|
13894
|
+
} else if (options?.revisionId === void 0) {
|
|
13895
|
+
searchParams.set("revision_mode", "active");
|
|
13896
|
+
}
|
|
13881
13897
|
const qs = searchParams.toString();
|
|
13882
13898
|
const response = await this.fetchRaw(`w/${wid}/export.zip${qs ? `?${qs}` : ""}`);
|
|
13899
|
+
if (options?.revisionId !== void 0 || options?.revisionMode === "all") {
|
|
13900
|
+
const version = response.headers.get("X-Gravityrail-Export-Version");
|
|
13901
|
+
const selection = response.headers.get("X-Gravityrail-Export-Selection");
|
|
13902
|
+
const selected = options?.revisionId !== void 0;
|
|
13903
|
+
if (version !== (selected ? "1.0" : "2.0") || selection !== (selected ? "selected" : "all") || selected && (workflowId === void 0 || !response.headers.get("X-Gravityrail-Export-Revision-Uuid")?.trim() || !response.headers.get("X-Gravityrail-Export-Workflow-Uuid")?.trim())) {
|
|
13904
|
+
try {
|
|
13905
|
+
await response.body?.cancel();
|
|
13906
|
+
} catch {
|
|
13907
|
+
}
|
|
13908
|
+
throw new Error(
|
|
13909
|
+
"Server did not return verified Workflow revision export headers. Request an explicit active snapshot or use a server that supports export provenance."
|
|
13910
|
+
);
|
|
13911
|
+
}
|
|
13912
|
+
}
|
|
13883
13913
|
return response.arrayBuffer();
|
|
13884
13914
|
}
|
|
13915
|
+
// Compatibility shape check only. The backend WorkspaceExportData
|
|
13916
|
+
// _validate_revision_archive owns full graph/publication invariants.
|
|
13917
|
+
verifyExportSelection(data, workflowId, options) {
|
|
13918
|
+
const hasUuid = (value) => typeof value === "string" && value.trim().length > 0;
|
|
13919
|
+
if (options?.revisionId !== void 0) {
|
|
13920
|
+
const revisions = data?.workflowRevisions;
|
|
13921
|
+
if (workflowId === void 0 || data?.version !== "1.0" || data.workflowRevisionSelection !== "selected" || !Array.isArray(revisions) || revisions.length !== 1) {
|
|
13922
|
+
throw new Error(
|
|
13923
|
+
"Server did not return the requested Workflow revision. Use a server that supports revision selection."
|
|
13924
|
+
);
|
|
13925
|
+
}
|
|
13926
|
+
if (!hasUuid(revisions[0]?.uuid) || !hasUuid(revisions[0]?.workflow?.uuid) || !hasUuid(data.workflows?.[0]?.uuid) || data.workflows?.length !== 1 || revisions[0]?.workflow?.uuid !== data.workflows[0]?.uuid) {
|
|
13927
|
+
throw new Error("Server export does not match the requested Workflow revision.");
|
|
13928
|
+
}
|
|
13929
|
+
} else if (options?.revisionMode === "all" && (data?.version !== "2.0" || data.workflowRevisionSelection !== "all" || !Array.isArray(data.workflowRevisions) || !Array.isArray(data.workflows) || data.workflows.some(
|
|
13930
|
+
(workflow) => !hasUuid(workflow?.uuid) || !data.workflowRevisions?.some(
|
|
13931
|
+
(revision) => hasUuid(revision?.uuid) && revision?.workflow?.uuid === workflow.uuid
|
|
13932
|
+
)
|
|
13933
|
+
))) {
|
|
13934
|
+
throw new Error(
|
|
13935
|
+
"Server did not return all Workflow revisions. Request an explicit active snapshot or use a server that supports revision archives."
|
|
13936
|
+
);
|
|
13937
|
+
}
|
|
13938
|
+
}
|
|
13885
13939
|
/** Import workspace configuration from exported JSON, or from a ZIP archive.
|
|
13886
13940
|
* Pass ``archive`` (the ZIP from ``exportWorkspaceZip``) to restore file
|
|
13887
13941
|
* contents. ``exportData`` may be omitted when the ZIP contains it.
|
|
@@ -14418,6 +14472,7 @@ var WORKSPACE_METHOD_NAMES = [
|
|
|
14418
14472
|
"getAgents",
|
|
14419
14473
|
"getAnonymousResolution",
|
|
14420
14474
|
"getAnonymousFormSubmission",
|
|
14475
|
+
"getAnonymousFormSchema",
|
|
14421
14476
|
"getAnonymousResolutions",
|
|
14422
14477
|
"getApiKeys",
|
|
14423
14478
|
"getArchiveBlockers",
|
|
@@ -14803,6 +14858,7 @@ var WORKSPACE_METHOD_NAMES = [
|
|
|
14803
14858
|
"linkUnlinkedSalesforceMembersByIdentity",
|
|
14804
14859
|
"listAbilities",
|
|
14805
14860
|
"listAnonymousFormSubmissions",
|
|
14861
|
+
"submitAnonymousForm",
|
|
14806
14862
|
"listAppConnections",
|
|
14807
14863
|
"listAvailableAppConnections",
|
|
14808
14864
|
"listInstallations",
|
|
@@ -15256,6 +15312,8 @@ function bindTrackedLinksMethods(target, trackedLinks) {
|
|
|
15256
15312
|
|
|
15257
15313
|
// src/client/bindings/data-types.ts
|
|
15258
15314
|
function bindDataTypesMethods(target, dataTypes) {
|
|
15315
|
+
target.getAnonymousFormSchema = dataTypes.getAnonymousFormSchema.bind(dataTypes);
|
|
15316
|
+
target.submitAnonymousForm = dataTypes.submitAnonymousForm.bind(dataTypes);
|
|
15259
15317
|
target.getDataTypeDraft = dataTypes.getDataTypeDraft.bind(dataTypes);
|
|
15260
15318
|
target.saveDataTypeDraft = dataTypes.saveDataTypeDraft.bind(dataTypes);
|
|
15261
15319
|
target.publishDataTypeDraft = dataTypes.publishDataTypeDraft.bind(dataTypes);
|
|
@@ -16450,6 +16508,8 @@ var GravityRailClient = class {
|
|
|
16450
16508
|
getCelContext;
|
|
16451
16509
|
validateCelExpression;
|
|
16452
16510
|
// Data Types API
|
|
16511
|
+
getAnonymousFormSchema;
|
|
16512
|
+
submitAnonymousForm;
|
|
16453
16513
|
getDataTypes;
|
|
16454
16514
|
getDataTypeDraft;
|
|
16455
16515
|
saveDataTypeDraft;
|
|
@@ -17744,6 +17804,86 @@ var ChatInteractionController = class {
|
|
|
17744
17804
|
this.pending = void 0;
|
|
17745
17805
|
}
|
|
17746
17806
|
};
|
|
17807
|
+
|
|
17808
|
+
// src/workflows/path.ts
|
|
17809
|
+
function getPathDefinition(path) {
|
|
17810
|
+
if (path.routing != null) {
|
|
17811
|
+
const routing = path.routing;
|
|
17812
|
+
if (typeof routing !== "object" || Array.isArray(routing)) return null;
|
|
17813
|
+
const fields = routing;
|
|
17814
|
+
if (fields.kind !== void 0 && fields.kind !== "outcome" || typeof fields.outcome !== "string" || !/^[a-z][a-z0-9_]{0,31}$/.test(fields.outcome) || Object.keys(fields).some((key) => key !== "kind" && key !== "outcome"))
|
|
17815
|
+
return null;
|
|
17816
|
+
return {
|
|
17817
|
+
availability: { kind: "always", expression: null, decisionPrompt: null },
|
|
17818
|
+
selection: {
|
|
17819
|
+
mode: "automatic",
|
|
17820
|
+
rule: "task_outcome",
|
|
17821
|
+
choicePrompt: null,
|
|
17822
|
+
condition: null,
|
|
17823
|
+
outcome: fields.outcome,
|
|
17824
|
+
policy: "matching_outcome",
|
|
17825
|
+
priority: null
|
|
17826
|
+
}
|
|
17827
|
+
};
|
|
17828
|
+
}
|
|
17829
|
+
const expression = path.celCondition?.trim() ? path.celCondition : null;
|
|
17830
|
+
const automatic = expression !== null && Boolean(path.required || path.autoNext);
|
|
17831
|
+
return {
|
|
17832
|
+
availability: {
|
|
17833
|
+
kind: expression === null ? "always" : "cel",
|
|
17834
|
+
expression,
|
|
17835
|
+
decisionPrompt: null
|
|
17836
|
+
},
|
|
17837
|
+
selection: {
|
|
17838
|
+
mode: automatic ? "automatic" : "choice",
|
|
17839
|
+
rule: automatic ? "when_available" : "choice",
|
|
17840
|
+
choicePrompt: automatic ? null : path.choicePrompt?.trim() || null,
|
|
17841
|
+
condition: null,
|
|
17842
|
+
outcome: null,
|
|
17843
|
+
policy: automatic ? "first_match" : "choose_one",
|
|
17844
|
+
priority: automatic ? path.order ?? 0 : null
|
|
17845
|
+
}
|
|
17846
|
+
};
|
|
17847
|
+
}
|
|
17848
|
+
function pathDraftFromStorage(path) {
|
|
17849
|
+
const definition = getPathDefinition(path);
|
|
17850
|
+
if (!definition || definition.selection.rule === "task_outcome") {
|
|
17851
|
+
throw new Error("This Path requires its dedicated routing editor.");
|
|
17852
|
+
}
|
|
17853
|
+
return {
|
|
17854
|
+
availabilityCondition: path.celCondition ?? "",
|
|
17855
|
+
selectionMode: definition.selection.mode,
|
|
17856
|
+
choicePrompt: path.choicePrompt ?? "",
|
|
17857
|
+
selectionPriority: path.order ?? 0
|
|
17858
|
+
};
|
|
17859
|
+
}
|
|
17860
|
+
function pathDraftToStorage(draft, original) {
|
|
17861
|
+
const initial = pathDraftFromStorage(original);
|
|
17862
|
+
const hasCondition = draft.availabilityCondition.trim() !== "";
|
|
17863
|
+
if (draft.selectionMode === "automatic" && !hasCondition) {
|
|
17864
|
+
throw new Error("Automatic Path selection requires a CEL condition.");
|
|
17865
|
+
}
|
|
17866
|
+
if (draft.availabilityCondition === initial.availabilityCondition && draft.selectionMode === initial.selectionMode && draft.choicePrompt === initial.choicePrompt && draft.selectionPriority === initial.selectionPriority)
|
|
17867
|
+
return original;
|
|
17868
|
+
const celCondition = draft.availabilityCondition === initial.availabilityCondition ? original.celCondition : draft.availabilityCondition.trim() ? draft.availabilityCondition : null;
|
|
17869
|
+
const choicePrompt = draft.choicePrompt === initial.choicePrompt ? original.choicePrompt : draft.choicePrompt.trim() ? draft.choicePrompt : null;
|
|
17870
|
+
let autoNext = original.autoNext;
|
|
17871
|
+
let required = original.required;
|
|
17872
|
+
if (draft.selectionMode === "automatic" && initial.selectionMode !== "automatic") {
|
|
17873
|
+
autoNext = true;
|
|
17874
|
+
} else if (draft.selectionMode === "choice" && (hasCondition || initial.availabilityCondition.trim() !== "") && Boolean(autoNext || required)) {
|
|
17875
|
+
autoNext = false;
|
|
17876
|
+
required = false;
|
|
17877
|
+
}
|
|
17878
|
+
return {
|
|
17879
|
+
...original,
|
|
17880
|
+
celCondition,
|
|
17881
|
+
choicePrompt,
|
|
17882
|
+
autoNext,
|
|
17883
|
+
required,
|
|
17884
|
+
...draft.selectionPriority === initial.selectionPriority ? {} : { order: draft.selectionPriority }
|
|
17885
|
+
};
|
|
17886
|
+
}
|
|
17747
17887
|
// Annotate the CommonJS export names for ESM import in node:
|
|
17748
17888
|
0 && (module.exports = {
|
|
17749
17889
|
AGENT_NOTE_ORIGINS,
|
|
@@ -18068,6 +18208,7 @@ var ChatInteractionController = class {
|
|
|
18068
18208
|
getFeaturesByCategory,
|
|
18069
18209
|
getFeaturesByVisibility,
|
|
18070
18210
|
getMissingDependencies,
|
|
18211
|
+
getPathDefinition,
|
|
18071
18212
|
getResourceMetadata,
|
|
18072
18213
|
getRevisionState,
|
|
18073
18214
|
getTTSProvider,
|
|
@@ -18085,6 +18226,8 @@ var ChatInteractionController = class {
|
|
|
18085
18226
|
isSMSDeliveryFailureStatus,
|
|
18086
18227
|
parseExperimentStatCount,
|
|
18087
18228
|
parseOrgAuthChallenge,
|
|
18229
|
+
pathDraftFromStorage,
|
|
18230
|
+
pathDraftToStorage,
|
|
18088
18231
|
requiresAssistantParticipant,
|
|
18089
18232
|
usesBuiltinAssistant,
|
|
18090
18233
|
validateFeatureDependencies
|