@elevasis/core 0.35.1 → 0.37.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 +124 -2
- package/dist/auth/index.js +14 -1
- package/dist/index.d.ts +334 -3
- package/dist/index.js +21 -5
- package/dist/knowledge/index.d.ts +81 -0
- package/dist/organization-model/index.d.ts +334 -3
- package/dist/organization-model/index.js +21 -5
- package/dist/test-utils/index.d.ts +97 -0
- package/dist/test-utils/index.js +19 -4
- package/package.json +1 -1
- package/src/_gen/__tests__/__snapshots__/contracts.md.snap +67 -0
- 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__/domains/navigation-topbar.test.ts +282 -0
- package/src/organization-model/__tests__/migration-helpers.test.ts +11 -11
- package/src/organization-model/defaults.ts +2 -1
- package/src/organization-model/domains/navigation.ts +176 -139
- package/src/organization-model/icons.ts +1 -0
- package/src/organization-model/migration-helpers.ts +8 -1
- package/src/organization-model/published.ts +6 -6
- package/src/organization-model/types.ts +5 -1
- package/src/platform/constants/versions.ts +1 -1
- package/src/platform/registry/__tests__/resource-registry.test.ts +2053 -2054
- package/src/reference/_generated/contracts.md +67 -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/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
|
}
|
|
@@ -19817,6 +19817,7 @@ var ORGANIZATION_MODEL_ICON_TOKENS = [
|
|
|
19817
19817
|
"view",
|
|
19818
19818
|
"launch",
|
|
19819
19819
|
"message",
|
|
19820
|
+
"message-plus",
|
|
19820
19821
|
"escalate",
|
|
19821
19822
|
"promote",
|
|
19822
19823
|
"submit",
|
|
@@ -20001,9 +20002,22 @@ var SidebarNavigationSchema = z.object({
|
|
|
20001
20002
|
primary: SidebarSectionSchema,
|
|
20002
20003
|
bottom: SidebarSectionSchema
|
|
20003
20004
|
}).default({ primary: {}, bottom: {} });
|
|
20005
|
+
var TopbarActionNodeSchema = z.object({
|
|
20006
|
+
id: ModelIdSchema,
|
|
20007
|
+
label: LabelSchema,
|
|
20008
|
+
tooltip: DescriptionSchema.optional(),
|
|
20009
|
+
icon: IconNameSchema.optional(),
|
|
20010
|
+
order: z.number().int().optional(),
|
|
20011
|
+
enabled: z.boolean().default(true),
|
|
20012
|
+
devOnly: z.boolean().optional(),
|
|
20013
|
+
requiresAdmin: z.boolean().optional(),
|
|
20014
|
+
targets: SidebarSurfaceTargetsSchema.optional()
|
|
20015
|
+
});
|
|
20016
|
+
var TopbarSectionSchema = z.record(z.string(), TopbarActionNodeSchema).default({});
|
|
20004
20017
|
var OrganizationModelNavigationSchema = z.object({
|
|
20005
|
-
sidebar: SidebarNavigationSchema
|
|
20006
|
-
|
|
20018
|
+
sidebar: SidebarNavigationSchema,
|
|
20019
|
+
topbar: TopbarSectionSchema
|
|
20020
|
+
}).default({ sidebar: { primary: {}, bottom: {} }, topbar: {} });
|
|
20007
20021
|
z.object({
|
|
20008
20022
|
id: ModelIdSchema,
|
|
20009
20023
|
label: LabelSchema,
|
|
@@ -21608,7 +21622,8 @@ var DEFAULT_ORGANIZATION_MODEL_NAVIGATION = {
|
|
|
21608
21622
|
sidebar: {
|
|
21609
21623
|
primary: {},
|
|
21610
21624
|
bottom: {}
|
|
21611
|
-
}
|
|
21625
|
+
},
|
|
21626
|
+
topbar: {}
|
|
21612
21627
|
};
|
|
21613
21628
|
var DEFAULT_ORGANIZATION_MODEL = {
|
|
21614
21629
|
version: 1,
|
package/package.json
CHANGED
|
@@ -135,6 +135,18 @@ export type OrganizationModelSidebarSurfaceNode = Extract<OrganizationModelSideb
|
|
|
135
135
|
export type OrganizationModelSidebarGroupNode = Extract<OrganizationModelSidebarNode, { type: 'group' }>
|
|
136
136
|
```
|
|
137
137
|
|
|
138
|
+
### `OrganizationModelTopbarActionNode`
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
export type OrganizationModelTopbarActionNode = z.infer<typeof TopbarActionNodeSchema>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
### `OrganizationModelTopbarSection`
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
export type OrganizationModelTopbarSection = z.infer<typeof TopbarSectionSchema>
|
|
148
|
+
```
|
|
149
|
+
|
|
138
150
|
### `OrganizationModelTechStackEntry`
|
|
139
151
|
|
|
140
152
|
```typescript
|
|
@@ -845,6 +857,57 @@ export interface ShellRuntime {
|
|
|
845
857
|
}
|
|
846
858
|
```
|
|
847
859
|
|
|
860
|
+
### `ResolvedTopbarAction`
|
|
861
|
+
|
|
862
|
+
```typescript
|
|
863
|
+
/**
|
|
864
|
+
* A resolved topbar action — the OM node after gating/filtering, with icon resolved
|
|
865
|
+
* from the semantic icon registry. Passed to `TopbarActionModule.render`.
|
|
866
|
+
*/
|
|
867
|
+
export interface ResolvedTopbarAction {
|
|
868
|
+
id: string
|
|
869
|
+
label: string
|
|
870
|
+
tooltip?: string
|
|
871
|
+
icon: TablerIconComponent
|
|
872
|
+
order: number
|
|
873
|
+
}
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
### `TopbarActionModule`
|
|
877
|
+
|
|
878
|
+
```typescript
|
|
879
|
+
/**
|
|
880
|
+
* A topbar action module — binds a registry key to UI behavior (a render callback).
|
|
881
|
+
* The key must match `navigation.topbar[id]`. The OM supplies data + visibility;
|
|
882
|
+
* the module supplies behavior.
|
|
883
|
+
*/
|
|
884
|
+
export interface TopbarActionModule {
|
|
885
|
+
/** Stable key that matches the `id` of a `navigation.topbar` node. */
|
|
886
|
+
key: string
|
|
887
|
+
/** Render the topbar action. Receives the resolved OM node (icon pre-resolved). */
|
|
888
|
+
render: (ctx: { node: ResolvedTopbarAction }) => ReactNode
|
|
889
|
+
}
|
|
890
|
+
```
|
|
891
|
+
|
|
892
|
+
### `ResolvedTopbarActionEntry`
|
|
893
|
+
|
|
894
|
+
```typescript
|
|
895
|
+
/** A joined entry emitted by `getTopbarActions`: resolved OM node + module behavior. */
|
|
896
|
+
export interface ResolvedTopbarActionEntry {
|
|
897
|
+
node: ResolvedTopbarAction
|
|
898
|
+
render: TopbarActionModule['render']
|
|
899
|
+
}
|
|
900
|
+
```
|
|
901
|
+
|
|
902
|
+
### `TopbarActionsProjectionOptions`
|
|
903
|
+
|
|
904
|
+
```typescript
|
|
905
|
+
export interface TopbarActionsProjectionOptions {
|
|
906
|
+
isPlatformAdmin?: boolean
|
|
907
|
+
isDev?: boolean
|
|
908
|
+
}
|
|
909
|
+
```
|
|
910
|
+
|
|
848
911
|
### `OrganizationGraphSystemBridge`
|
|
849
912
|
|
|
850
913
|
```typescript
|
|
@@ -868,6 +931,8 @@ export interface OrganizationGraphContextValue {
|
|
|
868
931
|
```typescript
|
|
869
932
|
export interface ElevasisSystemsProviderProps {
|
|
870
933
|
systems?: SystemModule[]
|
|
934
|
+
/** Registered topbar action modules. OM node presence controls visibility; module supplies behavior. */
|
|
935
|
+
topbarActions?: TopbarActionModule[]
|
|
871
936
|
organizationModel?: ElevasisOrganizationModel
|
|
872
937
|
timeRange?: TimeRange
|
|
873
938
|
operationsApiUrl?: string
|
|
@@ -886,6 +951,8 @@ export interface ElevasisSystemsContextValue {
|
|
|
886
951
|
shellModel: ResolvedShellModel
|
|
887
952
|
shellRuntime: ShellRuntime
|
|
888
953
|
getSidebarLinks: (options?: ShellSidebarProjectionOptions) => ShellSidebarLinkGroup[]
|
|
954
|
+
/** Returns the list of topbar actions visible to the current user, in order. */
|
|
955
|
+
getTopbarActions: (options?: TopbarActionsProjectionOptions) => ResolvedTopbarActionEntry[]
|
|
889
956
|
enabledResolvedSystems: ResolvedSystemModule[]
|
|
890
957
|
resolvedSystems: ResolvedSystemModule[]
|
|
891
958
|
organizationGraph: OrganizationGraphContextValue
|
|
@@ -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
|