@dotbep/core 0.2.7 → 0.2.8
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/index.d.ts +7 -5
- package/dist/index.js +604 -596
- package/package.json +4 -1
- package/examples/01-participants.ts +0 -127
- package/examples/02-files.ts +0 -100
- package/examples/03-workflows.ts +0 -149
- package/examples/04-bim-uses.ts +0 -70
- package/examples/05-standards.ts +0 -60
- package/examples/06-schedule.ts +0 -124
- package/examples/07-loin.ts +0 -133
- package/examples/08-deliverables.ts +0 -126
- package/examples/09-notes.ts +0 -73
- package/examples/10-llm.ts +0 -109
- package/examples/11-resolved.ts +0 -133
- package/examples/12-history.ts +0 -166
- package/examples/13-engine.ts +0 -152
- package/examples/bep.d.ts +0 -38
- package/examples/example.bep +0 -0
- package/examples/run-all.ts +0 -38
- package/src/base/entity.ts +0 -148
- package/src/base/history.ts +0 -497
- package/src/base/index.ts +0 -5
- package/src/base/singleton.ts +0 -26
- package/src/entities/actions.ts +0 -25
- package/src/entities/adapters.ts +0 -16
- package/src/entities/annexes.ts +0 -17
- package/src/entities/assetTypes.ts +0 -30
- package/src/entities/automations.ts +0 -24
- package/src/entities/bimUses.ts +0 -50
- package/src/entities/deliverables.ts +0 -66
- package/src/entities/disciplines.ts +0 -21
- package/src/entities/effects.ts +0 -28
- package/src/entities/env.ts +0 -17
- package/src/entities/events.ts +0 -24
- package/src/entities/extensions.ts +0 -16
- package/src/entities/flags.ts +0 -17
- package/src/entities/guides.ts +0 -26
- package/src/entities/index.ts +0 -32
- package/src/entities/lbsNodes.ts +0 -193
- package/src/entities/lods.ts +0 -22
- package/src/entities/loin.ts +0 -127
- package/src/entities/lois.ts +0 -22
- package/src/entities/members.ts +0 -137
- package/src/entities/milestones.ts +0 -32
- package/src/entities/notes.ts +0 -27
- package/src/entities/objectives.ts +0 -17
- package/src/entities/phases.ts +0 -17
- package/src/entities/remoteData.ts +0 -17
- package/src/entities/resolvers.ts +0 -20
- package/src/entities/roles.ts +0 -29
- package/src/entities/softwares.ts +0 -26
- package/src/entities/standards.ts +0 -68
- package/src/entities/teams.ts +0 -42
- package/src/entities/workflows.ts +0 -256
- package/src/index.ts +0 -464
- package/src/runtime/Engine.ts +0 -352
- package/src/runtime/MemoryStorage.ts +0 -31
- package/src/runtime/Runtime.ts +0 -106
- package/src/runtime/index.ts +0 -4
- package/src/runtime/transitions.ts +0 -456
- package/src/runtime/types.ts +0 -279
- package/src/types/history.ts +0 -37
- package/src/types/index.ts +0 -24
- package/src/types/resolved.ts +0 -137
- package/src/types/schema.ts +0 -757
- package/src/utils/diff.ts +0 -109
- package/src/utils/index.ts +0 -9
- package/src/utils/integrity.ts +0 -108
- package/src/utils/lbs.ts +0 -116
- package/src/utils/mermaid.ts +0 -110
- package/src/utils/naming.ts +0 -62
- package/src/utils/nomenclature.ts +0 -107
- package/src/utils/normalize.ts +0 -35
- package/src/utils/raci.ts +0 -25
- package/src/utils/textFile.ts +0 -24
- package/tsconfig.json +0 -12
- package/vite.config.ts +0 -24
package/src/types/schema.ts
DELETED
|
@@ -1,757 +0,0 @@
|
|
|
1
|
-
import { z } from 'zod'
|
|
2
|
-
|
|
3
|
-
// ─── Primitives ───────────────────────────────────────────────────────────────
|
|
4
|
-
|
|
5
|
-
export const ISORole = z.enum([
|
|
6
|
-
'appointing-party',
|
|
7
|
-
'lead-appointed-party',
|
|
8
|
-
'appointed-party',
|
|
9
|
-
])
|
|
10
|
-
export type ISORole = z.infer<typeof ISORole>
|
|
11
|
-
|
|
12
|
-
export const AnnexType = z.enum(['video', 'document'])
|
|
13
|
-
export type AnnexType = z.infer<typeof AnnexType>
|
|
14
|
-
|
|
15
|
-
export const LBSNodeType = z.enum(['zone', 'location'])
|
|
16
|
-
export type LBSNodeType = z.infer<typeof LBSNodeType>
|
|
17
|
-
|
|
18
|
-
export const FlowDirection = z.enum(['LR', 'TB'])
|
|
19
|
-
export type FlowDirection = z.infer<typeof FlowDirection>
|
|
20
|
-
|
|
21
|
-
export const NodeType = z.enum(['start', 'end', 'process', 'decision', 'automation'])
|
|
22
|
-
export type NodeType = z.infer<typeof NodeType>
|
|
23
|
-
|
|
24
|
-
export const FlagSeverity = z.enum(['info', 'warning', 'blocking'])
|
|
25
|
-
export type FlagSeverity = z.infer<typeof FlagSeverity>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
export const FlagEntityType = z.enum([
|
|
29
|
-
'roles', 'members', 'teams',
|
|
30
|
-
'phases', 'milestones', 'lbs',
|
|
31
|
-
'disciplines', 'extensions', 'assetTypes', 'softwares',
|
|
32
|
-
'objectives', 'bimUses', 'actions', 'workflows',
|
|
33
|
-
'guides', 'annexes', 'standards',
|
|
34
|
-
'lods', 'lois', 'loin',
|
|
35
|
-
'deliverables',
|
|
36
|
-
])
|
|
37
|
-
export type FlagEntityType = z.infer<typeof FlagEntityType>
|
|
38
|
-
|
|
39
|
-
// ─── Project ──────────────────────────────────────────────────────────────────
|
|
40
|
-
|
|
41
|
-
export const ProjectSchema = z.object({
|
|
42
|
-
name: z.string().min(1),
|
|
43
|
-
code: z.string().min(1)
|
|
44
|
-
.describe('Must comply with the naming convention token pattern.'),
|
|
45
|
-
clientId: z.string()
|
|
46
|
-
.describe('ref Team.id').optional(),
|
|
47
|
-
description: z.string().optional(),
|
|
48
|
-
image: z.string().optional(),
|
|
49
|
-
websiteUrl: z.url().optional(),
|
|
50
|
-
}).describe('General metadata about the construction project the BEP belongs to.')
|
|
51
|
-
|
|
52
|
-
export type Project = z.infer<typeof ProjectSchema>
|
|
53
|
-
|
|
54
|
-
// ─── Participants ─────────────────────────────────────────────────────────────
|
|
55
|
-
|
|
56
|
-
export const RoleSchema = z.object({
|
|
57
|
-
id: z.uuid(),
|
|
58
|
-
name: z.string().min(1),
|
|
59
|
-
description: z.string().optional(),
|
|
60
|
-
color: z.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
|
|
61
|
-
}).describe('Defines a stakeholder role in the project. Used to assign process responsibilities in workflows and to resolve who must act at each workflow step.')
|
|
62
|
-
|
|
63
|
-
export type Role = z.infer<typeof RoleSchema>
|
|
64
|
-
|
|
65
|
-
export const MemberSchema = z.object({
|
|
66
|
-
email: z.email(),
|
|
67
|
-
name: z.string().min(1),
|
|
68
|
-
roleId: z.string(),
|
|
69
|
-
description: z.string().optional(),
|
|
70
|
-
bepEditor: z.boolean().optional(),
|
|
71
|
-
}).describe('A project participant identified by email. Each member holds one role, which determines their responsibilities across workflow steps.')
|
|
72
|
-
|
|
73
|
-
export type Member = z.infer<typeof MemberSchema>
|
|
74
|
-
|
|
75
|
-
export const TeamBaseSchema = z.object({
|
|
76
|
-
id: z.string().min(1)
|
|
77
|
-
.describe('Must comply with the naming convention token pattern.'),
|
|
78
|
-
name: z.string().min(1),
|
|
79
|
-
isoRole: ISORole,
|
|
80
|
-
description: z.string().optional(),
|
|
81
|
-
disciplineIds: z.array(z.string()).optional(),
|
|
82
|
-
representativeEmail: z.email().optional()
|
|
83
|
-
.describe('ref Member.email'),
|
|
84
|
-
memberEmails: z.array(z.email()).optional(),
|
|
85
|
-
}).describe('A company or discipline group participating in the project. Teams group members under an ISO role and can be assigned RACI responsibilities at the workflow level.')
|
|
86
|
-
|
|
87
|
-
export const TeamSchema = TeamBaseSchema
|
|
88
|
-
.refine(t => !t.representativeEmail || (t.memberEmails ?? []).includes(t.representativeEmail), {
|
|
89
|
-
message: 'representativeEmail must be included in memberEmails.',
|
|
90
|
-
path: ['representativeEmail'],
|
|
91
|
-
})
|
|
92
|
-
|
|
93
|
-
export type Team = z.infer<typeof TeamSchema>
|
|
94
|
-
|
|
95
|
-
// ─── Naming convention ────────────────────────────────────────────────────────
|
|
96
|
-
|
|
97
|
-
export const NamingTokenSchema = z.enum([
|
|
98
|
-
'project',
|
|
99
|
-
'team',
|
|
100
|
-
'discipline',
|
|
101
|
-
'assetType',
|
|
102
|
-
'lbsZone',
|
|
103
|
-
'lbsLocation',
|
|
104
|
-
])
|
|
105
|
-
|
|
106
|
-
export type NamingToken = z.infer<typeof NamingTokenSchema>
|
|
107
|
-
|
|
108
|
-
export const NamingSegmentSchema = z.discriminatedUnion('type', [
|
|
109
|
-
z.object({ type: z.literal('field'), token: NamingTokenSchema, pattern: z.string().optional() }),
|
|
110
|
-
z.object({ type: z.literal('sequence'), padding: z.number().int().min(1).optional() }),
|
|
111
|
-
])
|
|
112
|
-
|
|
113
|
-
export type NamingSegment = z.infer<typeof NamingSegmentSchema>
|
|
114
|
-
|
|
115
|
-
export const NamingConventionSchema = z.object({
|
|
116
|
-
delimiter: z.string().min(1),
|
|
117
|
-
segments: z.array(NamingSegmentSchema).min(1),
|
|
118
|
-
}).describe('A structured rule for generating deliverable names. Specifies a delimiter and a sequence of segments that are joined to produce consistent, parseable file names.')
|
|
119
|
-
.refine(c => c.segments.filter(s => s.type === 'sequence').length <= 1, {
|
|
120
|
-
message: 'segments can contain at most one sequence.',
|
|
121
|
-
path: ['segments'],
|
|
122
|
-
})
|
|
123
|
-
|
|
124
|
-
export type NamingConvention = z.infer<typeof NamingConventionSchema>
|
|
125
|
-
|
|
126
|
-
// ─── Project Context ──────────────────────────────────────────────────────────
|
|
127
|
-
|
|
128
|
-
export const PhaseSchema = z.object({
|
|
129
|
-
id: z.string().min(1),
|
|
130
|
-
name: z.string().min(1),
|
|
131
|
-
description: z.string().optional(),
|
|
132
|
-
}).describe('A project lifecycle stage used to organize milestones and deliverables.')
|
|
133
|
-
|
|
134
|
-
export type Phase = z.infer<typeof PhaseSchema>
|
|
135
|
-
|
|
136
|
-
export const MilestoneSchema = z.object({
|
|
137
|
-
id: z.string().min(1),
|
|
138
|
-
name: z.string().min(1),
|
|
139
|
-
date: z.iso.date(),
|
|
140
|
-
phaseId: z.string(),
|
|
141
|
-
description: z.string().optional(),
|
|
142
|
-
}).describe('A named deadline within a phase. Anchors deliverables and information requirements to a specific date in the project timeline.')
|
|
143
|
-
|
|
144
|
-
export type Milestone = z.infer<typeof MilestoneSchema>
|
|
145
|
-
|
|
146
|
-
export const LBSNodeBaseSchema = z.object({
|
|
147
|
-
id: z.string().min(1)
|
|
148
|
-
.describe('Used in deliverables nomenclature.'),
|
|
149
|
-
name: z.string().min(1),
|
|
150
|
-
type: LBSNodeType,
|
|
151
|
-
description: z.string().optional(),
|
|
152
|
-
lbsNodeIds: z.array(z.string()).optional()
|
|
153
|
-
.describe('ref LBSNode.id[]'),
|
|
154
|
-
}).describe('A spatial or functional subdivision of the project. LBS nodes form a hierarchy that scopes deliverables to specific spatial or functional areas.')
|
|
155
|
-
|
|
156
|
-
export const LBSNodeSchema = LBSNodeBaseSchema
|
|
157
|
-
.refine(n => !(n.lbsNodeIds ?? []).includes(n.id), {
|
|
158
|
-
message: 'A node cannot reference itself in lbsNodeIds.',
|
|
159
|
-
path: ['lbsNodeIds'],
|
|
160
|
-
})
|
|
161
|
-
|
|
162
|
-
export type LBSNode = z.infer<typeof LBSNodeSchema>
|
|
163
|
-
|
|
164
|
-
export const DisciplineSchema = z.object({
|
|
165
|
-
id: z.string().min(1)
|
|
166
|
-
.describe('Used in deliverables nomenclature.'),
|
|
167
|
-
name: z.string().min(1),
|
|
168
|
-
}).describe('A project discipline used to classify deliverables and information requirements. Represents a technical domain such as structure, architecture, or MEP.')
|
|
169
|
-
|
|
170
|
-
export type Discipline = z.infer<typeof DisciplineSchema>
|
|
171
|
-
|
|
172
|
-
// ─── Files ────────────────────────────────────────────────────────────────────
|
|
173
|
-
|
|
174
|
-
export const ExtensionSchema = z.object({
|
|
175
|
-
id: z.string().min(1),
|
|
176
|
-
name: z.string().min(1),
|
|
177
|
-
}).describe('A file format or extension accepted for deliverables, such as IFC, PDF, or DWG. Used to specify the required output formats per deliverable.')
|
|
178
|
-
|
|
179
|
-
export type Extension = z.infer<typeof ExtensionSchema>
|
|
180
|
-
|
|
181
|
-
export const AssetTypeSchema = z.object({
|
|
182
|
-
id: z.string().min(1)
|
|
183
|
-
.describe('Used in deliverables nomenclature.'),
|
|
184
|
-
name: z.string().min(1),
|
|
185
|
-
extensionIds: z.array(z.string()).optional(),
|
|
186
|
-
}).describe('A category of digital asset. Identifies the kind of information container — model, drawing, specification, etc. — independently of how it is used in the project.')
|
|
187
|
-
|
|
188
|
-
export type AssetType = z.infer<typeof AssetTypeSchema>
|
|
189
|
-
|
|
190
|
-
export const SoftwareSchema = z.object({
|
|
191
|
-
id: z.string().min(1).regex(/^[^:]+$/),
|
|
192
|
-
name: z.string().min(1),
|
|
193
|
-
version: z.string().min(1),
|
|
194
|
-
description: z.string().optional(),
|
|
195
|
-
assetTypeIds: z.array(z.string()).optional(),
|
|
196
|
-
url: z.string().optional(),
|
|
197
|
-
}).describe('A specific software application used by the project team. Linked to the asset types it produces and the BIM uses or actions that rely on it.')
|
|
198
|
-
|
|
199
|
-
export type Software = z.infer<typeof SoftwareSchema>
|
|
200
|
-
|
|
201
|
-
// ─── BIM Uses ─────────────────────────────────────────────────────────────────
|
|
202
|
-
|
|
203
|
-
export const ObjectiveSchema = z.object({
|
|
204
|
-
id: z.uuid(),
|
|
205
|
-
description: z.string().min(1),
|
|
206
|
-
}).describe('A stated reason for using BIM on this project. Objectives are the root of the BEP — all BIM uses, workflows, and deliverables must serve at least one.')
|
|
207
|
-
|
|
208
|
-
export type Objective = z.infer<typeof ObjectiveSchema>
|
|
209
|
-
|
|
210
|
-
export const BIMUseSchema = z.object({
|
|
211
|
-
id: z.uuid(),
|
|
212
|
-
name: z.string().min(1),
|
|
213
|
-
description: z.string().optional(),
|
|
214
|
-
objectiveIds: z.array(z.string()).optional(),
|
|
215
|
-
milestoneIds: z.array(z.string()).optional(),
|
|
216
|
-
workflowIds: z.array(z.string()).optional(),
|
|
217
|
-
}).describe('A specific application of BIM that serves one or more project objectives. Links intent to execution by connecting objectives, workflows, and milestones.')
|
|
218
|
-
|
|
219
|
-
export type BIMUse = z.infer<typeof BIMUseSchema>
|
|
220
|
-
|
|
221
|
-
// ─── Actions & Workflows ──────────────────────────────────────────────────────
|
|
222
|
-
|
|
223
|
-
export const ActionSchema = z.object({
|
|
224
|
-
id: z.uuid(),
|
|
225
|
-
name: z.string().min(1),
|
|
226
|
-
description: z.string().optional(),
|
|
227
|
-
softwareIds: z.array(z.string()).optional().describe('ref Software.id[]'),
|
|
228
|
-
guideIds: z.array(z.string()).optional().describe('ref Guide.id[]'),
|
|
229
|
-
}).describe('A human-performed activity referenced by workflow process nodes. Actions represent what a person in a given role does at a specific step.')
|
|
230
|
-
|
|
231
|
-
export type Action = z.infer<typeof ActionSchema>
|
|
232
|
-
|
|
233
|
-
// ─── Events & Effects (global catalogs) ───────────────────────────────────────
|
|
234
|
-
|
|
235
|
-
export const FlowPayloadFieldSchema = z.object({
|
|
236
|
-
key: z.string().min(1),
|
|
237
|
-
type: z.enum(['string', 'number', 'boolean', 'url']),
|
|
238
|
-
required: z.boolean(),
|
|
239
|
-
})
|
|
240
|
-
|
|
241
|
-
export type FlowPayloadField = z.infer<typeof FlowPayloadFieldSchema>
|
|
242
|
-
|
|
243
|
-
export const FlowEventSchema = z.object({
|
|
244
|
-
id: z.string().min(1).describe('Human-readable slug, e.g. "status-changed".'),
|
|
245
|
-
name: z.string().min(1),
|
|
246
|
-
payload: z.array(FlowPayloadFieldSchema).optional(),
|
|
247
|
-
}).describe('A signal that advances a workflow instance from one node to the next. Carries a typed payload that moves the workflow instance forward and can be evaluated by decision guards.')
|
|
248
|
-
|
|
249
|
-
export type FlowEvent = z.infer<typeof FlowEventSchema>
|
|
250
|
-
|
|
251
|
-
export const FlowEffectSchema = z.object({
|
|
252
|
-
id: z.string().min(1).describe('Human-readable slug, e.g. "notify".'),
|
|
253
|
-
name: z.string().min(1),
|
|
254
|
-
description: z.string().min(1).describe('Describe exactly what this effect must do so the runtime developer knows what to implement: what action it performs and what external system or service it calls.'),
|
|
255
|
-
payload: z.array(FlowPayloadFieldSchema).optional(),
|
|
256
|
-
}).describe('A fire-and-forget side effect triggered on a workflow edge. Executed by the runtime when the edge is traversed, using fields from the instance context as payload.')
|
|
257
|
-
|
|
258
|
-
export type FlowEffect = z.infer<typeof FlowEffectSchema>
|
|
259
|
-
|
|
260
|
-
export const FlowAutomationSchema = z.object({
|
|
261
|
-
id: z.string().min(1).describe('Human-readable slug, e.g. "verify-tolerances".'),
|
|
262
|
-
name: z.string().min(1),
|
|
263
|
-
description: z.string().min(1).describe('Describe what this handler checks and what decision it produces: what condition or business rule it evaluates and where that data comes from.'),
|
|
264
|
-
payload: z.array(FlowPayloadFieldSchema).optional()
|
|
265
|
-
.describe('Fields consumed from instance context and passed to the handler.'),
|
|
266
|
-
output: z.array(FlowPayloadFieldSchema)
|
|
267
|
-
.describe('Fields the handler must return. Guards on outgoing edges reference these.'),
|
|
268
|
-
}).describe('A system-executed node in a workflow. Runs a handler automatically, produces a typed output, and must be followed by a decision node that branches on that output.')
|
|
269
|
-
|
|
270
|
-
export type FlowAutomation = z.infer<typeof FlowAutomationSchema>
|
|
271
|
-
|
|
272
|
-
// ─── Flow graph ───────────────────────────────────────────────────────────────
|
|
273
|
-
|
|
274
|
-
export const NodeTimeoutSchema = z.object({
|
|
275
|
-
hours: z.number().positive(),
|
|
276
|
-
effectId: z.string().min(1).describe('ref FlowEffect.id'),
|
|
277
|
-
}).describe('A time-based escalation on a process or automation node. Fires a declared effect if the node has not been advanced within the given number of hours.')
|
|
278
|
-
|
|
279
|
-
export type NodeTimeout = z.infer<typeof NodeTimeoutSchema>
|
|
280
|
-
|
|
281
|
-
// ── Mixins ──
|
|
282
|
-
|
|
283
|
-
const RaciMixinSchema = z.object({
|
|
284
|
-
responsibleRoleIds: z.array(z.string()).optional().describe('ref Role.id[]'),
|
|
285
|
-
accountableRoleIds: z.array(z.string()).optional().describe('ref Role.id[]'),
|
|
286
|
-
consultedRoleIds: z.array(z.string()).optional().describe('ref Role.id[]'),
|
|
287
|
-
informedRoleIds: z.array(z.string()).optional().describe('ref Role.id[]'),
|
|
288
|
-
responsibleTeamIds: z.array(z.string()).optional().describe('ref Team.id[] — if set alongside responsibleRoleIds, actor must satisfy both.'),
|
|
289
|
-
accountableTeamIds: z.array(z.string()).optional().describe('ref Team.id[]'),
|
|
290
|
-
consultedTeamIds: z.array(z.string()).optional().describe('ref Team.id[]'),
|
|
291
|
-
informedTeamIds: z.array(z.string()).optional().describe('ref Team.id[]'),
|
|
292
|
-
responsibleEmails: z.array(z.string()).optional().describe('ref Member.email[] — authorizes specific members regardless of role or team.'),
|
|
293
|
-
accountableEmails: z.array(z.string()).optional().describe('ref Member.email[]'),
|
|
294
|
-
consultedEmails: z.array(z.string()).optional().describe('ref Member.email[]'),
|
|
295
|
-
informedEmails: z.array(z.string()).optional().describe('ref Member.email[]'),
|
|
296
|
-
})
|
|
297
|
-
|
|
298
|
-
const TimeoutMixinSchema = z.object({
|
|
299
|
-
timeouts: z.array(NodeTimeoutSchema).optional(),
|
|
300
|
-
})
|
|
301
|
-
|
|
302
|
-
// ── Concrete node schemas ──
|
|
303
|
-
|
|
304
|
-
export const FlowStartNodeSchema = z.object({
|
|
305
|
-
type: z.literal('start'),
|
|
306
|
-
})
|
|
307
|
-
|
|
308
|
-
export const FlowEndNodeSchema = z.object({
|
|
309
|
-
type: z.literal('end'),
|
|
310
|
-
})
|
|
311
|
-
|
|
312
|
-
export const FlowDecisionNodeSchema = z.object({
|
|
313
|
-
type: z.literal('decision'),
|
|
314
|
-
label: z.string().min(1),
|
|
315
|
-
})
|
|
316
|
-
|
|
317
|
-
export const FlowAutomationNodeSchema = z.object({
|
|
318
|
-
type: z.literal('automation'),
|
|
319
|
-
automationId: z.string().min(1).describe('ref FlowAutomation.id'),
|
|
320
|
-
}).extend(TimeoutMixinSchema.shape)
|
|
321
|
-
|
|
322
|
-
export const FlowProcessNodeSchema = z.object({
|
|
323
|
-
type: z.literal('process'),
|
|
324
|
-
actionId: z.string().optional().describe('ref Action.id'),
|
|
325
|
-
workflowId: z.string().optional().describe('ref Workflow.id — spawns child instances of this workflow when the node is entered.'),
|
|
326
|
-
blocking: z.boolean().optional().describe('If true, waits for all spawned child instances to complete before accepting outgoing transitions.'),
|
|
327
|
-
}).extend(RaciMixinSchema.shape).extend(TimeoutMixinSchema.shape)
|
|
328
|
-
.refine(n => !!n.actionId !== !!n.workflowId, {
|
|
329
|
-
message: 'process nodes require exactly one of actionId or workflowId.',
|
|
330
|
-
path: ['actionId'],
|
|
331
|
-
})
|
|
332
|
-
.refine(n => !n.blocking || !!n.workflowId, {
|
|
333
|
-
message: 'blocking requires workflowId to be set.',
|
|
334
|
-
path: ['blocking'],
|
|
335
|
-
})
|
|
336
|
-
.refine(n =>
|
|
337
|
-
(n.responsibleRoleIds?.length ?? 0) > 0 ||
|
|
338
|
-
(n.responsibleTeamIds?.length ?? 0) > 0 ||
|
|
339
|
-
(n.responsibleEmails?.length ?? 0) > 0,
|
|
340
|
-
{
|
|
341
|
-
message: 'process nodes require at least one responsible (role, team, or email).',
|
|
342
|
-
path: ['responsibleRoleIds'],
|
|
343
|
-
}
|
|
344
|
-
)
|
|
345
|
-
|
|
346
|
-
export const FlowNodeSchema = z.union([
|
|
347
|
-
FlowStartNodeSchema,
|
|
348
|
-
FlowEndNodeSchema,
|
|
349
|
-
FlowDecisionNodeSchema,
|
|
350
|
-
FlowAutomationNodeSchema,
|
|
351
|
-
FlowProcessNodeSchema,
|
|
352
|
-
]).describe('A node in a workflow diagram used to describe steps.')
|
|
353
|
-
|
|
354
|
-
export type FlowStartNode = z.infer<typeof FlowStartNodeSchema>
|
|
355
|
-
export type FlowEndNode = z.infer<typeof FlowEndNodeSchema>
|
|
356
|
-
export type FlowDecisionNode = z.infer<typeof FlowDecisionNodeSchema>
|
|
357
|
-
export type FlowAutomationNode = z.infer<typeof FlowAutomationNodeSchema>
|
|
358
|
-
export type FlowProcessNode = z.infer<typeof FlowProcessNodeSchema>
|
|
359
|
-
export type FlowNode = z.infer<typeof FlowNodeSchema>
|
|
360
|
-
|
|
361
|
-
export const EdgeGuardSchema = z.object({
|
|
362
|
-
field: z.string().min(1),
|
|
363
|
-
operator: z.enum(['eq', 'neq', 'gt', 'lt', 'contains', 'exists']),
|
|
364
|
-
value: z.union([z.string(), z.number(), z.boolean()]).optional(),
|
|
365
|
-
})
|
|
366
|
-
.refine(g => g.operator === 'exists' || g.value !== undefined, {
|
|
367
|
-
message: 'value is required when operator is not "exists".',
|
|
368
|
-
path: ['value'],
|
|
369
|
-
})
|
|
370
|
-
|
|
371
|
-
export type EdgeGuard = z.infer<typeof EdgeGuardSchema>
|
|
372
|
-
|
|
373
|
-
const FlowEdgeBaseSchema = z.object({
|
|
374
|
-
from: z.string().describe('ref FlowNode key'),
|
|
375
|
-
to: z.string().describe('ref FlowNode key'),
|
|
376
|
-
label: z.string().optional(),
|
|
377
|
-
effectIds: z.array(z.string().min(1)).optional().describe('ref FlowEffect.id[]'),
|
|
378
|
-
}).refine(e => e.from !== e.to, {
|
|
379
|
-
message: 'A flow edge cannot point from a node to itself.',
|
|
380
|
-
path: ['to'],
|
|
381
|
-
})
|
|
382
|
-
|
|
383
|
-
export const FlowTransitionEdgeSchema = FlowEdgeBaseSchema.extend({
|
|
384
|
-
triggerEventId: z.string().min(1).describe('ref FlowEvent.id — the event that fires this transition'),
|
|
385
|
-
})
|
|
386
|
-
|
|
387
|
-
export const FlowDecisionEdgeSchema = FlowEdgeBaseSchema.extend({
|
|
388
|
-
guard: EdgeGuardSchema.describe('Condition evaluated against the instance context to determine which path to take.'),
|
|
389
|
-
})
|
|
390
|
-
|
|
391
|
-
// Edges from start/end nodes carry no trigger and no guard — they are structural connectors only.
|
|
392
|
-
export const FlowDirectEdgeSchema = FlowEdgeBaseSchema
|
|
393
|
-
|
|
394
|
-
export const FlowEdgeSchema = z.union([FlowTransitionEdgeSchema, FlowDecisionEdgeSchema, FlowDirectEdgeSchema])
|
|
395
|
-
|
|
396
|
-
export type FlowTransitionEdge = z.infer<typeof FlowTransitionEdgeSchema>
|
|
397
|
-
export type FlowDecisionEdge = z.infer<typeof FlowDecisionEdgeSchema>
|
|
398
|
-
export type FlowDirectEdge = z.infer<typeof FlowDirectEdgeSchema>
|
|
399
|
-
export type FlowEdge = z.infer<typeof FlowEdgeSchema>
|
|
400
|
-
|
|
401
|
-
export const FlowDiagramSchema = z.object({
|
|
402
|
-
direction: FlowDirection.default("LR"),
|
|
403
|
-
nodes: z.record(z.string(), FlowNodeSchema),
|
|
404
|
-
edges: z.record(z.string(), FlowEdgeSchema),
|
|
405
|
-
}).superRefine((diagram, ctx) => {
|
|
406
|
-
const nodeEntries = Object.entries(diagram.nodes)
|
|
407
|
-
|
|
408
|
-
// ── Exactly one start and one end ──
|
|
409
|
-
const startCount = nodeEntries.filter(([, n]) => n.type === 'start').length
|
|
410
|
-
const endCount = nodeEntries.filter(([, n]) => n.type === 'end').length
|
|
411
|
-
|
|
412
|
-
if (startCount !== 1) {
|
|
413
|
-
ctx.addIssue({
|
|
414
|
-
code: "custom",
|
|
415
|
-
message: `Diagram must have exactly one start node (found ${startCount}).`,
|
|
416
|
-
path: ['nodes'],
|
|
417
|
-
})
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
if (endCount !== 1) {
|
|
421
|
-
ctx.addIssue({
|
|
422
|
-
code: "custom",
|
|
423
|
-
message: `Diagram must have exactly one end node (found ${endCount}).`,
|
|
424
|
-
path: ['nodes'],
|
|
425
|
-
})
|
|
426
|
-
}
|
|
427
|
-
|
|
428
|
-
// ── Build outgoing edge index ──
|
|
429
|
-
const outgoing: Record<string, { edgeId: string; toKey: string }[]> = {}
|
|
430
|
-
for (const [edgeId, edge] of Object.entries(diagram.edges)) {
|
|
431
|
-
outgoing[edge.from] ??= []
|
|
432
|
-
outgoing[edge.from].push({ edgeId, toKey: edge.to })
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
// ── Per-node structural rules ──
|
|
436
|
-
for (const [nodeKey, node] of nodeEntries) {
|
|
437
|
-
const outs = outgoing[nodeKey] ?? []
|
|
438
|
-
|
|
439
|
-
if (node.type === 'automation') {
|
|
440
|
-
if (outs.length !== 1) {
|
|
441
|
-
ctx.addIssue({
|
|
442
|
-
code: "custom",
|
|
443
|
-
message: `automation node must have exactly one outgoing edge (found ${outs.length}).`,
|
|
444
|
-
path: ['nodes', nodeKey],
|
|
445
|
-
})
|
|
446
|
-
} else if (diagram.nodes[outs[0].toKey]?.type !== 'decision') {
|
|
447
|
-
ctx.addIssue({
|
|
448
|
-
code: "custom",
|
|
449
|
-
message: 'automation node must connect directly to a decision node.',
|
|
450
|
-
path: ['nodes', nodeKey],
|
|
451
|
-
})
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
if (node.type === 'decision' && outs.length < 2) {
|
|
456
|
-
ctx.addIssue({
|
|
457
|
-
code: "custom",
|
|
458
|
-
message: `decision node must have at least two outgoing edges (found ${outs.length}).`,
|
|
459
|
-
path: ['nodes', nodeKey],
|
|
460
|
-
})
|
|
461
|
-
}
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
// ── Per-edge structural rules ──
|
|
465
|
-
for (const [edgeId, edge] of Object.entries(diagram.edges)) {
|
|
466
|
-
const fromNode = diagram.nodes[edge.from]
|
|
467
|
-
if (!fromNode) continue
|
|
468
|
-
|
|
469
|
-
const hasTriggerId = 'triggerEventId' in edge
|
|
470
|
-
const hasGuard = 'guard' in edge
|
|
471
|
-
|
|
472
|
-
if (fromNode.type === 'start' && (hasTriggerId || hasGuard)) {
|
|
473
|
-
ctx.addIssue({
|
|
474
|
-
code: "custom",
|
|
475
|
-
message: 'Edges from start nodes cannot have a trigger or guard — instance creation is the implicit trigger.',
|
|
476
|
-
path: ['edges', edgeId],
|
|
477
|
-
})
|
|
478
|
-
}
|
|
479
|
-
|
|
480
|
-
if (fromNode.type === 'decision' && !hasGuard) {
|
|
481
|
-
ctx.addIssue({
|
|
482
|
-
code: "custom",
|
|
483
|
-
message: 'Edges from decision nodes must have a guard — all paths must be explicit.',
|
|
484
|
-
path: ['edges', edgeId, 'guard'],
|
|
485
|
-
})
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
if (fromNode.type === 'decision' && hasTriggerId) {
|
|
489
|
-
ctx.addIssue({
|
|
490
|
-
code: "custom",
|
|
491
|
-
message: 'Edges from decision nodes cannot have a trigger — traversal is automatic via guards.',
|
|
492
|
-
path: ['edges', edgeId, 'triggerEventId'],
|
|
493
|
-
})
|
|
494
|
-
}
|
|
495
|
-
|
|
496
|
-
if ((fromNode.type === 'process' || fromNode.type === 'automation') && !hasTriggerId) {
|
|
497
|
-
ctx.addIssue({
|
|
498
|
-
code: "custom",
|
|
499
|
-
message: `Edges from ${fromNode.type} nodes must have a trigger.`,
|
|
500
|
-
path: ['edges', edgeId, 'triggerEventId'],
|
|
501
|
-
})
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
if ((fromNode.type === 'process' || fromNode.type === 'automation') && hasGuard) {
|
|
505
|
-
ctx.addIssue({
|
|
506
|
-
code: "custom",
|
|
507
|
-
message: `Edges from ${fromNode.type} nodes cannot have a guard — use a decision node to branch.`,
|
|
508
|
-
path: ['edges', edgeId, 'guard'],
|
|
509
|
-
})
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
}).describe('The visual and structural definition of a workflow.')
|
|
513
|
-
|
|
514
|
-
export type FlowDiagram = z.infer<typeof FlowDiagramSchema>
|
|
515
|
-
|
|
516
|
-
export const WorkflowSchema = z.object({
|
|
517
|
-
id: z.uuid(),
|
|
518
|
-
name: z.string().min(1),
|
|
519
|
-
description: z.string().optional(),
|
|
520
|
-
example: z.string().optional().describe("Narrative example showcasing one cycle of this workflow."),
|
|
521
|
-
trackedAssetTypeId: z.string().optional()
|
|
522
|
-
.describe('ref AssetType.id'),
|
|
523
|
-
diagram: FlowDiagramSchema,
|
|
524
|
-
}).describe('A reusable process definition associated with one or more BIM uses. Describes the ordered steps, responsibilities, and transitions that govern how work is carried out.')
|
|
525
|
-
|
|
526
|
-
export type Workflow = z.infer<typeof WorkflowSchema>
|
|
527
|
-
|
|
528
|
-
// ─── Guides & Annexes ─────────────────────────────────────────────────────────
|
|
529
|
-
|
|
530
|
-
export const AnnexSchema = z.object({
|
|
531
|
-
id: z.uuid(),
|
|
532
|
-
name: z.string().min(1),
|
|
533
|
-
type: AnnexType,
|
|
534
|
-
url: z.string().min(1),
|
|
535
|
-
description: z.string().optional(),
|
|
536
|
-
}).describe('Supporting material attached to the BEP, such as a reference document or instructional video.')
|
|
537
|
-
|
|
538
|
-
export type Annex = z.infer<typeof AnnexSchema>
|
|
539
|
-
|
|
540
|
-
export const GuideSchema = z.object({
|
|
541
|
-
id: z.uuid(),
|
|
542
|
-
name: z.string().min(1),
|
|
543
|
-
description: z.string().optional().describe("Short guide description, not it's content"),
|
|
544
|
-
annexIds: z.array(z.string()).optional(),
|
|
545
|
-
}).describe('A how-to reference included in the BEP. Groups related annexes and provides direction on how specific tasks or standards should be applied.')
|
|
546
|
-
|
|
547
|
-
export type Guide = z.infer<typeof GuideSchema>
|
|
548
|
-
|
|
549
|
-
// ─── Standards ────────────────────────────────────────────────────────────────
|
|
550
|
-
|
|
551
|
-
export const StandardSchema = z.object({
|
|
552
|
-
id: z.uuid(),
|
|
553
|
-
name: z.string().min(1),
|
|
554
|
-
description: z.string().optional(),
|
|
555
|
-
contentPath: z.string()
|
|
556
|
-
.describe('Relative path to the .md file inside the .bep archive.'),
|
|
557
|
-
}).describe('A normative reference or rule that governs how work is carried out on the project. Standards define what must be followed, as opposed to guides which explain how.')
|
|
558
|
-
|
|
559
|
-
export type Standard = z.infer<typeof StandardSchema>
|
|
560
|
-
|
|
561
|
-
// ─── LOIN ─────────────────────────────────────────────────────────────────────
|
|
562
|
-
|
|
563
|
-
export const LODSchema = z.object({
|
|
564
|
-
id: z.string().min(1),
|
|
565
|
-
name: z.string().min(1),
|
|
566
|
-
checklist: z.array(z.string()).optional(),
|
|
567
|
-
}).describe('A geometric detail level assigned to model elements in LOIN requirements. Specifies the geometric precision required of a model element at a given milestone.')
|
|
568
|
-
|
|
569
|
-
export type LOD = z.infer<typeof LODSchema>
|
|
570
|
-
|
|
571
|
-
export const LOISchema = z.object({
|
|
572
|
-
id: z.string().min(1),
|
|
573
|
-
name: z.string().min(1),
|
|
574
|
-
checklist: z.array(z.string()).optional(),
|
|
575
|
-
}).describe('An information detail level assigned to model elements in LOIN requirements. Specifies the non-geometric properties required of a model element at a given milestone.')
|
|
576
|
-
|
|
577
|
-
export type LOI = z.infer<typeof LOISchema>
|
|
578
|
-
|
|
579
|
-
export const LOINMilestoneSchema = z.object({
|
|
580
|
-
milestoneId: z.string(),
|
|
581
|
-
lodId: z.string().min(1),
|
|
582
|
-
loiId: z.string().min(1),
|
|
583
|
-
idsPath: z.string().optional()
|
|
584
|
-
.describe('Relative path to the .ids (Information Delivery Specification) file inside the .bep archive.'),
|
|
585
|
-
})
|
|
586
|
-
|
|
587
|
-
export type LOINMilestone = z.infer<typeof LOINMilestoneSchema>
|
|
588
|
-
|
|
589
|
-
export const LOINSchema = z.object({
|
|
590
|
-
id: z.uuid(),
|
|
591
|
-
element: z.string().min(1),
|
|
592
|
-
disciplineId: z.string(),
|
|
593
|
-
milestones: z.array(LOINMilestoneSchema).optional(),
|
|
594
|
-
}).describe('An information requirement for a model element. Declares what geometric detail and information properties are required for a specific model element and discipline across project milestones.')
|
|
595
|
-
|
|
596
|
-
export type LOIN = z.infer<typeof LOINSchema>
|
|
597
|
-
|
|
598
|
-
// ─── Notes & Flags ────────────────────────────────────────────────────────────
|
|
599
|
-
|
|
600
|
-
export const NoteSchema = z.object({
|
|
601
|
-
id: z.uuid(),
|
|
602
|
-
message: z.string().min(1),
|
|
603
|
-
memberEmail: z.email(),
|
|
604
|
-
createdAt: z.iso.datetime(),
|
|
605
|
-
}).describe('A timestamped comment left by a project member on the BEP.')
|
|
606
|
-
|
|
607
|
-
export type Note = z.infer<typeof NoteSchema>
|
|
608
|
-
|
|
609
|
-
export const FlagBaseSchema = z.object({
|
|
610
|
-
id: z.uuid(),
|
|
611
|
-
entity: FlagEntityType.nullable().describe('null = BEP-level flag'),
|
|
612
|
-
entityId: z.string().nullable(),
|
|
613
|
-
severity: FlagSeverity,
|
|
614
|
-
message: z.string().min(1),
|
|
615
|
-
generatedAt: z.iso.datetime(),
|
|
616
|
-
}).describe('A diagnostic message attached to the BEP or one of its entities. Indicates an issue or observation with a severity level that guides the author.')
|
|
617
|
-
|
|
618
|
-
export const FlagSchema = FlagBaseSchema
|
|
619
|
-
.refine(f => (f.entity === null) === (f.entityId === null), {
|
|
620
|
-
message: 'entity and entityId must both be null (BEP-level) or both be non-null (entity-level).',
|
|
621
|
-
})
|
|
622
|
-
|
|
623
|
-
export type Flag = z.infer<typeof FlagSchema>
|
|
624
|
-
|
|
625
|
-
// ─── Deliverables ─────────────────────────────────────────────────────────────
|
|
626
|
-
|
|
627
|
-
export const DeliverableBaseSchema = z.object({
|
|
628
|
-
id: z.uuid(),
|
|
629
|
-
description: z.string().optional(),
|
|
630
|
-
lbsNodeId: z.string().optional(),
|
|
631
|
-
disciplineId: z.string(),
|
|
632
|
-
assetTypeId: z.string(),
|
|
633
|
-
extensionIds: z.array(z.string()).optional(),
|
|
634
|
-
responsibleId: z.string(),
|
|
635
|
-
milestoneId: z.string(),
|
|
636
|
-
dueDate: z.iso.date().optional(),
|
|
637
|
-
predecessorId: z.string().optional(),
|
|
638
|
-
url: z.url().optional(),
|
|
639
|
-
resolverId: z.string().min(1).optional(),
|
|
640
|
-
}).describe('A formal output committed by a team at a milestone. Defines what must be delivered, in what format, by whom, and by when.')
|
|
641
|
-
|
|
642
|
-
export const DeliverableSchema = DeliverableBaseSchema
|
|
643
|
-
.refine(d => !d.predecessorId || d.predecessorId !== d.id, {
|
|
644
|
-
message: 'predecessorId cannot reference the deliverable itself.',
|
|
645
|
-
path: ['predecessorId'],
|
|
646
|
-
})
|
|
647
|
-
|
|
648
|
-
export type Deliverable = z.infer<typeof DeliverableSchema>
|
|
649
|
-
|
|
650
|
-
// ─── Environment variables ────────────────────────────────────────────────────
|
|
651
|
-
|
|
652
|
-
export const EnvVarSchema = z.object({
|
|
653
|
-
key: z.string().min(1).describe('Variable name referenced in effect handlers as config.KEY.'),
|
|
654
|
-
description: z.string().optional(),
|
|
655
|
-
secret: z.boolean().optional(),
|
|
656
|
-
}).describe('A runtime configuration entry for effect and automation handlers. Used to store credentials, endpoints, or other runtime settings without hardcoding them.')
|
|
657
|
-
|
|
658
|
-
export type EnvVar = z.infer<typeof EnvVarSchema>
|
|
659
|
-
|
|
660
|
-
// ─── Remote Data ────────────────────────────────────────────────────────
|
|
661
|
-
|
|
662
|
-
export const ResolverSchema = z.object({
|
|
663
|
-
id: z.string().min(1).describe('Human-readable slug, e.g. "google-sheet"'),
|
|
664
|
-
name: z.string().min(1),
|
|
665
|
-
description: z.string().min(1).describe('Describe what external data this fetches, from where, and what format it returns: which service or API it calls, what credentials it needs, and the shape of the raw payload it returns so the runtime developer and adapter author know what to expect.'),
|
|
666
|
-
envKeys: z.array(z.string().min(1)).describe('ref EnvVar.key[] — env vars required by this resolver handler.'),
|
|
667
|
-
}).describe('A server-side handler declared in the BEP bundle that authenticates against an external service, fetches data, and returns a raw payload for a live resource.')
|
|
668
|
-
|
|
669
|
-
export type Resolver = z.infer<typeof ResolverSchema>
|
|
670
|
-
|
|
671
|
-
export const AdapterSchema = z.object({
|
|
672
|
-
id: z.string().min(1).describe('Human-readable slug, e.g. "discipline-progress"'),
|
|
673
|
-
name: z.string().min(1),
|
|
674
|
-
description: z.string().min(1).describe('Describe the input format (resolver output shape), the transformation applied, and the output format (lens-compatible shape) so the runtime developer can implement the correct mapping.'),
|
|
675
|
-
}).describe('A transformation function declared in the BEP bundle that converts resolver output into a compatible format.')
|
|
676
|
-
|
|
677
|
-
export type Adapter = z.infer<typeof AdapterSchema>
|
|
678
|
-
|
|
679
|
-
export const RemoteDataSchema = z.object({
|
|
680
|
-
id: z.uuid(),
|
|
681
|
-
name: z.string().min(1),
|
|
682
|
-
description: z.string().min(1).describe('Describe what this data source represents in the project context: what information it holds, how it is used in the BEP, and who or what system produces it.'),
|
|
683
|
-
url: z.url(),
|
|
684
|
-
resolverId: z.string().min(1).optional().describe('ref Resolver.id'),
|
|
685
|
-
}).describe('A reference to an external data source that is fetched at runtime. The BEP does not store the data — it only declares where it lives and how to access it using a resolver.')
|
|
686
|
-
|
|
687
|
-
export type RemoteData = z.infer<typeof RemoteDataSchema>
|
|
688
|
-
|
|
689
|
-
// ─── BEP Root ─────────────────────────────────────────────────────────────────
|
|
690
|
-
|
|
691
|
-
export const BEPSchema = z.object({
|
|
692
|
-
project: ProjectSchema,
|
|
693
|
-
deliverableNamingConvention: NamingConventionSchema.optional(),
|
|
694
|
-
roles: z.array(RoleSchema),
|
|
695
|
-
members: z.array(MemberSchema),
|
|
696
|
-
teams: z.array(TeamSchema),
|
|
697
|
-
phases: z.array(PhaseSchema),
|
|
698
|
-
milestones: z.array(MilestoneSchema),
|
|
699
|
-
lbs: z.array(LBSNodeSchema),
|
|
700
|
-
disciplines: z.array(DisciplineSchema),
|
|
701
|
-
extensions: z.array(ExtensionSchema),
|
|
702
|
-
assetTypes: z.array(AssetTypeSchema),
|
|
703
|
-
softwares: z.array(SoftwareSchema),
|
|
704
|
-
objectives: z.array(ObjectiveSchema),
|
|
705
|
-
bimUses: z.array(BIMUseSchema),
|
|
706
|
-
actions: z.array(ActionSchema),
|
|
707
|
-
events: z.array(FlowEventSchema),
|
|
708
|
-
effects: z.array(FlowEffectSchema),
|
|
709
|
-
automations: z.array(FlowAutomationSchema),
|
|
710
|
-
env: z.array(EnvVarSchema),
|
|
711
|
-
resolvers: z.array(ResolverSchema),
|
|
712
|
-
adapters: z.array(AdapterSchema),
|
|
713
|
-
remoteData: z.array(RemoteDataSchema),
|
|
714
|
-
workflows: z.array(WorkflowSchema),
|
|
715
|
-
guides: z.array(GuideSchema),
|
|
716
|
-
annexes: z.array(AnnexSchema),
|
|
717
|
-
standards: z.array(StandardSchema),
|
|
718
|
-
lods: z.array(LODSchema),
|
|
719
|
-
lois: z.array(LOISchema),
|
|
720
|
-
loin: z.array(LOINSchema),
|
|
721
|
-
deliverables: z.array(DeliverableSchema),
|
|
722
|
-
notes: z.array(NoteSchema),
|
|
723
|
-
flags: z.array(FlagSchema),
|
|
724
|
-
})
|
|
725
|
-
|
|
726
|
-
export type BEP = z.infer<typeof BEPSchema>
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
// ─── Changelog ────────────────────────────────────────────────────────────────
|
|
730
|
-
|
|
731
|
-
export const BEPVersionBase = z.object({
|
|
732
|
-
version: z.string().regex(/^\d+\.\d+$/)
|
|
733
|
-
.describe('Format: "{major}.{minor}" (e.g. "1.0", "2.3").'),
|
|
734
|
-
date: z.iso.datetime(),
|
|
735
|
-
author: z.email().describe('ref Member.email'),
|
|
736
|
-
description: z.string().min(1),
|
|
737
|
-
diff: z.string().nullable()
|
|
738
|
-
.describe('Relative path to inverse diff (RFC 6902). null on v0.0.'),
|
|
739
|
-
})
|
|
740
|
-
|
|
741
|
-
export const BEPVersionSchema = z.discriminatedUnion('type', [
|
|
742
|
-
BEPVersionBase.extend({ type: z.literal('patch') }),
|
|
743
|
-
BEPVersionBase.extend({
|
|
744
|
-
type: z.literal('version'),
|
|
745
|
-
approvedBy: z.array(z.email()).describe('ref Member.email[]'),
|
|
746
|
-
}),
|
|
747
|
-
])
|
|
748
|
-
|
|
749
|
-
export type BEPVersion = z.infer<typeof BEPVersionSchema>
|
|
750
|
-
|
|
751
|
-
export const ChangelogSchema = z.object({
|
|
752
|
-
current: z.string().regex(/^\d+\.\d+$/)
|
|
753
|
-
.describe('Current version in "{major}.{minor}" format.'),
|
|
754
|
-
versions: z.array(BEPVersionSchema),
|
|
755
|
-
})
|
|
756
|
-
|
|
757
|
-
export type Changelog = z.infer<typeof ChangelogSchema>
|