@elevasis/core 0.35.0 → 0.36.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/dist/auth/index.d.ts +45 -2
- package/dist/auth/index.js +16 -1
- package/dist/index.js +2 -0
- package/dist/organization-model/index.js +2 -0
- package/dist/test-utils/index.d.ts +18 -0
- package/dist/test-utils/index.js +1 -1
- package/package.json +1 -1
- package/src/auth/multi-tenancy/invitations/api-schemas.ts +24 -9
- package/src/auth/multi-tenancy/invitations/index.ts +11 -9
- package/src/auth/multi-tenancy/organizations/__tests__/api-schemas.test.ts +39 -3
- package/src/auth/multi-tenancy/organizations/api-schemas.ts +36 -8
- package/src/auth/multi-tenancy/organizations/index.ts +13 -11
- package/src/business/acquisition/build-templates.test.ts +34 -0
- package/src/business/acquisition/build-templates.ts +8 -1
- package/src/organization-model/__tests__/lookup-helpers.test.ts +34 -0
- package/src/organization-model/__tests__/migration-helpers.test.ts +11 -11
- package/src/organization-model/helpers.ts +18 -0
- package/src/organization-model/migration-helpers.ts +8 -1
- package/src/platform/constants/versions.ts +3 -3
- package/src/platform/registry/__tests__/resource-registry.test.ts +2121 -2053
- package/src/platform/registry/resource-registry.ts +2 -0
- package/src/scaffold-registry/index.ts +4 -4
- package/src/supabase/database.types.ts +15 -0
- package/src/test-utils/rls/RLSTestContext.ts +3 -3
package/dist/auth/index.d.ts
CHANGED
|
@@ -349,6 +349,18 @@ declare const UpdateOrganizationSchema: z.ZodObject<{
|
|
|
349
349
|
}, z.core.$strip>>>>;
|
|
350
350
|
metadata: z.ZodOptional<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
351
351
|
}, z.core.$strict>;
|
|
352
|
+
/**
|
|
353
|
+
* Update organization auth config
|
|
354
|
+
* PATCH /organizations/:id/config/auth
|
|
355
|
+
*
|
|
356
|
+
* Security:
|
|
357
|
+
* - Narrow config surface: only appOrigin is accepted
|
|
358
|
+
* - App origin is an origin URL, not a route URL
|
|
359
|
+
* - Strict mode prevents accidental writes to unrelated config keys
|
|
360
|
+
*/
|
|
361
|
+
declare const UpdateOrganizationAuthConfigSchema: z.ZodObject<{
|
|
362
|
+
appOrigin: z.ZodString;
|
|
363
|
+
}, z.core.$strict>;
|
|
352
364
|
/**
|
|
353
365
|
* List organizations with filters
|
|
354
366
|
* GET /organizations
|
|
@@ -364,6 +376,7 @@ declare const ListOrganizationsQuerySchema: z.ZodObject<{
|
|
|
364
376
|
}, z.core.$strip>;
|
|
365
377
|
type CreateOrganizationInput = z.infer<typeof CreateOrganizationSchema>;
|
|
366
378
|
type UpdateOrganizationInput = z.infer<typeof UpdateOrganizationSchema>;
|
|
379
|
+
type UpdateOrganizationAuthConfigInput = z.infer<typeof UpdateOrganizationAuthConfigSchema>;
|
|
367
380
|
type ListOrganizationsQuery = z.infer<typeof ListOrganizationsQuerySchema>;
|
|
368
381
|
type OrganizationIdParam = z.infer<typeof OrganizationIdParamSchema>;
|
|
369
382
|
|
|
@@ -3876,6 +3889,17 @@ type Database = {
|
|
|
3876
3889
|
Args: never;
|
|
3877
3890
|
Returns: undefined;
|
|
3878
3891
|
};
|
|
3892
|
+
repair_membership_role_assignments: {
|
|
3893
|
+
Args: never;
|
|
3894
|
+
Returns: {
|
|
3895
|
+
membership_id: string;
|
|
3896
|
+
organization_id: string;
|
|
3897
|
+
repaired: boolean;
|
|
3898
|
+
role_id: string;
|
|
3899
|
+
role_slug: string;
|
|
3900
|
+
user_id: string;
|
|
3901
|
+
}[];
|
|
3902
|
+
};
|
|
3879
3903
|
sync_all_memberships_with_role: {
|
|
3880
3904
|
Args: {
|
|
3881
3905
|
p_role_id: string;
|
|
@@ -3888,6 +3912,13 @@ type Database = {
|
|
|
3888
3912
|
};
|
|
3889
3913
|
Returns: undefined;
|
|
3890
3914
|
};
|
|
3915
|
+
update_membership_role_assignment: {
|
|
3916
|
+
Args: {
|
|
3917
|
+
p_role_slug: string;
|
|
3918
|
+
p_workos_membership_id: string;
|
|
3919
|
+
};
|
|
3920
|
+
Returns: string;
|
|
3921
|
+
};
|
|
3891
3922
|
upsert_user_profile: {
|
|
3892
3923
|
Args: never;
|
|
3893
3924
|
Returns: {
|
|
@@ -4108,6 +4139,17 @@ declare const SendInvitationSchema: z.ZodObject<{
|
|
|
4108
4139
|
declare const AcceptInvitationSchema: z.ZodObject<{
|
|
4109
4140
|
invitation_token: z.ZodString;
|
|
4110
4141
|
}, z.core.$strict>;
|
|
4142
|
+
/**
|
|
4143
|
+
* Public invitation router query
|
|
4144
|
+
* GET /api/invite
|
|
4145
|
+
*
|
|
4146
|
+
* Security:
|
|
4147
|
+
* - Token validated as present only; service layer performs WorkOS lookup
|
|
4148
|
+
* - Router redirects with only the token needed by tenant AuthKit login
|
|
4149
|
+
*/
|
|
4150
|
+
declare const InviteRouterQuerySchema: z.ZodObject<{
|
|
4151
|
+
invitation_token: z.ZodString;
|
|
4152
|
+
}, z.core.$strict>;
|
|
4111
4153
|
/**
|
|
4112
4154
|
* List invitations with filters
|
|
4113
4155
|
* GET /invitations
|
|
@@ -4130,6 +4172,7 @@ declare const ListInvitationsQuerySchema: z.ZodObject<{
|
|
|
4130
4172
|
}, z.core.$strict>;
|
|
4131
4173
|
type SendInvitationInput = z.infer<typeof SendInvitationSchema>;
|
|
4132
4174
|
type AcceptInvitationInput = z.infer<typeof AcceptInvitationSchema>;
|
|
4175
|
+
type InviteRouterQuery = z.infer<typeof InviteRouterQuerySchema>;
|
|
4133
4176
|
type ListInvitationsQuery = z.infer<typeof ListInvitationsQuerySchema>;
|
|
4134
4177
|
type InvitationIdParam = z.infer<typeof InvitationIdParamSchema>;
|
|
4135
4178
|
|
|
@@ -5415,5 +5458,5 @@ interface AccessModel {
|
|
|
5415
5458
|
declare function checkAccess(input: AccessKeyInput, ctx: AccessContext): AccessAnswer;
|
|
5416
5459
|
declare function createAccessModel(organizationModel: OrganizationModel): AccessModel;
|
|
5417
5460
|
|
|
5418
|
-
export { AcceptInvitationSchema, AccessActionSchema, AccessKeyInputSchema, AccessKeyObjectSchema, AccessKeySchema, AccessKeys, AssignMembershipRoleRequestSchema, CreateMembershipSchema, CreateOrgRoleRequestSchema, CreateOrganizationSchema, DEFAULT_ACCESS_ACTION, DIAGNOSTIC_VIEW_ACCESS_KEYS, ExternalIdParamSchema, InvitationIdParamSchema, ListInvitationsQuerySchema, ListMembershipsQuerySchema, ListOrganizationsQuerySchema, ListUsersQuerySchema, MEMBERSHIP_STATUS_COLORS, MembershipIdParamSchema, MembershipParamsSchema, MembershipRoleParamsSchema, MembershipRoleSchema, MembershipStatusSchema, MyOrgPermissionsResponseSchema, NormalizedAccessKeySchema, OrgIdParamSchema, OrgRoleParamsSchema, OrgRolesParamsSchema, OrganizationDomainSchema, OrganizationIdParamSchema, OrganizationNameSchema, PERMISSIONS, PERMISSION_CATALOG, PLATFORM_ADMIN_ACCESS_KEY, PLATFORM_ADMIN_ACCESS_KEY_SHORTHAND, SendInvitationSchema, THEME_PRESETS, ThemePresetEnum, UpdateMembershipSchema, UpdateMyProfileSchema, UpdateOrgRoleRequestSchema, UpdateOrganizationSchema, UpdateUserSchema, UserIdParamSchema, accessKeyToString, asSupabaseOrgId, asSupabaseOrgIdOrNull, asWorkOsOrgId, asWorkOsOrgIdOrNull, brandSupabaseOrgId, brandWorkOsOrgId, checkAccess, createAccessModel, deriveAccessKeyCatalog, findAccessCatalogEntry, isPermissionKey, isSupabaseOrgId, isWorkOsOrgId, listAccessKeys, normalizeAccessKey, rolePermissionForAccessKey, transformMembershipToTableRow, transformSupabaseToInvitation, transformSupabaseToMembership };
|
|
5419
|
-
export type { AcceptInvitationInput, AcceptInvitationRequest, AcceptanceResult, AccessAction, AccessAnswer, AccessCatalogEntry, AccessCatalogEntrySource, AccessContext, AccessContextMembership, AccessContextProfile, AccessKey, AccessKeyCatalog, AccessKeyInput, AccessKeyObject, AccessModel, AccessReason, AccessRestrictedBy, AdminOrgInvitation, AssignMembershipRoleInput, CreateCredentialParams, CreateMembershipInput, CreateMembershipRequest, CreateOrgRoleInput, CreateOrganizationInput, CreateUserRequest, CredentialMetadata, DeriveAccessKeyCatalogOptions, ExternalIdParam, Invitation, InvitationIdParam, InvitationListQuery, InvitationState, InvitationSummaryResponse, InvitationWithDetails, ListInvitationsParams, ListInvitationsQuery, ListMembershipsParams, ListMembershipsQuery, ListMembershipsResponse, ListOrganizationsQuery, ListUsersParams, ListUsersQuery, ListUsersResponse, MembershipIdParam, MembershipParams, MembershipRole, MembershipRoleParams, MembershipStatus, MembershipTableRow, MembershipWithDetails, MyOrgPermissionsResponse, NormalizedAccessKey, OrgIdParam, OrgMetadata, OrgRoleParams, OrgRolesParams, Organization, OrganizationIdParam, OrganizationMembership, OrganizationSummary, PermissionDescriptor, PermissionKey, SendInvitationInput, SendInvitationRequest, SupabaseOrgId, SupabaseOrgInvitation, SupabaseOrgInvitationInsert, SupabaseOrgInvitationUpdate, SupabaseOrgMembership, SupabaseOrgMembershipInsert, SupabaseOrgMembershipUpdate, ThemePresetName, UpdateMembershipInput, UpdateMembershipRequest, UpdateMyProfileInput, UpdateOrgRoleInput, UpdateOrganizationInput, UpdateUserInput, UpdateUserRequest, User, UserConfig, UserIdParam, UserSummary, WorkOsOrgId };
|
|
5461
|
+
export { AcceptInvitationSchema, AccessActionSchema, AccessKeyInputSchema, AccessKeyObjectSchema, AccessKeySchema, AccessKeys, AssignMembershipRoleRequestSchema, CreateMembershipSchema, CreateOrgRoleRequestSchema, CreateOrganizationSchema, DEFAULT_ACCESS_ACTION, DIAGNOSTIC_VIEW_ACCESS_KEYS, ExternalIdParamSchema, InvitationIdParamSchema, InviteRouterQuerySchema, ListInvitationsQuerySchema, ListMembershipsQuerySchema, ListOrganizationsQuerySchema, ListUsersQuerySchema, MEMBERSHIP_STATUS_COLORS, MembershipIdParamSchema, MembershipParamsSchema, MembershipRoleParamsSchema, MembershipRoleSchema, MembershipStatusSchema, MyOrgPermissionsResponseSchema, NormalizedAccessKeySchema, OrgIdParamSchema, OrgRoleParamsSchema, OrgRolesParamsSchema, OrganizationDomainSchema, OrganizationIdParamSchema, OrganizationNameSchema, PERMISSIONS, PERMISSION_CATALOG, PLATFORM_ADMIN_ACCESS_KEY, PLATFORM_ADMIN_ACCESS_KEY_SHORTHAND, SendInvitationSchema, THEME_PRESETS, ThemePresetEnum, UpdateMembershipSchema, UpdateMyProfileSchema, UpdateOrgRoleRequestSchema, UpdateOrganizationAuthConfigSchema, UpdateOrganizationSchema, UpdateUserSchema, UserIdParamSchema, accessKeyToString, asSupabaseOrgId, asSupabaseOrgIdOrNull, asWorkOsOrgId, asWorkOsOrgIdOrNull, brandSupabaseOrgId, brandWorkOsOrgId, checkAccess, createAccessModel, deriveAccessKeyCatalog, findAccessCatalogEntry, isPermissionKey, isSupabaseOrgId, isWorkOsOrgId, listAccessKeys, normalizeAccessKey, rolePermissionForAccessKey, transformMembershipToTableRow, transformSupabaseToInvitation, transformSupabaseToMembership };
|
|
5462
|
+
export type { AcceptInvitationInput, AcceptInvitationRequest, AcceptanceResult, AccessAction, AccessAnswer, AccessCatalogEntry, AccessCatalogEntrySource, AccessContext, AccessContextMembership, AccessContextProfile, AccessKey, AccessKeyCatalog, AccessKeyInput, AccessKeyObject, AccessModel, AccessReason, AccessRestrictedBy, AdminOrgInvitation, AssignMembershipRoleInput, CreateCredentialParams, CreateMembershipInput, CreateMembershipRequest, CreateOrgRoleInput, CreateOrganizationInput, CreateUserRequest, CredentialMetadata, DeriveAccessKeyCatalogOptions, ExternalIdParam, Invitation, InvitationIdParam, InvitationListQuery, InvitationState, InvitationSummaryResponse, InvitationWithDetails, InviteRouterQuery, ListInvitationsParams, ListInvitationsQuery, ListMembershipsParams, ListMembershipsQuery, ListMembershipsResponse, ListOrganizationsQuery, ListUsersParams, ListUsersQuery, ListUsersResponse, MembershipIdParam, MembershipParams, MembershipRole, MembershipRoleParams, MembershipStatus, MembershipTableRow, MembershipWithDetails, MyOrgPermissionsResponse, NormalizedAccessKey, OrgIdParam, OrgMetadata, OrgRoleParams, OrgRolesParams, Organization, OrganizationIdParam, OrganizationMembership, OrganizationSummary, PermissionDescriptor, PermissionKey, SendInvitationInput, SendInvitationRequest, SupabaseOrgId, SupabaseOrgInvitation, SupabaseOrgInvitationInsert, SupabaseOrgInvitationUpdate, SupabaseOrgMembership, SupabaseOrgMembershipInsert, SupabaseOrgMembershipUpdate, ThemePresetName, UpdateMembershipInput, UpdateMembershipRequest, UpdateMyProfileInput, UpdateOrgRoleInput, UpdateOrganizationAuthConfigInput, UpdateOrganizationInput, UpdateUserInput, UpdateUserRequest, User, UserConfig, UserIdParam, UserSummary, WorkOsOrgId };
|
package/dist/auth/index.js
CHANGED
|
@@ -226,6 +226,16 @@ var CreateOrganizationSchema = z.object({
|
|
|
226
226
|
var UpdateOrganizationSchema = CreateOrganizationSchema.partial().strict().refine((data) => Object.keys(data).length > 0, {
|
|
227
227
|
message: "At least one field (name, domainData, or metadata) must be provided"
|
|
228
228
|
});
|
|
229
|
+
var UpdateOrganizationAuthConfigSchema = z.object({
|
|
230
|
+
appOrigin: z.string().trim().url("App origin must be a valid URL").max(2048, "App origin must be at most 2048 characters").refine((value) => {
|
|
231
|
+
try {
|
|
232
|
+
const url = new URL(value);
|
|
233
|
+
return url.pathname === "/" && url.search === "" && url.hash === "";
|
|
234
|
+
} catch {
|
|
235
|
+
return false;
|
|
236
|
+
}
|
|
237
|
+
}, "App origin must not include a path, query string, or hash")
|
|
238
|
+
}).strict();
|
|
229
239
|
var ListOrganizationsQuerySchema = z.object({
|
|
230
240
|
limit: z.coerce.number().int().min(1).max(100).default(20),
|
|
231
241
|
before: z.string().optional(),
|
|
@@ -394,6 +404,9 @@ var SendInvitationSchema = z.object({
|
|
|
394
404
|
var AcceptInvitationSchema = z.object({
|
|
395
405
|
invitation_token: z.string().min(1, "Invitation token is required")
|
|
396
406
|
}).strict();
|
|
407
|
+
var InviteRouterQuerySchema = z.object({
|
|
408
|
+
invitation_token: z.string().min(1, "Invitation token is required")
|
|
409
|
+
}).strict();
|
|
397
410
|
var ListInvitationsQuerySchema = z.object({
|
|
398
411
|
organizationId: z.string().optional(),
|
|
399
412
|
userId: z.string().optional(),
|
|
@@ -410,6 +423,8 @@ function childSystemsOf(system) {
|
|
|
410
423
|
return system.systems ?? system.subsystems ?? {};
|
|
411
424
|
}
|
|
412
425
|
function getSystem(model, path) {
|
|
426
|
+
const flatMatch = model.systems[path];
|
|
427
|
+
if (flatMatch !== void 0) return flatMatch;
|
|
413
428
|
const segments = path.split(".");
|
|
414
429
|
let current = model.systems;
|
|
415
430
|
let node;
|
|
@@ -634,4 +649,4 @@ function createAccessModel(organizationModel) {
|
|
|
634
649
|
};
|
|
635
650
|
}
|
|
636
651
|
|
|
637
|
-
export { AcceptInvitationSchema, AccessActionSchema, AccessKeyInputSchema, AccessKeyObjectSchema, AccessKeySchema, AccessKeys, AssignMembershipRoleRequestSchema, CreateMembershipSchema, CreateOrgRoleRequestSchema, CreateOrganizationSchema, DEFAULT_ACCESS_ACTION, DIAGNOSTIC_VIEW_ACCESS_KEYS, ExternalIdParamSchema, InvitationIdParamSchema, ListInvitationsQuerySchema, ListMembershipsQuerySchema, ListOrganizationsQuerySchema, ListUsersQuerySchema, MEMBERSHIP_STATUS_COLORS, MembershipIdParamSchema, MembershipParamsSchema, MembershipRoleParamsSchema, MembershipRoleSchema, MembershipStatusSchema, MyOrgPermissionsResponseSchema, NormalizedAccessKeySchema, OrgIdParamSchema, OrgRoleParamsSchema, OrgRolesParamsSchema, OrganizationDomainSchema, OrganizationIdParamSchema, OrganizationNameSchema, PERMISSIONS, PERMISSION_CATALOG, PLATFORM_ADMIN_ACCESS_KEY, PLATFORM_ADMIN_ACCESS_KEY_SHORTHAND, SendInvitationSchema, THEME_PRESETS, ThemePresetEnum, UpdateMembershipSchema, UpdateMyProfileSchema, UpdateOrgRoleRequestSchema, UpdateOrganizationSchema, UpdateUserSchema, UserIdParamSchema, accessKeyToString, asSupabaseOrgId, asSupabaseOrgIdOrNull, asWorkOsOrgId, asWorkOsOrgIdOrNull, brandSupabaseOrgId, brandWorkOsOrgId, checkAccess, createAccessModel, deriveAccessKeyCatalog, findAccessCatalogEntry, isPermissionKey, isSupabaseOrgId, isWorkOsOrgId, listAccessKeys, normalizeAccessKey, rolePermissionForAccessKey, transformMembershipToTableRow, transformSupabaseToInvitation, transformSupabaseToMembership };
|
|
652
|
+
export { AcceptInvitationSchema, AccessActionSchema, AccessKeyInputSchema, AccessKeyObjectSchema, AccessKeySchema, AccessKeys, AssignMembershipRoleRequestSchema, CreateMembershipSchema, CreateOrgRoleRequestSchema, CreateOrganizationSchema, DEFAULT_ACCESS_ACTION, DIAGNOSTIC_VIEW_ACCESS_KEYS, ExternalIdParamSchema, InvitationIdParamSchema, InviteRouterQuerySchema, ListInvitationsQuerySchema, ListMembershipsQuerySchema, ListOrganizationsQuerySchema, ListUsersQuerySchema, MEMBERSHIP_STATUS_COLORS, MembershipIdParamSchema, MembershipParamsSchema, MembershipRoleParamsSchema, MembershipRoleSchema, MembershipStatusSchema, MyOrgPermissionsResponseSchema, NormalizedAccessKeySchema, OrgIdParamSchema, OrgRoleParamsSchema, OrgRolesParamsSchema, OrganizationDomainSchema, OrganizationIdParamSchema, OrganizationNameSchema, PERMISSIONS, PERMISSION_CATALOG, PLATFORM_ADMIN_ACCESS_KEY, PLATFORM_ADMIN_ACCESS_KEY_SHORTHAND, SendInvitationSchema, THEME_PRESETS, ThemePresetEnum, UpdateMembershipSchema, UpdateMyProfileSchema, UpdateOrgRoleRequestSchema, UpdateOrganizationAuthConfigSchema, UpdateOrganizationSchema, UpdateUserSchema, UserIdParamSchema, accessKeyToString, asSupabaseOrgId, asSupabaseOrgIdOrNull, asWorkOsOrgId, asWorkOsOrgIdOrNull, brandSupabaseOrgId, brandWorkOsOrgId, checkAccess, createAccessModel, deriveAccessKeyCatalog, findAccessCatalogEntry, isPermissionKey, isSupabaseOrgId, isWorkOsOrgId, listAccessKeys, normalizeAccessKey, rolePermissionForAccessKey, transformMembershipToTableRow, transformSupabaseToInvitation, transformSupabaseToMembership };
|
package/dist/index.js
CHANGED
|
@@ -726,6 +726,8 @@ function childSystemsOf2(system) {
|
|
|
726
726
|
return system.systems ?? system.subsystems ?? {};
|
|
727
727
|
}
|
|
728
728
|
function getSystem(model, path) {
|
|
729
|
+
const flatMatch = model.systems[path];
|
|
730
|
+
if (flatMatch !== void 0) return flatMatch;
|
|
729
731
|
const segments = path.split(".");
|
|
730
732
|
let current = model.systems;
|
|
731
733
|
let node;
|
|
@@ -726,6 +726,8 @@ function childSystemsOf2(system) {
|
|
|
726
726
|
return system.systems ?? system.subsystems ?? {};
|
|
727
727
|
}
|
|
728
728
|
function getSystem(model, path) {
|
|
729
|
+
const flatMatch = model.systems[path];
|
|
730
|
+
if (flatMatch !== void 0) return flatMatch;
|
|
729
731
|
const segments = path.split(".");
|
|
730
732
|
let current = model.systems;
|
|
731
733
|
let node;
|
|
@@ -3126,6 +3126,17 @@ type Database = {
|
|
|
3126
3126
|
Args: never;
|
|
3127
3127
|
Returns: undefined;
|
|
3128
3128
|
};
|
|
3129
|
+
repair_membership_role_assignments: {
|
|
3130
|
+
Args: never;
|
|
3131
|
+
Returns: {
|
|
3132
|
+
membership_id: string;
|
|
3133
|
+
organization_id: string;
|
|
3134
|
+
repaired: boolean;
|
|
3135
|
+
role_id: string;
|
|
3136
|
+
role_slug: string;
|
|
3137
|
+
user_id: string;
|
|
3138
|
+
}[];
|
|
3139
|
+
};
|
|
3129
3140
|
sync_all_memberships_with_role: {
|
|
3130
3141
|
Args: {
|
|
3131
3142
|
p_role_id: string;
|
|
@@ -3138,6 +3149,13 @@ type Database = {
|
|
|
3138
3149
|
};
|
|
3139
3150
|
Returns: undefined;
|
|
3140
3151
|
};
|
|
3152
|
+
update_membership_role_assignment: {
|
|
3153
|
+
Args: {
|
|
3154
|
+
p_role_slug: string;
|
|
3155
|
+
p_workos_membership_id: string;
|
|
3156
|
+
};
|
|
3157
|
+
Returns: string;
|
|
3158
|
+
};
|
|
3141
3159
|
upsert_user_profile: {
|
|
3142
3160
|
Args: never;
|
|
3143
3161
|
Returns: {
|
package/dist/test-utils/index.js
CHANGED
|
@@ -2011,7 +2011,7 @@ var RLSTestContext = class {
|
|
|
2011
2011
|
if (roleErr || !roleDef) {
|
|
2012
2012
|
throw new Error(`Failed to look up system role '${slug}': ${roleErr?.message ?? "not found"}`);
|
|
2013
2013
|
}
|
|
2014
|
-
const { error: assignErr } = await this.adminClient.from("org_rol_assignments").
|
|
2014
|
+
const { error: assignErr } = await this.adminClient.from("org_rol_assignments").upsert({ membership_id: membershipId, role_id: roleDef.id }, { onConflict: "membership_id,role_id" });
|
|
2015
2015
|
if (assignErr) {
|
|
2016
2016
|
throw new Error(`Failed to assign system role '${slug}' to membership: ${assignErr.message}`);
|
|
2017
2017
|
}
|
package/package.json
CHANGED
|
@@ -59,11 +59,25 @@ export const SendInvitationSchema = z
|
|
|
59
59
|
* Security:
|
|
60
60
|
* - Token validated (non-empty string)
|
|
61
61
|
*/
|
|
62
|
-
export const AcceptInvitationSchema = z
|
|
63
|
-
.object({
|
|
64
|
-
invitation_token: z.string().min(1, 'Invitation token is required')
|
|
65
|
-
})
|
|
66
|
-
.strict()
|
|
62
|
+
export const AcceptInvitationSchema = z
|
|
63
|
+
.object({
|
|
64
|
+
invitation_token: z.string().min(1, 'Invitation token is required')
|
|
65
|
+
})
|
|
66
|
+
.strict()
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Public invitation router query
|
|
70
|
+
* GET /api/invite
|
|
71
|
+
*
|
|
72
|
+
* Security:
|
|
73
|
+
* - Token validated as present only; service layer performs WorkOS lookup
|
|
74
|
+
* - Router redirects with only the token needed by tenant AuthKit login
|
|
75
|
+
*/
|
|
76
|
+
export const InviteRouterQuerySchema = z
|
|
77
|
+
.object({
|
|
78
|
+
invitation_token: z.string().min(1, 'Invitation token is required')
|
|
79
|
+
})
|
|
80
|
+
.strict()
|
|
67
81
|
|
|
68
82
|
// ============================================================================
|
|
69
83
|
// Query Parameters
|
|
@@ -98,7 +112,8 @@ export const ListInvitationsQuerySchema = z
|
|
|
98
112
|
// ============================================================================
|
|
99
113
|
|
|
100
114
|
// Export inferred types for use in route handlers
|
|
101
|
-
export type SendInvitationInput = z.infer<typeof SendInvitationSchema>
|
|
102
|
-
export type AcceptInvitationInput = z.infer<typeof AcceptInvitationSchema>
|
|
103
|
-
export type
|
|
104
|
-
export type
|
|
115
|
+
export type SendInvitationInput = z.infer<typeof SendInvitationSchema>
|
|
116
|
+
export type AcceptInvitationInput = z.infer<typeof AcceptInvitationSchema>
|
|
117
|
+
export type InviteRouterQuery = z.infer<typeof InviteRouterQuerySchema>
|
|
118
|
+
export type ListInvitationsQuery = z.infer<typeof ListInvitationsQuerySchema>
|
|
119
|
+
export type InvitationIdParam = z.infer<typeof InvitationIdParamSchema>
|
|
@@ -27,12 +27,14 @@ export { transformSupabaseToInvitation } from './supabase'
|
|
|
27
27
|
|
|
28
28
|
// Validation schemas
|
|
29
29
|
export {
|
|
30
|
-
SendInvitationSchema,
|
|
31
|
-
AcceptInvitationSchema,
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
type
|
|
36
|
-
type
|
|
37
|
-
type
|
|
38
|
-
|
|
30
|
+
SendInvitationSchema,
|
|
31
|
+
AcceptInvitationSchema,
|
|
32
|
+
InviteRouterQuerySchema,
|
|
33
|
+
ListInvitationsQuerySchema,
|
|
34
|
+
InvitationIdParamSchema,
|
|
35
|
+
type SendInvitationInput,
|
|
36
|
+
type AcceptInvitationInput,
|
|
37
|
+
type InviteRouterQuery,
|
|
38
|
+
type ListInvitationsQuery,
|
|
39
|
+
type InvitationIdParam
|
|
40
|
+
} from './api-schemas'
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
CreateOrganizationSchema,
|
|
4
|
+
ListOrganizationsQuerySchema,
|
|
5
|
+
UpdateOrganizationAuthConfigSchema,
|
|
6
|
+
UpdateOrganizationSchema
|
|
7
|
+
} from '../api-schemas'
|
|
3
8
|
|
|
4
9
|
// ---------------------------------------------------------------------------
|
|
5
10
|
// CreateOrganizationSchema
|
|
@@ -87,7 +92,7 @@ describe('CreateOrganizationSchema', () => {
|
|
|
87
92
|
// UpdateOrganizationSchema
|
|
88
93
|
// ---------------------------------------------------------------------------
|
|
89
94
|
|
|
90
|
-
describe('UpdateOrganizationSchema', () => {
|
|
95
|
+
describe('UpdateOrganizationSchema', () => {
|
|
91
96
|
it('rejects an empty object (at least one field required)', () => {
|
|
92
97
|
// UpdateOrganizationSchema is CreateOrganizationSchema.partial().strict().refine(...)
|
|
93
98
|
// The .refine() enforces the documented "at least one field" convention
|
|
@@ -135,7 +140,38 @@ describe('UpdateOrganizationSchema', () => {
|
|
|
135
140
|
it('rejects unknown top-level fields (.strict() mode)', () => {
|
|
136
141
|
expect(UpdateOrganizationSchema.safeParse({ name: 'Valid Name', unknownField: 'bad' }).success).toBe(false)
|
|
137
142
|
})
|
|
138
|
-
})
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
// UpdateOrganizationAuthConfigSchema
|
|
147
|
+
// ---------------------------------------------------------------------------
|
|
148
|
+
|
|
149
|
+
describe('UpdateOrganizationAuthConfigSchema', () => {
|
|
150
|
+
it('accepts an HTTPS app origin', () => {
|
|
151
|
+
expect(UpdateOrganizationAuthConfigSchema.safeParse({ appOrigin: 'https://app.example.com' }).success).toBe(true)
|
|
152
|
+
})
|
|
153
|
+
|
|
154
|
+
it('rejects app origins with paths, query strings, or hashes', () => {
|
|
155
|
+
expect(UpdateOrganizationAuthConfigSchema.safeParse({ appOrigin: 'https://app.example.com/login' }).success).toBe(
|
|
156
|
+
false
|
|
157
|
+
)
|
|
158
|
+
expect(UpdateOrganizationAuthConfigSchema.safeParse({ appOrigin: 'https://app.example.com?x=1' }).success).toBe(
|
|
159
|
+
false
|
|
160
|
+
)
|
|
161
|
+
expect(UpdateOrganizationAuthConfigSchema.safeParse({ appOrigin: 'https://app.example.com#hash' }).success).toBe(
|
|
162
|
+
false
|
|
163
|
+
)
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
it('rejects unknown config fields', () => {
|
|
167
|
+
expect(
|
|
168
|
+
UpdateOrganizationAuthConfigSchema.safeParse({
|
|
169
|
+
appOrigin: 'https://app.example.com',
|
|
170
|
+
other: true
|
|
171
|
+
}).success
|
|
172
|
+
).toBe(false)
|
|
173
|
+
})
|
|
174
|
+
})
|
|
139
175
|
|
|
140
176
|
// ---------------------------------------------------------------------------
|
|
141
177
|
// ListOrganizationsQuerySchema
|
|
@@ -101,11 +101,38 @@ export const CreateOrganizationSchema = z
|
|
|
101
101
|
* - At least one field required (matches documented Update schema convention,
|
|
102
102
|
* see .claude/rules/core-package.md → "api-schemas.ts Pattern")
|
|
103
103
|
*/
|
|
104
|
-
export const UpdateOrganizationSchema = CreateOrganizationSchema.partial()
|
|
105
|
-
.strict()
|
|
106
|
-
.refine((data) => Object.keys(data).length > 0, {
|
|
107
|
-
message: 'At least one field (name, domainData, or metadata) must be provided'
|
|
108
|
-
})
|
|
104
|
+
export const UpdateOrganizationSchema = CreateOrganizationSchema.partial()
|
|
105
|
+
.strict()
|
|
106
|
+
.refine((data) => Object.keys(data).length > 0, {
|
|
107
|
+
message: 'At least one field (name, domainData, or metadata) must be provided'
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Update organization auth config
|
|
112
|
+
* PATCH /organizations/:id/config/auth
|
|
113
|
+
*
|
|
114
|
+
* Security:
|
|
115
|
+
* - Narrow config surface: only appOrigin is accepted
|
|
116
|
+
* - App origin is an origin URL, not a route URL
|
|
117
|
+
* - Strict mode prevents accidental writes to unrelated config keys
|
|
118
|
+
*/
|
|
119
|
+
export const UpdateOrganizationAuthConfigSchema = z
|
|
120
|
+
.object({
|
|
121
|
+
appOrigin: z
|
|
122
|
+
.string()
|
|
123
|
+
.trim()
|
|
124
|
+
.url('App origin must be a valid URL')
|
|
125
|
+
.max(2048, 'App origin must be at most 2048 characters')
|
|
126
|
+
.refine((value) => {
|
|
127
|
+
try {
|
|
128
|
+
const url = new URL(value)
|
|
129
|
+
return url.pathname === '/' && url.search === '' && url.hash === ''
|
|
130
|
+
} catch {
|
|
131
|
+
return false
|
|
132
|
+
}
|
|
133
|
+
}, 'App origin must not include a path, query string, or hash')
|
|
134
|
+
})
|
|
135
|
+
.strict()
|
|
109
136
|
|
|
110
137
|
// ============================================================================
|
|
111
138
|
// Query Parameters
|
|
@@ -131,6 +158,7 @@ export const ListOrganizationsQuerySchema = z.object({
|
|
|
131
158
|
|
|
132
159
|
// Export inferred types for use in route handlers
|
|
133
160
|
export type CreateOrganizationInput = z.infer<typeof CreateOrganizationSchema>
|
|
134
|
-
export type UpdateOrganizationInput = z.infer<typeof UpdateOrganizationSchema>
|
|
135
|
-
export type
|
|
136
|
-
export type
|
|
161
|
+
export type UpdateOrganizationInput = z.infer<typeof UpdateOrganizationSchema>
|
|
162
|
+
export type UpdateOrganizationAuthConfigInput = z.infer<typeof UpdateOrganizationAuthConfigSchema>
|
|
163
|
+
export type ListOrganizationsQuery = z.infer<typeof ListOrganizationsQuerySchema>
|
|
164
|
+
export type OrganizationIdParam = z.infer<typeof OrganizationIdParamSchema>
|
|
@@ -8,16 +8,18 @@ export type { Organization, OrganizationSummary } from './organization'
|
|
|
8
8
|
|
|
9
9
|
// Validation schemas
|
|
10
10
|
export {
|
|
11
|
-
CreateOrganizationSchema,
|
|
12
|
-
UpdateOrganizationSchema,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
type
|
|
19
|
-
type
|
|
20
|
-
type
|
|
21
|
-
|
|
11
|
+
CreateOrganizationSchema,
|
|
12
|
+
UpdateOrganizationSchema,
|
|
13
|
+
UpdateOrganizationAuthConfigSchema,
|
|
14
|
+
ListOrganizationsQuerySchema,
|
|
15
|
+
OrganizationIdParamSchema,
|
|
16
|
+
OrganizationNameSchema,
|
|
17
|
+
OrganizationDomainSchema,
|
|
18
|
+
type CreateOrganizationInput,
|
|
19
|
+
type UpdateOrganizationInput,
|
|
20
|
+
type UpdateOrganizationAuthConfigInput,
|
|
21
|
+
type ListOrganizationsQuery,
|
|
22
|
+
type OrganizationIdParam
|
|
23
|
+
} from './api-schemas'
|
|
22
24
|
|
|
23
25
|
// Note: WorkOS types and transforms available via @repo/core/server
|
|
@@ -170,6 +170,40 @@ describe('createBuildPlanSnapshotFromTemplate', () => {
|
|
|
170
170
|
]
|
|
171
171
|
})
|
|
172
172
|
})
|
|
173
|
+
|
|
174
|
+
it('normalizes stage-key dependencies to step ids for snapshots', () => {
|
|
175
|
+
const snapshot = createBuildPlanSnapshotFromTemplate({
|
|
176
|
+
id: 'stage-key-dependency',
|
|
177
|
+
label: 'Stage Key Dependency',
|
|
178
|
+
steps: [
|
|
179
|
+
{
|
|
180
|
+
id: 'source-companies',
|
|
181
|
+
label: 'Companies found',
|
|
182
|
+
primaryEntity: 'company',
|
|
183
|
+
outputs: ['company'],
|
|
184
|
+
stageKey: 'populated',
|
|
185
|
+
dependencyMode: 'per-record-eligibility',
|
|
186
|
+
actionKey: 'lead-gen.company.source',
|
|
187
|
+
defaultBatchSize: 100,
|
|
188
|
+
maxBatchSize: 250
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
id: 'qualify-companies',
|
|
192
|
+
label: 'Companies qualified',
|
|
193
|
+
primaryEntity: 'company',
|
|
194
|
+
outputs: ['company'],
|
|
195
|
+
stageKey: 'qualified',
|
|
196
|
+
dependsOn: ['populated'],
|
|
197
|
+
dependencyMode: 'per-record-eligibility',
|
|
198
|
+
actionKey: 'lead-gen.company.qualify',
|
|
199
|
+
defaultBatchSize: 50,
|
|
200
|
+
maxBatchSize: 100
|
|
201
|
+
}
|
|
202
|
+
]
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
expect(snapshot.steps[1]?.dependsOn).toEqual(['source-companies'])
|
|
206
|
+
})
|
|
173
207
|
})
|
|
174
208
|
|
|
175
209
|
describe('createBuildPlanSnapshotFromTemplateId', () => {
|
|
@@ -30,6 +30,9 @@ export function isProspectingBuildTemplateId(
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export function createBuildPlanSnapshotFromTemplate(template: ProspectingBuildTemplate): BuildPlanSnapshot {
|
|
33
|
+
const stepIdByStageKey = new Map(template.steps.map((step) => [step.stageKey, step.id]))
|
|
34
|
+
const stepIds = new Set(template.steps.map((step) => step.id))
|
|
35
|
+
|
|
33
36
|
return {
|
|
34
37
|
templateId: template.id,
|
|
35
38
|
templateLabel: template.label,
|
|
@@ -50,7 +53,11 @@ export function createBuildPlanSnapshotFromTemplate(template: ProspectingBuildTe
|
|
|
50
53
|
|
|
51
54
|
if (step.description) snapshotStep.description = step.description
|
|
52
55
|
if (step.recordEntity) snapshotStep.recordEntity = step.recordEntity
|
|
53
|
-
if (step.dependsOn?.length)
|
|
56
|
+
if (step.dependsOn?.length) {
|
|
57
|
+
snapshotStep.dependsOn = step.dependsOn.map((dependency) =>
|
|
58
|
+
stepIds.has(dependency) ? dependency : (stepIdByStageKey.get(dependency) ?? dependency)
|
|
59
|
+
)
|
|
60
|
+
}
|
|
54
61
|
if (step.credentialRequirements?.length) {
|
|
55
62
|
snapshotStep.credentialRequirements = step.credentialRequirements.map((requirement: CredentialRequirement) => ({
|
|
56
63
|
...requirement
|
|
@@ -64,6 +64,23 @@ describe('getSystem', () => {
|
|
|
64
64
|
expect(sys?.label).toBe('Leaf')
|
|
65
65
|
})
|
|
66
66
|
|
|
67
|
+
it('returns a flat system whose id contains dots', () => {
|
|
68
|
+
const model = resolveOrganizationModel({
|
|
69
|
+
systems: {
|
|
70
|
+
sales: { id: 'sales', order: 10, label: 'Sales', enabled: true, path: '/sales' },
|
|
71
|
+
'sales.lead-gen': {
|
|
72
|
+
id: 'sales.lead-gen',
|
|
73
|
+
order: 20,
|
|
74
|
+
label: 'Lead Gen',
|
|
75
|
+
enabled: true,
|
|
76
|
+
path: '/lead-gen'
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
expect(getSystem(model, 'sales.lead-gen')?.label).toBe('Lead Gen')
|
|
82
|
+
})
|
|
83
|
+
|
|
67
84
|
it('returns undefined for unresolved paths', () => {
|
|
68
85
|
const model = makeNestedModel()
|
|
69
86
|
expect(getSystem(model, 'nonexistent')).toBeUndefined()
|
|
@@ -90,6 +107,23 @@ describe('getSystemAncestors', () => {
|
|
|
90
107
|
])
|
|
91
108
|
})
|
|
92
109
|
|
|
110
|
+
it('returns available ancestors for flat dotted system ids', () => {
|
|
111
|
+
const model = resolveOrganizationModel({
|
|
112
|
+
systems: {
|
|
113
|
+
sales: { id: 'sales', order: 10, label: 'Sales', enabled: true, path: '/sales' },
|
|
114
|
+
'sales.lead-gen': {
|
|
115
|
+
id: 'sales.lead-gen',
|
|
116
|
+
order: 20,
|
|
117
|
+
label: 'Lead Gen',
|
|
118
|
+
enabled: true,
|
|
119
|
+
path: '/lead-gen'
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
expect(getSystemAncestors(model, 'sales.lead-gen').map((system) => system.label)).toEqual(['Sales', 'Lead Gen'])
|
|
125
|
+
})
|
|
126
|
+
|
|
93
127
|
it('returns empty arrays for unresolved paths', () => {
|
|
94
128
|
const model = makeNestedModel()
|
|
95
129
|
expect(getSystemAncestors(model, 'does.not.exist')).toEqual([])
|
|
@@ -114,17 +114,6 @@ function modelWithBuildTemplate(): OrganizationModel {
|
|
|
114
114
|
kind: 'template-step',
|
|
115
115
|
appliesTo: 'sales.lead-gen:object/list',
|
|
116
116
|
entries: {
|
|
117
|
-
'source-companies': {
|
|
118
|
-
label: 'Source Companies',
|
|
119
|
-
order: 10,
|
|
120
|
-
primaryEntity: 'company',
|
|
121
|
-
outputs: ['company'],
|
|
122
|
-
stageKey: 'populated',
|
|
123
|
-
dependencyMode: 'per-record-eligibility',
|
|
124
|
-
actionKey: 'lead-gen.company.source',
|
|
125
|
-
defaultBatchSize: 100,
|
|
126
|
-
maxBatchSize: 500
|
|
127
|
-
},
|
|
128
117
|
'qualify-companies': {
|
|
129
118
|
label: 'Qualify Companies',
|
|
130
119
|
order: 20,
|
|
@@ -136,6 +125,17 @@ function modelWithBuildTemplate(): OrganizationModel {
|
|
|
136
125
|
actionKey: 'lead-gen.company.qualify',
|
|
137
126
|
defaultBatchSize: 100,
|
|
138
127
|
maxBatchSize: 500
|
|
128
|
+
},
|
|
129
|
+
'source-companies': {
|
|
130
|
+
label: 'Source Companies',
|
|
131
|
+
order: 10,
|
|
132
|
+
primaryEntity: 'company',
|
|
133
|
+
outputs: ['company'],
|
|
134
|
+
stageKey: 'populated',
|
|
135
|
+
dependencyMode: 'per-record-eligibility',
|
|
136
|
+
actionKey: 'lead-gen.company.source',
|
|
137
|
+
defaultBatchSize: 100,
|
|
138
|
+
maxBatchSize: 500
|
|
139
139
|
}
|
|
140
140
|
}
|
|
141
141
|
}
|
|
@@ -149,6 +149,9 @@ export function devOnlyFor(systems: Record<string, OrganizationModelSystemEntry>
|
|
|
149
149
|
* Returns `undefined` if any segment is missing.
|
|
150
150
|
*/
|
|
151
151
|
export function getSystem(model: OrganizationModel, path: string): SystemEntryWithTree | undefined {
|
|
152
|
+
const flatMatch = (model.systems as Record<string, SystemEntryWithTree>)[path]
|
|
153
|
+
if (flatMatch !== undefined) return flatMatch
|
|
154
|
+
|
|
152
155
|
const segments = path.split('.')
|
|
153
156
|
// model.systems is typed as Record<string, OrganizationModelSystemEntry>; cast
|
|
154
157
|
// to SystemEntryWithTree (which extends it with optional content + subsystems).
|
|
@@ -171,6 +174,21 @@ export function getSystem(model: OrganizationModel, path: string): SystemEntryWi
|
|
|
171
174
|
* Returns an empty array if the path cannot be resolved.
|
|
172
175
|
*/
|
|
173
176
|
export function getSystemAncestors(model: OrganizationModel, path: string): SystemEntryWithTree[] {
|
|
177
|
+
const flatMatch = (model.systems as Record<string, SystemEntryWithTree>)[path]
|
|
178
|
+
if (flatMatch !== undefined) {
|
|
179
|
+
const ancestorIds = path
|
|
180
|
+
.split('.')
|
|
181
|
+
.map((_, index, segments) => segments.slice(0, index + 1).join('.'))
|
|
182
|
+
.slice(0, -1)
|
|
183
|
+
|
|
184
|
+
return [
|
|
185
|
+
...ancestorIds
|
|
186
|
+
.map((ancestorId) => (model.systems as Record<string, SystemEntryWithTree>)[ancestorId])
|
|
187
|
+
.filter((system): system is SystemEntryWithTree => system !== undefined),
|
|
188
|
+
flatMatch
|
|
189
|
+
]
|
|
190
|
+
}
|
|
191
|
+
|
|
174
192
|
const segments = path.split('.')
|
|
175
193
|
const result: SystemEntryWithTree[] = []
|
|
176
194
|
let current: Record<string, SystemEntryWithTree> = model.systems as Record<string, SystemEntryWithTree>
|
|
@@ -124,7 +124,14 @@ export function getAllBuildTemplates(model: OrganizationModel): BuildTemplate[]
|
|
|
124
124
|
entriesOf(catalog).map(([templateId, templateEntry]) => {
|
|
125
125
|
const stepCatalogId = stringValue(templateEntry.stepCatalog) as OntologyId | undefined
|
|
126
126
|
const stepCatalog = stepCatalogId !== undefined ? stepCatalogs.get(stepCatalogId) : undefined
|
|
127
|
-
const steps =
|
|
127
|
+
const steps =
|
|
128
|
+
stepCatalog === undefined
|
|
129
|
+
? []
|
|
130
|
+
: entriesOf(stepCatalog).sort(
|
|
131
|
+
([leftId, left], [rightId, right]) =>
|
|
132
|
+
numberValue(left.order, Number.MAX_SAFE_INTEGER) -
|
|
133
|
+
numberValue(right.order, Number.MAX_SAFE_INTEGER) || leftId.localeCompare(rightId)
|
|
134
|
+
)
|
|
128
135
|
|
|
129
136
|
return {
|
|
130
137
|
order: numberValue(templateEntry.order, Number.MAX_SAFE_INTEGER),
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export const VERSION = {
|
|
2
|
-
CURRENT: '1.12.
|
|
3
|
-
}
|
|
1
|
+
export const VERSION = {
|
|
2
|
+
CURRENT: '1.12.12'
|
|
3
|
+
}
|