@elevasis/ui 2.41.0 → 2.42.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/api/index.js +4 -6
- package/dist/app/index.css +384 -0
- package/dist/app/index.d.ts +5 -1
- package/dist/app/index.js +17 -26
- package/dist/auth/index.css +659 -0
- package/dist/auth/index.js +19 -5
- package/dist/charts/index.css +533 -0
- package/dist/charts/index.js +18 -14
- package/dist/{chunk-JAN2ZXN5.js → chunk-3MTAHV5M.js} +28535 -18021
- package/dist/{chunk-73EWE2EW.js → chunk-EDVZ3AHA.js} +1 -1
- package/dist/chunk-GMXGDO3I.js +244 -0
- package/dist/{chunk-CXY7FMUM.js → chunk-GUKY77FJ.js} +50 -4
- package/dist/{chunk-TE4P6OSJ.js → chunk-MA7YCY7C.js} +1 -1
- package/dist/{chunk-5JYKCULK.js → chunk-NZ2F5RQ4.js} +44 -2
- package/dist/{chunk-WF7CONXF.js → chunk-OJJK27GC.js} +658 -6
- package/dist/chunk-YEGMSADG.js +1781 -0
- package/dist/components/chat/index.js +1 -2
- package/dist/components/index.css +149 -149
- package/dist/components/index.d.ts +5 -1
- package/dist/components/index.js +13 -36
- package/dist/components/navigation/index.css +659 -0
- package/dist/components/navigation/index.js +25 -3
- package/dist/features/auth/index.js +14 -37
- package/dist/features/clients/index.css +149 -149
- package/dist/features/clients/index.js +13 -36
- package/dist/features/crm/index.js +13 -36
- package/dist/features/dashboard/index.d.ts +5 -1
- package/dist/features/dashboard/index.js +13 -36
- package/dist/features/delivery/index.js +13 -36
- package/dist/features/knowledge/index.css +659 -0
- package/dist/features/knowledge/index.js +25 -247
- package/dist/features/lead-gen/index.d.ts +5 -1
- package/dist/features/lead-gen/index.js +13 -36
- package/dist/features/monitoring/index.js +13 -36
- package/dist/features/monitoring/requests/index.js +19 -149
- package/dist/features/operations/index.d.ts +5 -1
- package/dist/features/operations/index.js +13 -36
- package/dist/features/seo/index.js +1 -4
- package/dist/features/settings/index.js +13 -36
- package/dist/hooks/access/index.css +659 -0
- package/dist/hooks/access/index.js +19 -4
- package/dist/hooks/delivery/index.js +13 -36
- package/dist/hooks/index.d.ts +5 -1
- package/dist/hooks/index.js +13 -36
- package/dist/hooks/operations/command-view/utils/transformCommandViewData.d.ts +5 -1
- package/dist/hooks/published.d.ts +5 -1
- package/dist/hooks/published.js +13 -36
- package/dist/index.d.ts +5 -1
- package/dist/index.js +14 -37
- package/dist/initialization/index.js +1 -1
- package/dist/knowledge/index.css +659 -0
- package/dist/knowledge/index.d.ts +5 -1
- package/dist/knowledge/index.js +25 -15
- package/dist/layout/index.css +659 -0
- package/dist/layout/index.js +24 -9
- package/dist/organization/index.js +13 -36
- package/dist/provider/index.css +384 -0
- package/dist/provider/index.d.ts +5 -1
- package/dist/provider/index.js +18 -21
- package/dist/provider/published.css +533 -0
- package/dist/provider/published.d.ts +5 -1
- package/dist/provider/published.js +18 -16
- package/dist/test-utils/index.js +4 -6
- package/dist/theme/index.js +2 -5
- package/dist/theme/presets/index.js +1 -2
- package/dist/types/index.d.ts +5 -1
- package/dist/utils/index.d.ts +5 -1
- package/dist/utils/index.js +1 -3
- package/package.json +3 -3
- package/dist/chunk-3KMDHCAR.js +0 -52
- package/dist/chunk-4DRI3G36.js +0 -1016
- package/dist/chunk-56O7QQE7.js +0 -356
- package/dist/chunk-5EYJ2GIN.js +0 -122
- package/dist/chunk-66U7JOWV.js +0 -425
- package/dist/chunk-6D4LCJ52.js +0 -10
- package/dist/chunk-6ROXVZ3L.js +0 -9
- package/dist/chunk-A2XN6PR2.js +0 -111
- package/dist/chunk-B2DZLPDL.js +0 -39
- package/dist/chunk-CLDCYJQT.js +0 -1
- package/dist/chunk-CTJBPF3Z.js +0 -734
- package/dist/chunk-DT3QYZVU.js +0 -23
- package/dist/chunk-FIMGOWOT.js +0 -3644
- package/dist/chunk-IIMU5YAJ.js +0 -53
- package/dist/chunk-JHVEA5NE.js +0 -133
- package/dist/chunk-L7GXUSCV.js +0 -215
- package/dist/chunk-NYBEU5TE.js +0 -118
- package/dist/chunk-QVQMOQXB.js +0 -1240
- package/dist/chunk-RH5VWWSC.js +0 -624
- package/dist/chunk-RXH4D6TY.js +0 -801
- package/dist/chunk-S4R2ZQS7.js +0 -2131
- package/dist/chunk-TYRUKGGD.js +0 -46
- package/dist/chunk-VAAU2Z3S.js +0 -85
- package/dist/chunk-WLOQ4IBG.js +0 -654
- package/dist/chunk-X4WBGKJQ.js +0 -138
- package/dist/chunk-YPWN2WQ3.js +0 -340
|
@@ -0,0 +1,1781 @@
|
|
|
1
|
+
import { formatDistanceToNow } from 'date-fns';
|
|
2
|
+
import { IconUser, IconExternalLink, IconPlug, IconBolt, IconGitBranch, IconBrain } from '@tabler/icons-react';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
|
|
5
|
+
// src/utils/dateFormatters.ts
|
|
6
|
+
function formatDateTime(dateString) {
|
|
7
|
+
if (!dateString) return "Never";
|
|
8
|
+
const date = new Date(dateString);
|
|
9
|
+
return date.toLocaleDateString("en-US", {
|
|
10
|
+
year: "numeric",
|
|
11
|
+
month: "short",
|
|
12
|
+
day: "numeric",
|
|
13
|
+
hour: "2-digit",
|
|
14
|
+
minute: "2-digit"
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function formatDate(dateString) {
|
|
18
|
+
if (!dateString) return "Never";
|
|
19
|
+
const date = new Date(dateString);
|
|
20
|
+
return date.toLocaleDateString("en-US", {
|
|
21
|
+
year: "numeric",
|
|
22
|
+
month: "short",
|
|
23
|
+
day: "numeric"
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function formatChartAxisDate(dateString) {
|
|
27
|
+
if (!dateString) return "";
|
|
28
|
+
const date = new Date(dateString);
|
|
29
|
+
const month = date.getMonth() + 1;
|
|
30
|
+
const day = date.getDate();
|
|
31
|
+
const hour = date.getHours();
|
|
32
|
+
const period = hour >= 12 ? "PM" : "AM";
|
|
33
|
+
const hour12 = hour % 12 || 12;
|
|
34
|
+
return `${month}/${day}, ${hour12}${period}`;
|
|
35
|
+
}
|
|
36
|
+
function formatRelativeTime(date) {
|
|
37
|
+
if (!date) return "N/A";
|
|
38
|
+
const dateObj = typeof date === "string" ? new Date(date) : date;
|
|
39
|
+
return formatDistanceToNow(dateObj, { addSuffix: true });
|
|
40
|
+
}
|
|
41
|
+
function formatTimeAgo(date) {
|
|
42
|
+
if (!date) return "-";
|
|
43
|
+
const dateObj = typeof date === "string" ? new Date(date) : date;
|
|
44
|
+
const diffMs = Date.now() - dateObj.getTime();
|
|
45
|
+
const diffMinutes = Math.floor(diffMs / (1e3 * 60));
|
|
46
|
+
const diffHours = Math.floor(diffMs / (1e3 * 60 * 60));
|
|
47
|
+
const diffDays = Math.floor(diffMs / (1e3 * 60 * 60 * 24));
|
|
48
|
+
if (diffMinutes < 1) return "Just now";
|
|
49
|
+
if (diffHours < 1) return `${diffMinutes}m ago`;
|
|
50
|
+
if (diffDays < 1) return `${diffHours}h ago`;
|
|
51
|
+
if (diffDays === 1) return "1d ago";
|
|
52
|
+
return `${diffDays}d ago`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// src/utils/validateEmail.ts
|
|
56
|
+
var validateEmail = (email) => {
|
|
57
|
+
return /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)+$/.test(email);
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
// src/utils/error-utils.ts
|
|
61
|
+
var APIClientError = class extends Error {
|
|
62
|
+
statusCode;
|
|
63
|
+
code;
|
|
64
|
+
requestId;
|
|
65
|
+
fields;
|
|
66
|
+
retryAfter;
|
|
67
|
+
constructor(message, code, statusCode, requestId, fields, retryAfter) {
|
|
68
|
+
super(message);
|
|
69
|
+
this.name = "APIClientError";
|
|
70
|
+
this.code = code;
|
|
71
|
+
this.statusCode = statusCode;
|
|
72
|
+
this.requestId = requestId;
|
|
73
|
+
this.fields = fields;
|
|
74
|
+
this.retryAfter = retryAfter;
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
function isAPIClientError(error) {
|
|
78
|
+
return error instanceof APIClientError;
|
|
79
|
+
}
|
|
80
|
+
function getErrorInfo(error) {
|
|
81
|
+
if (isAPIClientError(error)) {
|
|
82
|
+
return {
|
|
83
|
+
message: error.message,
|
|
84
|
+
code: error.code,
|
|
85
|
+
requestId: error.requestId,
|
|
86
|
+
statusCode: error.statusCode,
|
|
87
|
+
fields: error.fields,
|
|
88
|
+
retryAfter: error.retryAfter
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
if (error instanceof Error) {
|
|
92
|
+
return {
|
|
93
|
+
message: error.message
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return {
|
|
97
|
+
message: String(error)
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
function getErrorTitle(code) {
|
|
101
|
+
if (!code) return "Error";
|
|
102
|
+
switch (code) {
|
|
103
|
+
case "VALIDATION_ERROR":
|
|
104
|
+
return "Validation Error";
|
|
105
|
+
case "AUTHENTICATION_FAILED":
|
|
106
|
+
return "Authentication Required";
|
|
107
|
+
case "FORBIDDEN":
|
|
108
|
+
return "Access Denied";
|
|
109
|
+
case "NOT_FOUND":
|
|
110
|
+
return "Not Found";
|
|
111
|
+
case "CONFLICT":
|
|
112
|
+
return "Conflict";
|
|
113
|
+
case "RATE_LIMIT_EXCEEDED":
|
|
114
|
+
return "Rate Limit Exceeded";
|
|
115
|
+
case "INTERNAL_SERVER_ERROR":
|
|
116
|
+
return "Server Error";
|
|
117
|
+
case "SERVICE_UNAVAILABLE":
|
|
118
|
+
return "Service Temporarily Unavailable";
|
|
119
|
+
default:
|
|
120
|
+
return "Error";
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
function formatErrorMessage(message, requestId, fields, retryAfter) {
|
|
124
|
+
let formatted = message;
|
|
125
|
+
if (fields && Object.keys(fields).length > 0) {
|
|
126
|
+
const fieldErrors = [];
|
|
127
|
+
for (const [field, errors] of Object.entries(fields)) {
|
|
128
|
+
const fieldName = field === "root" || field === "_root" ? "Request" : field;
|
|
129
|
+
errors.forEach((error) => {
|
|
130
|
+
fieldErrors.push(`\u2022 ${fieldName}: ${error}`);
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
if (fieldErrors.length > 0) {
|
|
134
|
+
formatted = fieldErrors.join("\n");
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (retryAfter) {
|
|
138
|
+
formatted += `
|
|
139
|
+
|
|
140
|
+
Please wait ${retryAfter} seconds before retrying.`;
|
|
141
|
+
}
|
|
142
|
+
if (requestId) {
|
|
143
|
+
formatted += `
|
|
144
|
+
|
|
145
|
+
Request ID: ${requestId}`;
|
|
146
|
+
}
|
|
147
|
+
return formatted;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ../core/src/platform/utils/debounce.ts
|
|
151
|
+
function debounce(fn, wait) {
|
|
152
|
+
let timeoutId = null;
|
|
153
|
+
const debounced = (...args) => {
|
|
154
|
+
if (timeoutId !== null) {
|
|
155
|
+
clearTimeout(timeoutId);
|
|
156
|
+
}
|
|
157
|
+
timeoutId = setTimeout(() => {
|
|
158
|
+
fn(...args);
|
|
159
|
+
timeoutId = null;
|
|
160
|
+
}, wait);
|
|
161
|
+
};
|
|
162
|
+
debounced.cancel = () => {
|
|
163
|
+
if (timeoutId !== null) {
|
|
164
|
+
clearTimeout(timeoutId);
|
|
165
|
+
timeoutId = null;
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
return debounced;
|
|
169
|
+
}
|
|
170
|
+
var ORGANIZATION_MODEL_ICON_TOKENS = [
|
|
171
|
+
// Navigation / app areas
|
|
172
|
+
"dashboard",
|
|
173
|
+
"calendar",
|
|
174
|
+
"sales",
|
|
175
|
+
"crm",
|
|
176
|
+
"lead-gen",
|
|
177
|
+
"projects",
|
|
178
|
+
"clients",
|
|
179
|
+
"operations",
|
|
180
|
+
"monitoring",
|
|
181
|
+
"knowledge",
|
|
182
|
+
"settings",
|
|
183
|
+
"admin",
|
|
184
|
+
"archive",
|
|
185
|
+
"business",
|
|
186
|
+
"finance",
|
|
187
|
+
"platform",
|
|
188
|
+
"seo",
|
|
189
|
+
// Knowledge kinds
|
|
190
|
+
"playbook",
|
|
191
|
+
"strategy",
|
|
192
|
+
"reference",
|
|
193
|
+
// Resource kinds
|
|
194
|
+
"agent",
|
|
195
|
+
"workflow",
|
|
196
|
+
"integration",
|
|
197
|
+
"database",
|
|
198
|
+
"user",
|
|
199
|
+
"team",
|
|
200
|
+
// Integration specifics
|
|
201
|
+
"gmail",
|
|
202
|
+
"google-sheets",
|
|
203
|
+
"attio",
|
|
204
|
+
// Surface / UI views
|
|
205
|
+
"overview",
|
|
206
|
+
"command-view",
|
|
207
|
+
"command-queue",
|
|
208
|
+
"pipeline",
|
|
209
|
+
"lists",
|
|
210
|
+
"resources",
|
|
211
|
+
// Actions
|
|
212
|
+
"approve",
|
|
213
|
+
"reject",
|
|
214
|
+
"retry",
|
|
215
|
+
"edit",
|
|
216
|
+
"view",
|
|
217
|
+
"launch",
|
|
218
|
+
"message",
|
|
219
|
+
"message-plus",
|
|
220
|
+
"escalate",
|
|
221
|
+
"promote",
|
|
222
|
+
"submit",
|
|
223
|
+
"email",
|
|
224
|
+
// Status
|
|
225
|
+
"success",
|
|
226
|
+
"error",
|
|
227
|
+
"warning",
|
|
228
|
+
"info",
|
|
229
|
+
"pending",
|
|
230
|
+
// OM / UI group icons
|
|
231
|
+
"bolt",
|
|
232
|
+
"building",
|
|
233
|
+
"briefcase",
|
|
234
|
+
"apps",
|
|
235
|
+
"graph",
|
|
236
|
+
"shield",
|
|
237
|
+
"users",
|
|
238
|
+
"chart-bar",
|
|
239
|
+
"search"
|
|
240
|
+
];
|
|
241
|
+
var CustomIconTokenSchema = z.string().trim().max(80).regex(
|
|
242
|
+
/^custom\.[a-z0-9]+(?:[-._][a-z0-9]+)*$/,
|
|
243
|
+
'Custom icon tokens must start with "custom." followed by lowercase alphanumeric segments'
|
|
244
|
+
);
|
|
245
|
+
var OrganizationModelBuiltinIconTokenSchema = z.enum(ORGANIZATION_MODEL_ICON_TOKENS);
|
|
246
|
+
var OrganizationModelIconTokenSchema = z.union([
|
|
247
|
+
OrganizationModelBuiltinIconTokenSchema,
|
|
248
|
+
CustomIconTokenSchema
|
|
249
|
+
]);
|
|
250
|
+
|
|
251
|
+
// ../core/src/organization-model/domains/shared.ts
|
|
252
|
+
var ModelIdSchema = z.string().trim().min(1).max(100).regex(/^[a-z0-9]+(?:[-._][a-z0-9]+)*$/, "IDs must be lowercase and use -, _, or . separators");
|
|
253
|
+
var LabelSchema = z.string().trim().min(1).max(120);
|
|
254
|
+
var DescriptionSchema = z.string().trim().min(1).max(2e3);
|
|
255
|
+
var ColorTokenSchema = z.string().trim().min(1).max(50);
|
|
256
|
+
var IconNameSchema = OrganizationModelIconTokenSchema;
|
|
257
|
+
var PathSchema = z.string().trim().startsWith("/").max(300);
|
|
258
|
+
var ReferenceIdsSchema = z.array(ModelIdSchema).default([]);
|
|
259
|
+
var DisplayMetadataSchema = z.object({
|
|
260
|
+
label: LabelSchema,
|
|
261
|
+
description: DescriptionSchema.optional(),
|
|
262
|
+
color: ColorTokenSchema.optional(),
|
|
263
|
+
icon: IconNameSchema.optional()
|
|
264
|
+
});
|
|
265
|
+
var TechStackEntrySchema = z.object({
|
|
266
|
+
/** Name of the external platform (e.g. "HubSpot", "Stripe", "Notion"). */
|
|
267
|
+
platform: z.string().trim().min(1).max(200),
|
|
268
|
+
/** Free-form description of what this integration is used for. */
|
|
269
|
+
purpose: z.string().trim().min(1).max(500),
|
|
270
|
+
/**
|
|
271
|
+
* Health of the credential backing this integration.
|
|
272
|
+
* - configured: credential present and valid
|
|
273
|
+
* - pending: not yet set up
|
|
274
|
+
* - expired: credential existed but has lapsed
|
|
275
|
+
* - missing: expected but not present
|
|
276
|
+
*/
|
|
277
|
+
credentialStatus: z.enum(["configured", "pending", "expired", "missing"]),
|
|
278
|
+
/**
|
|
279
|
+
* Whether this integration is the primary system of record for its domain
|
|
280
|
+
* (e.g. HubSpot is SoR for contacts). Defaults to false.
|
|
281
|
+
*/
|
|
282
|
+
isSystemOfRecord: z.boolean().default(false)
|
|
283
|
+
});
|
|
284
|
+
DisplayMetadataSchema.extend({
|
|
285
|
+
id: ModelIdSchema,
|
|
286
|
+
resourceId: z.string().trim().min(1).max(255),
|
|
287
|
+
resourceType: z.enum(["workflow", "agent", "trigger", "integration", "external", "human_checkpoint"]),
|
|
288
|
+
systemIds: ReferenceIdsSchema,
|
|
289
|
+
entityIds: ReferenceIdsSchema,
|
|
290
|
+
surfaceIds: ReferenceIdsSchema,
|
|
291
|
+
actionIds: ReferenceIdsSchema,
|
|
292
|
+
/** Optional tech-stack metadata for external-SaaS integrations. */
|
|
293
|
+
techStack: TechStackEntrySchema.optional()
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
// ../core/src/organization-model/helpers.ts
|
|
297
|
+
function defineDomainRecord(schema, entries) {
|
|
298
|
+
return Object.fromEntries(
|
|
299
|
+
entries.map((entry) => {
|
|
300
|
+
const parsed = schema.parse(entry);
|
|
301
|
+
return [parsed.id, parsed];
|
|
302
|
+
})
|
|
303
|
+
);
|
|
304
|
+
}
|
|
305
|
+
function childSystemsOf(system) {
|
|
306
|
+
return system.systems ?? system.subsystems ?? {};
|
|
307
|
+
}
|
|
308
|
+
function defaultPathFor(id) {
|
|
309
|
+
return `/${id.replaceAll(".", "/")}`;
|
|
310
|
+
}
|
|
311
|
+
function parentIdOf(id) {
|
|
312
|
+
const index = id.lastIndexOf(".");
|
|
313
|
+
return index === -1 ? void 0 : id.slice(0, index);
|
|
314
|
+
}
|
|
315
|
+
function findById(systems, id) {
|
|
316
|
+
return systems[id];
|
|
317
|
+
}
|
|
318
|
+
function findByPath(systems, path) {
|
|
319
|
+
return Object.values(systems).find((system) => (system.path ?? system.ui?.path ?? defaultPathFor(system.id)) === path);
|
|
320
|
+
}
|
|
321
|
+
function childrenOf(systems, id) {
|
|
322
|
+
const prefix = `${id}.`;
|
|
323
|
+
return Object.values(systems).filter(
|
|
324
|
+
(system) => system.id.startsWith(prefix) && !system.id.slice(prefix.length).includes(".")
|
|
325
|
+
);
|
|
326
|
+
}
|
|
327
|
+
function topLevel(systems) {
|
|
328
|
+
return Object.values(systems).filter((system) => !system.id.includes("."));
|
|
329
|
+
}
|
|
330
|
+
function ancestorsOf(systems, id) {
|
|
331
|
+
const segments = id.split(".");
|
|
332
|
+
const ids = segments.map((_, index) => segments.slice(0, index + 1).join("."));
|
|
333
|
+
return ids.map((ancestorId) => findById(systems, ancestorId)).filter((system) => Boolean(system));
|
|
334
|
+
}
|
|
335
|
+
function parentOf(systems, id) {
|
|
336
|
+
const parentId = parentIdOf(id);
|
|
337
|
+
return parentId ? findById(systems, parentId) : void 0;
|
|
338
|
+
}
|
|
339
|
+
function inheritedValue(systems, id, getValue) {
|
|
340
|
+
return ancestorsOf(systems, id).slice().reverse().map(getValue).find((value) => value !== void 0);
|
|
341
|
+
}
|
|
342
|
+
function requiresAdminFor(systems, id) {
|
|
343
|
+
return inheritedValue(systems, id, (system) => system.requiresAdmin) ?? false;
|
|
344
|
+
}
|
|
345
|
+
function devOnlyFor(systems, id) {
|
|
346
|
+
return inheritedValue(systems, id, (system) => system.devOnly || system.lifecycle === "beta" || void 0) ?? false;
|
|
347
|
+
}
|
|
348
|
+
function getSystem(model, path) {
|
|
349
|
+
const flatMatch = model.systems[path];
|
|
350
|
+
if (flatMatch !== void 0) return flatMatch;
|
|
351
|
+
const segments = path.split(".");
|
|
352
|
+
let current = model.systems;
|
|
353
|
+
let node;
|
|
354
|
+
for (const seg of segments) {
|
|
355
|
+
node = current[seg];
|
|
356
|
+
if (node === void 0) return void 0;
|
|
357
|
+
current = childSystemsOf(node);
|
|
358
|
+
}
|
|
359
|
+
return node;
|
|
360
|
+
}
|
|
361
|
+
function getSystemAncestors(model, path) {
|
|
362
|
+
const flatMatch = model.systems[path];
|
|
363
|
+
if (flatMatch !== void 0) {
|
|
364
|
+
const ancestorIds = path.split(".").map((_, index, segments2) => segments2.slice(0, index + 1).join(".")).slice(0, -1);
|
|
365
|
+
return [
|
|
366
|
+
...ancestorIds.map((ancestorId) => model.systems[ancestorId]).filter((system) => system !== void 0),
|
|
367
|
+
flatMatch
|
|
368
|
+
];
|
|
369
|
+
}
|
|
370
|
+
const segments = path.split(".");
|
|
371
|
+
const result = [];
|
|
372
|
+
let current = model.systems;
|
|
373
|
+
for (const seg of segments) {
|
|
374
|
+
const node = current[seg];
|
|
375
|
+
if (node === void 0) return [];
|
|
376
|
+
result.push(node);
|
|
377
|
+
current = childSystemsOf(node);
|
|
378
|
+
}
|
|
379
|
+
return result;
|
|
380
|
+
}
|
|
381
|
+
function listAllSystems(model) {
|
|
382
|
+
const results = [];
|
|
383
|
+
function walk(map, prefix) {
|
|
384
|
+
for (const [localId2, system] of Object.entries(map)) {
|
|
385
|
+
const fullPath = prefix ? `${prefix}.${localId2}` : localId2;
|
|
386
|
+
results.push({ path: fullPath, system });
|
|
387
|
+
const childSystems = childSystemsOf(system);
|
|
388
|
+
if (Object.keys(childSystems).length > 0) {
|
|
389
|
+
walk(childSystems, fullPath);
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
walk(model.systems, "");
|
|
394
|
+
return results;
|
|
395
|
+
}
|
|
396
|
+
function isPlainJsonObject(value) {
|
|
397
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
398
|
+
}
|
|
399
|
+
function mergeJsonConfig(base, override) {
|
|
400
|
+
const result = { ...base };
|
|
401
|
+
for (const [key, value] of Object.entries(override)) {
|
|
402
|
+
const existing = result[key];
|
|
403
|
+
result[key] = isPlainJsonObject(existing) && isPlainJsonObject(value) ? mergeJsonConfig(existing, value) : value;
|
|
404
|
+
}
|
|
405
|
+
return result;
|
|
406
|
+
}
|
|
407
|
+
function resolveSystemConfig(model, path) {
|
|
408
|
+
const system = getSystem(model, path);
|
|
409
|
+
if (system === void 0) return {};
|
|
410
|
+
return mergeJsonConfig({}, system.config ?? {});
|
|
411
|
+
}
|
|
412
|
+
function getResourcesForSystem(model, systemPath, options = {}) {
|
|
413
|
+
const { includeDescendants = false } = options;
|
|
414
|
+
const prefix = systemPath + ".";
|
|
415
|
+
return Object.values(model.resources ?? {}).filter(
|
|
416
|
+
(r) => r.systemPath === systemPath || includeDescendants && r.systemPath.startsWith(prefix)
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// ../core/src/organization-model/domains/entities.ts
|
|
421
|
+
var EntityIdSchema = ModelIdSchema;
|
|
422
|
+
var EntityLinkKindSchema = z.enum(["belongs-to", "has-many", "has-one", "many-to-many"]).meta({ label: "Link kind" });
|
|
423
|
+
var EntityLinkSchema = z.object({
|
|
424
|
+
toEntity: EntityIdSchema.meta({ ref: "entity" }),
|
|
425
|
+
kind: EntityLinkKindSchema,
|
|
426
|
+
via: z.string().trim().min(1).max(255).optional(),
|
|
427
|
+
label: LabelSchema.optional()
|
|
428
|
+
});
|
|
429
|
+
var EntitySchema = z.object({
|
|
430
|
+
id: EntityIdSchema,
|
|
431
|
+
/** Domain-map iteration order. Convention: multiples of 10 (10, 20, 30, ...) to allow easy insertion. */
|
|
432
|
+
order: z.number(),
|
|
433
|
+
label: LabelSchema,
|
|
434
|
+
description: DescriptionSchema.optional(),
|
|
435
|
+
ownedBySystemId: ModelIdSchema.meta({ ref: "system" }),
|
|
436
|
+
table: z.string().trim().min(1).max(255).optional(),
|
|
437
|
+
rowSchema: ModelIdSchema.optional(),
|
|
438
|
+
stateCatalogId: ModelIdSchema.optional(),
|
|
439
|
+
links: z.array(EntityLinkSchema).optional()
|
|
440
|
+
});
|
|
441
|
+
var EntitiesDomainSchema = z.record(z.string(), EntitySchema).refine((record) => Object.entries(record).every(([key, entry]) => entry.id === key), {
|
|
442
|
+
message: "Each entity entry id must match its map key"
|
|
443
|
+
}).default({});
|
|
444
|
+
function defineEntities(entries) {
|
|
445
|
+
return defineDomainRecord(EntitySchema, entries);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// ../core/src/organization-model/domains/actions.ts
|
|
449
|
+
var ActionResourceIdSchema = z.string().trim().min(1).max(255).regex(/^[A-Za-z0-9]+(?:[-._][A-Za-z0-9]+)*$/, "Resource IDs must use letters, numbers, -, _, or . separators");
|
|
450
|
+
z.enum(["slash-command", "mcp-tool", "api-endpoint", "script-execution"]).meta({ label: "Invocation kind" });
|
|
451
|
+
var ActionIdSchema = ModelIdSchema;
|
|
452
|
+
var ActionScopeSchema = z.union([
|
|
453
|
+
z.literal("global"),
|
|
454
|
+
z.object({
|
|
455
|
+
domain: ModelIdSchema
|
|
456
|
+
})
|
|
457
|
+
]);
|
|
458
|
+
var ActionRefSchema = z.object({
|
|
459
|
+
actionId: ActionIdSchema.meta({ ref: "action" }),
|
|
460
|
+
intent: z.enum(["exposes", "consumes"]).meta({ label: "Intent" })
|
|
461
|
+
});
|
|
462
|
+
var SlashCommandInvocationSchema = z.object({
|
|
463
|
+
kind: z.literal("slash-command"),
|
|
464
|
+
command: z.string().trim().min(1).max(200).regex(/^\/[^\s].*$/, "Slash commands must start with /"),
|
|
465
|
+
toolFactory: ModelIdSchema.optional()
|
|
466
|
+
});
|
|
467
|
+
var McpToolInvocationSchema = z.object({
|
|
468
|
+
kind: z.literal("mcp-tool"),
|
|
469
|
+
server: ModelIdSchema,
|
|
470
|
+
name: ModelIdSchema
|
|
471
|
+
});
|
|
472
|
+
var ApiEndpointInvocationSchema = z.object({
|
|
473
|
+
kind: z.literal("api-endpoint"),
|
|
474
|
+
method: z.enum(["GET", "POST", "PATCH", "DELETE"]).meta({ label: "HTTP method" }),
|
|
475
|
+
path: z.string().trim().startsWith("/").max(500),
|
|
476
|
+
requestSchema: ModelIdSchema.optional(),
|
|
477
|
+
responseSchema: ModelIdSchema.optional()
|
|
478
|
+
});
|
|
479
|
+
var ScriptExecutionInvocationSchema = z.object({
|
|
480
|
+
kind: z.literal("script-execution"),
|
|
481
|
+
resourceId: ActionResourceIdSchema
|
|
482
|
+
});
|
|
483
|
+
var ActionInvocationSchema = z.discriminatedUnion("kind", [
|
|
484
|
+
SlashCommandInvocationSchema,
|
|
485
|
+
McpToolInvocationSchema,
|
|
486
|
+
ApiEndpointInvocationSchema,
|
|
487
|
+
ScriptExecutionInvocationSchema
|
|
488
|
+
]);
|
|
489
|
+
var ActionSchema = z.object({
|
|
490
|
+
id: ActionIdSchema,
|
|
491
|
+
/** Domain-map iteration order. Convention: multiples of 10 (10, 20, 30, ...) to allow easy insertion. */
|
|
492
|
+
order: z.number(),
|
|
493
|
+
label: LabelSchema,
|
|
494
|
+
description: DescriptionSchema.optional(),
|
|
495
|
+
scope: ActionScopeSchema.default("global"),
|
|
496
|
+
resourceId: ActionResourceIdSchema.optional(),
|
|
497
|
+
affects: z.array(EntityIdSchema.meta({ ref: "entity" })).optional(),
|
|
498
|
+
invocations: z.array(ActionInvocationSchema).default([]),
|
|
499
|
+
knowledge: z.array(ModelIdSchema.meta({ ref: "knowledge" })).default([]).optional(),
|
|
500
|
+
lifecycle: z.enum(["draft", "beta", "active", "deprecated", "archived"]).meta({ label: "Lifecycle", color: "teal" }).default("active")
|
|
501
|
+
});
|
|
502
|
+
var ActionsDomainSchema = z.record(z.string(), ActionSchema).refine((record) => Object.entries(record).every(([key, entry]) => entry.id === key), {
|
|
503
|
+
message: "Each action entry id must match its map key"
|
|
504
|
+
}).default({});
|
|
505
|
+
var DEFAULT_ORGANIZATION_MODEL_ACTIONS = {};
|
|
506
|
+
function defineActions(entries) {
|
|
507
|
+
return defineDomainRecord(ActionSchema, entries);
|
|
508
|
+
}
|
|
509
|
+
var OntologyKindSchema = z.enum([
|
|
510
|
+
"object",
|
|
511
|
+
"link",
|
|
512
|
+
"action",
|
|
513
|
+
"catalog",
|
|
514
|
+
"event",
|
|
515
|
+
"interface",
|
|
516
|
+
"value-type",
|
|
517
|
+
"property",
|
|
518
|
+
"group",
|
|
519
|
+
"surface"
|
|
520
|
+
]);
|
|
521
|
+
var SYSTEM_PATH_PATTERN = "[a-z0-9][a-z0-9-]*(?:\\.[a-z0-9][a-z0-9-]*)*";
|
|
522
|
+
var LOCAL_ID_PATTERN = "[a-z0-9][a-z0-9._-]*";
|
|
523
|
+
var ONTOLOGY_ID_PATTERN = `^(global|${SYSTEM_PATH_PATTERN}):(${OntologyKindSchema.options.join("|")})\\/(${LOCAL_ID_PATTERN})$`;
|
|
524
|
+
var ONTOLOGY_ID_REGEX = new RegExp(ONTOLOGY_ID_PATTERN);
|
|
525
|
+
var OntologyIdSchema = z.string().trim().min(1).max(300).regex(
|
|
526
|
+
ONTOLOGY_ID_REGEX,
|
|
527
|
+
"Ontology IDs must use <system-path>:<kind>/<local-id> or global:<kind>/<local-id>"
|
|
528
|
+
);
|
|
529
|
+
function parseOntologyId(id) {
|
|
530
|
+
const normalized = OntologyIdSchema.parse(id);
|
|
531
|
+
const match = ONTOLOGY_ID_REGEX.exec(normalized);
|
|
532
|
+
if (match === null) {
|
|
533
|
+
throw new Error(`Invalid ontology ID "${id}"`);
|
|
534
|
+
}
|
|
535
|
+
return {
|
|
536
|
+
id: normalized,
|
|
537
|
+
scope: match[1],
|
|
538
|
+
kind: match[2],
|
|
539
|
+
localId: match[3],
|
|
540
|
+
isGlobal: match[1] === "global"
|
|
541
|
+
};
|
|
542
|
+
}
|
|
543
|
+
function formatOntologyId(input) {
|
|
544
|
+
return OntologyIdSchema.parse(`${input.scope}:${input.kind}/${input.localId}`);
|
|
545
|
+
}
|
|
546
|
+
var OntologyReferenceListSchema = z.array(OntologyIdSchema).default([]).optional();
|
|
547
|
+
var OntologyRecordBaseSchema = z.object({
|
|
548
|
+
id: OntologyIdSchema,
|
|
549
|
+
label: z.string().trim().min(1).max(160).optional(),
|
|
550
|
+
description: z.string().trim().min(1).max(2e3).optional(),
|
|
551
|
+
ownerSystemId: z.string().trim().min(1).max(200).optional(),
|
|
552
|
+
aliases: z.array(OntologyIdSchema).optional()
|
|
553
|
+
}).passthrough();
|
|
554
|
+
var OntologyObjectTypeSchema = OntologyRecordBaseSchema.extend({
|
|
555
|
+
properties: z.record(z.string().trim().min(1).max(200), z.unknown()).optional(),
|
|
556
|
+
storage: z.record(z.string(), z.unknown()).optional()
|
|
557
|
+
});
|
|
558
|
+
var OntologyLinkTypeSchema = OntologyRecordBaseSchema.extend({
|
|
559
|
+
from: OntologyIdSchema,
|
|
560
|
+
to: OntologyIdSchema,
|
|
561
|
+
cardinality: z.string().trim().min(1).max(80).optional(),
|
|
562
|
+
via: z.string().trim().min(1).max(255).optional()
|
|
563
|
+
});
|
|
564
|
+
var OntologyActionTypeSchema = OntologyRecordBaseSchema.extend({
|
|
565
|
+
actsOn: OntologyReferenceListSchema,
|
|
566
|
+
input: z.record(z.string().trim().min(1).max(200), z.unknown()).optional(),
|
|
567
|
+
effects: z.array(z.record(z.string(), z.unknown())).optional()
|
|
568
|
+
});
|
|
569
|
+
var OntologyCatalogTypeSchema = OntologyRecordBaseSchema.extend({
|
|
570
|
+
kind: z.string().trim().min(1).max(120).optional(),
|
|
571
|
+
appliesTo: OntologyIdSchema.optional(),
|
|
572
|
+
entries: z.record(z.string().trim().min(1).max(200), z.unknown()).optional()
|
|
573
|
+
});
|
|
574
|
+
var OntologyEventTypeSchema = OntologyRecordBaseSchema.extend({
|
|
575
|
+
payload: z.record(z.string().trim().min(1).max(200), z.unknown()).optional()
|
|
576
|
+
});
|
|
577
|
+
var OntologyInterfaceTypeSchema = OntologyRecordBaseSchema.extend({
|
|
578
|
+
properties: z.record(z.string().trim().min(1).max(200), z.unknown()).optional()
|
|
579
|
+
});
|
|
580
|
+
var OntologyValueTypeSchema = OntologyRecordBaseSchema.extend({
|
|
581
|
+
primitive: z.string().trim().min(1).max(120).optional()
|
|
582
|
+
});
|
|
583
|
+
var OntologySharedPropertySchema = OntologyRecordBaseSchema.extend({
|
|
584
|
+
valueType: OntologyIdSchema.optional(),
|
|
585
|
+
searchable: z.boolean().optional(),
|
|
586
|
+
pii: z.boolean().optional()
|
|
587
|
+
});
|
|
588
|
+
var OntologyGroupSchema = OntologyRecordBaseSchema.extend({
|
|
589
|
+
members: OntologyReferenceListSchema
|
|
590
|
+
});
|
|
591
|
+
var OntologySurfaceTypeSchema = OntologyRecordBaseSchema.extend({
|
|
592
|
+
route: z.string().trim().min(1).max(500).optional()
|
|
593
|
+
});
|
|
594
|
+
var OntologyScopeSchema = z.object({
|
|
595
|
+
objectTypes: z.record(OntologyIdSchema, OntologyObjectTypeSchema).default({}).optional(),
|
|
596
|
+
linkTypes: z.record(OntologyIdSchema, OntologyLinkTypeSchema).default({}).optional(),
|
|
597
|
+
actionTypes: z.record(OntologyIdSchema, OntologyActionTypeSchema).default({}).optional(),
|
|
598
|
+
catalogTypes: z.record(OntologyIdSchema, OntologyCatalogTypeSchema).default({}).optional(),
|
|
599
|
+
eventTypes: z.record(OntologyIdSchema, OntologyEventTypeSchema).default({}).optional(),
|
|
600
|
+
interfaceTypes: z.record(OntologyIdSchema, OntologyInterfaceTypeSchema).default({}).optional(),
|
|
601
|
+
valueTypes: z.record(OntologyIdSchema, OntologyValueTypeSchema).default({}).optional(),
|
|
602
|
+
sharedProperties: z.record(OntologyIdSchema, OntologySharedPropertySchema).default({}).optional(),
|
|
603
|
+
groups: z.record(OntologyIdSchema, OntologyGroupSchema).default({}).optional(),
|
|
604
|
+
surfaces: z.record(OntologyIdSchema, OntologySurfaceTypeSchema).default({}).optional()
|
|
605
|
+
}).default({});
|
|
606
|
+
var DEFAULT_ONTOLOGY_SCOPE = {
|
|
607
|
+
valueTypes: {
|
|
608
|
+
"global:value-type/uuid": {
|
|
609
|
+
id: "global:value-type/uuid",
|
|
610
|
+
label: "UUID",
|
|
611
|
+
primitive: "string"
|
|
612
|
+
},
|
|
613
|
+
"global:value-type/text": {
|
|
614
|
+
id: "global:value-type/text",
|
|
615
|
+
label: "Text",
|
|
616
|
+
primitive: "string"
|
|
617
|
+
},
|
|
618
|
+
"global:value-type/url": {
|
|
619
|
+
id: "global:value-type/url",
|
|
620
|
+
label: "URL",
|
|
621
|
+
primitive: "string"
|
|
622
|
+
},
|
|
623
|
+
"global:value-type/email": {
|
|
624
|
+
id: "global:value-type/email",
|
|
625
|
+
label: "Email",
|
|
626
|
+
primitive: "string"
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
var SCOPE_KIND = {
|
|
631
|
+
objectTypes: "object",
|
|
632
|
+
linkTypes: "link",
|
|
633
|
+
actionTypes: "action",
|
|
634
|
+
catalogTypes: "catalog",
|
|
635
|
+
eventTypes: "event",
|
|
636
|
+
interfaceTypes: "interface",
|
|
637
|
+
valueTypes: "value-type",
|
|
638
|
+
sharedProperties: "property",
|
|
639
|
+
groups: "group",
|
|
640
|
+
surfaces: "surface"
|
|
641
|
+
};
|
|
642
|
+
var SCOPE_KEYS = Object.keys(SCOPE_KIND);
|
|
643
|
+
function ontologyGraphNodeId(id) {
|
|
644
|
+
return `ontology:${OntologyIdSchema.parse(id)}`;
|
|
645
|
+
}
|
|
646
|
+
function listResolvedOntologyRecords(index) {
|
|
647
|
+
return SCOPE_KEYS.flatMap((scopeKey) => {
|
|
648
|
+
const kind = SCOPE_KIND[scopeKey];
|
|
649
|
+
return Object.entries(index[scopeKey]).sort(([leftId], [rightId]) => leftId.localeCompare(rightId)).map(([id, record]) => ({
|
|
650
|
+
id,
|
|
651
|
+
kind,
|
|
652
|
+
record
|
|
653
|
+
}));
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
function originFromContext(context) {
|
|
657
|
+
return {
|
|
658
|
+
kind: context.kind,
|
|
659
|
+
source: context.source,
|
|
660
|
+
path: context.path,
|
|
661
|
+
...context.systemPath !== void 0 ? { systemPath: context.systemPath } : {},
|
|
662
|
+
...context.legacyId !== void 0 ? { legacyId: context.legacyId } : {}
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
function createEmptyIndex() {
|
|
666
|
+
return {
|
|
667
|
+
objectTypes: {},
|
|
668
|
+
linkTypes: {},
|
|
669
|
+
actionTypes: {},
|
|
670
|
+
catalogTypes: {},
|
|
671
|
+
eventTypes: {},
|
|
672
|
+
interfaceTypes: {},
|
|
673
|
+
valueTypes: {},
|
|
674
|
+
sharedProperties: {},
|
|
675
|
+
groups: {},
|
|
676
|
+
surfaces: {}
|
|
677
|
+
};
|
|
678
|
+
}
|
|
679
|
+
function sortResolvedOntologyIndex(index) {
|
|
680
|
+
const sorted = createEmptyIndex();
|
|
681
|
+
for (const scopeKey of SCOPE_KEYS) {
|
|
682
|
+
const target = sorted[scopeKey];
|
|
683
|
+
for (const [id, record] of Object.entries(index[scopeKey]).sort(
|
|
684
|
+
([leftId], [rightId]) => leftId.localeCompare(rightId)
|
|
685
|
+
)) {
|
|
686
|
+
target[id] = record;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return sorted;
|
|
690
|
+
}
|
|
691
|
+
function childSystemsOf2(system) {
|
|
692
|
+
return system.systems ?? system.subsystems ?? {};
|
|
693
|
+
}
|
|
694
|
+
function addRecord(index, diagnostics, sourcesById, scopeKey, record, context) {
|
|
695
|
+
let parsed;
|
|
696
|
+
try {
|
|
697
|
+
parsed = parseOntologyId(record.id);
|
|
698
|
+
} catch {
|
|
699
|
+
diagnostics.push({
|
|
700
|
+
code: "invalid_ontology_id",
|
|
701
|
+
message: `Invalid ontology ID "${record.id}" from ${context.source} at ${context.path.join(".")}`,
|
|
702
|
+
id: record.id,
|
|
703
|
+
path: context.path,
|
|
704
|
+
source: context.source,
|
|
705
|
+
origin: originFromContext(context)
|
|
706
|
+
});
|
|
707
|
+
return;
|
|
708
|
+
}
|
|
709
|
+
const expectedKind = SCOPE_KIND[scopeKey];
|
|
710
|
+
if (parsed.kind !== expectedKind) {
|
|
711
|
+
diagnostics.push({
|
|
712
|
+
code: "ontology_kind_mismatch",
|
|
713
|
+
message: `Ontology ID "${record.id}" has kind "${parsed.kind}" but was authored in ${scopeKey} (${expectedKind}) at ${context.path.join(".")}`,
|
|
714
|
+
id: record.id,
|
|
715
|
+
path: context.path,
|
|
716
|
+
source: context.source,
|
|
717
|
+
origin: originFromContext(context)
|
|
718
|
+
});
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
const existing = sourcesById.get(parsed.id);
|
|
722
|
+
if (existing !== void 0) {
|
|
723
|
+
diagnostics.push({
|
|
724
|
+
code: "duplicate_ontology_id",
|
|
725
|
+
message: `Duplicate ontology ID "${parsed.id}" from ${context.source} at ${context.path.join(".")} conflicts with ${existing.source} at ${existing.path.join(".")}`,
|
|
726
|
+
id: parsed.id,
|
|
727
|
+
path: context.path,
|
|
728
|
+
source: context.source,
|
|
729
|
+
origin: originFromContext(context),
|
|
730
|
+
existingSource: existing.source,
|
|
731
|
+
existingOrigin: originFromContext(existing)
|
|
732
|
+
});
|
|
733
|
+
return;
|
|
734
|
+
}
|
|
735
|
+
sourcesById.set(parsed.id, context);
|
|
736
|
+
index[scopeKey][parsed.id] = {
|
|
737
|
+
...record,
|
|
738
|
+
origin: originFromContext(context)
|
|
739
|
+
};
|
|
740
|
+
}
|
|
741
|
+
function addScope(index, diagnostics, sourcesById, scope, source, path) {
|
|
742
|
+
if (scope === void 0) return;
|
|
743
|
+
for (const scopeKey of SCOPE_KEYS) {
|
|
744
|
+
const records = scope[scopeKey] ?? {};
|
|
745
|
+
for (const [key, record] of Object.entries(records)) {
|
|
746
|
+
addRecord(index, diagnostics, sourcesById, scopeKey, record, {
|
|
747
|
+
source,
|
|
748
|
+
path: [...path, scopeKey, key],
|
|
749
|
+
kind: "authored",
|
|
750
|
+
systemPath: source.startsWith("system:") ? source.slice("system:".length, -".ontology".length) : void 0
|
|
751
|
+
});
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
function legacyObjectId(entity) {
|
|
756
|
+
return formatOntologyId({ scope: entity.ownedBySystemId, kind: "object", localId: entity.id });
|
|
757
|
+
}
|
|
758
|
+
function legacyActionOwner(action, entities) {
|
|
759
|
+
const firstAffectedEntityId = action.affects?.find((entityId) => entities[entityId] !== void 0);
|
|
760
|
+
if (firstAffectedEntityId !== void 0) {
|
|
761
|
+
return entities[firstAffectedEntityId].ownedBySystemId;
|
|
762
|
+
}
|
|
763
|
+
if (typeof action.scope === "object") {
|
|
764
|
+
return action.scope.domain;
|
|
765
|
+
}
|
|
766
|
+
return "global";
|
|
767
|
+
}
|
|
768
|
+
function addLegacyEntityProjections(index, diagnostics, sourcesById, entities) {
|
|
769
|
+
for (const entity of Object.values(entities)) {
|
|
770
|
+
const objectType = {
|
|
771
|
+
id: legacyObjectId(entity),
|
|
772
|
+
label: entity.label,
|
|
773
|
+
description: entity.description,
|
|
774
|
+
ownerSystemId: entity.ownedBySystemId,
|
|
775
|
+
...entity.table !== void 0 ? {
|
|
776
|
+
storage: {
|
|
777
|
+
kind: "table",
|
|
778
|
+
table: entity.table
|
|
779
|
+
}
|
|
780
|
+
} : {},
|
|
781
|
+
...entity.rowSchema !== void 0 ? { rowSchema: entity.rowSchema } : {},
|
|
782
|
+
...entity.stateCatalogId !== void 0 ? { stateCatalogId: entity.stateCatalogId } : {}
|
|
783
|
+
};
|
|
784
|
+
addRecord(index, diagnostics, sourcesById, "objectTypes", objectType, {
|
|
785
|
+
source: "legacy.entities",
|
|
786
|
+
path: ["entities", entity.id],
|
|
787
|
+
kind: "projected",
|
|
788
|
+
systemPath: entity.ownedBySystemId,
|
|
789
|
+
legacyId: entity.id
|
|
790
|
+
});
|
|
791
|
+
entity.links?.forEach((link, linkIndex) => {
|
|
792
|
+
const targetEntity = entities[link.toEntity];
|
|
793
|
+
if (targetEntity === void 0) return;
|
|
794
|
+
const linkType = {
|
|
795
|
+
id: formatOntologyId({
|
|
796
|
+
scope: entity.ownedBySystemId,
|
|
797
|
+
kind: "link",
|
|
798
|
+
localId: `${entity.id}-${link.toEntity}-${linkIndex}`
|
|
799
|
+
}),
|
|
800
|
+
label: link.label ?? link.kind,
|
|
801
|
+
ownerSystemId: entity.ownedBySystemId,
|
|
802
|
+
from: legacyObjectId(entity),
|
|
803
|
+
to: legacyObjectId(targetEntity),
|
|
804
|
+
cardinality: link.kind,
|
|
805
|
+
...link.via !== void 0 ? { via: link.via } : {}
|
|
806
|
+
};
|
|
807
|
+
addRecord(index, diagnostics, sourcesById, "linkTypes", linkType, {
|
|
808
|
+
source: "legacy.entities.links",
|
|
809
|
+
path: ["entities", entity.id, "links", linkIndex],
|
|
810
|
+
kind: "projected",
|
|
811
|
+
systemPath: entity.ownedBySystemId,
|
|
812
|
+
legacyId: `${entity.id}.links.${linkIndex}`
|
|
813
|
+
});
|
|
814
|
+
});
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
function addLegacyActionProjections(index, diagnostics, sourcesById, actions, entities) {
|
|
818
|
+
for (const action of Object.values(actions)) {
|
|
819
|
+
const ownerSystemId = legacyActionOwner(action, entities);
|
|
820
|
+
const actionType = {
|
|
821
|
+
id: formatOntologyId({ scope: ownerSystemId, kind: "action", localId: action.id }),
|
|
822
|
+
label: action.label,
|
|
823
|
+
description: action.description,
|
|
824
|
+
ownerSystemId,
|
|
825
|
+
actsOn: action.affects?.map((entityId) => entities[entityId] ? legacyObjectId(entities[entityId]) : void 0).filter((id) => id !== void 0),
|
|
826
|
+
...action.resourceId !== void 0 ? { resourceId: action.resourceId } : {},
|
|
827
|
+
...action.invocations !== void 0 ? { invocations: action.invocations } : {},
|
|
828
|
+
...action.lifecycle !== void 0 ? { lifecycle: action.lifecycle } : {},
|
|
829
|
+
legacyActionId: action.id
|
|
830
|
+
};
|
|
831
|
+
addRecord(index, diagnostics, sourcesById, "actionTypes", actionType, {
|
|
832
|
+
source: "legacy.actions",
|
|
833
|
+
path: ["actions", action.id],
|
|
834
|
+
kind: "projected",
|
|
835
|
+
systemPath: ownerSystemId,
|
|
836
|
+
legacyId: action.id
|
|
837
|
+
});
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
function addSystemScopes(index, diagnostics, sourcesById, systems, prefix, schemaPath) {
|
|
841
|
+
for (const [key, system] of Object.entries(systems)) {
|
|
842
|
+
const systemPath = prefix ? `${prefix}.${key}` : key;
|
|
843
|
+
const currentPath = [...schemaPath, key];
|
|
844
|
+
addScope(index, diagnostics, sourcesById, system.ontology, `system:${systemPath}.ontology`, [
|
|
845
|
+
...currentPath,
|
|
846
|
+
"ontology"
|
|
847
|
+
]);
|
|
848
|
+
addSystemScopes(index, diagnostics, sourcesById, childSystemsOf2(system), systemPath, [
|
|
849
|
+
...currentPath,
|
|
850
|
+
system.systems !== void 0 ? "systems" : "subsystems"
|
|
851
|
+
]);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
function compileOrganizationOntology(model) {
|
|
855
|
+
const ontology = createEmptyIndex();
|
|
856
|
+
const diagnostics = [];
|
|
857
|
+
const sourcesById = /* @__PURE__ */ new Map();
|
|
858
|
+
addScope(ontology, diagnostics, sourcesById, model.ontology, "organization.ontology", ["ontology"]);
|
|
859
|
+
addSystemScopes(ontology, diagnostics, sourcesById, model.systems ?? {}, "", ["systems"]);
|
|
860
|
+
addLegacyEntityProjections(ontology, diagnostics, sourcesById, model.entities ?? {});
|
|
861
|
+
addLegacyActionProjections(ontology, diagnostics, sourcesById, model.actions ?? {}, model.entities ?? {});
|
|
862
|
+
return { ontology: sortResolvedOntologyIndex(ontology), diagnostics };
|
|
863
|
+
}
|
|
864
|
+
|
|
865
|
+
// ../core/src/organization-model/domains/systems.ts
|
|
866
|
+
var SystemKindSchema = z.enum(["product", "operational", "platform", "diagnostic"]).meta({ label: "System kind", color: "blue" });
|
|
867
|
+
var SystemLifecycleSchema = z.enum(["draft", "beta", "active", "deprecated", "archived"]).meta({ label: "Lifecycle", color: "teal" });
|
|
868
|
+
var SystemStatusSchema = z.enum(["active", "deprecated", "archived"]).meta({ label: "Status", color: "teal" });
|
|
869
|
+
var SystemIdSchema = ModelIdSchema;
|
|
870
|
+
var SystemPathSchema = z.string().trim().min(1).regex(
|
|
871
|
+
/^[a-z0-9][a-z0-9-]*(?:\.[a-z0-9][a-z0-9-]*)*$/,
|
|
872
|
+
'must be a dotted lowercase path (e.g. "sales.lead-gen" or "sales.crm")'
|
|
873
|
+
);
|
|
874
|
+
var UiPositionSchema = z.enum(["sidebar-primary", "sidebar-bottom"]).meta({ label: "UI position" });
|
|
875
|
+
var NodeIdStringSchema = z.string().trim().min(1).max(200).regex(
|
|
876
|
+
/^[a-z][a-z-]*:([a-z0-9-]+)(\.[a-z0-9-]+)*(:[a-z0-9.-]+)*$/,
|
|
877
|
+
"Node references must use kind:dotted-path (e.g. system:sales.crm or resource:lead-gen.company.qualify)"
|
|
878
|
+
);
|
|
879
|
+
var SystemUiSchema = z.object({
|
|
880
|
+
path: PathSchema,
|
|
881
|
+
surfaces: ReferenceIdsSchema,
|
|
882
|
+
icon: IconNameSchema.optional(),
|
|
883
|
+
order: z.number().int().optional()
|
|
884
|
+
});
|
|
885
|
+
var SystemInterfaceKeySchema = ModelIdSchema;
|
|
886
|
+
var SystemInterfaceLifecycleSchema = z.enum(["draft", "active", "disabled", "deprecated", "archived"]).meta({ label: "System interface lifecycle", color: "teal" });
|
|
887
|
+
var SYSTEM_INTERFACE_PROFILES = [
|
|
888
|
+
{
|
|
889
|
+
systemPath: "sales.lead-gen",
|
|
890
|
+
interfaceKey: "api",
|
|
891
|
+
readinessProfile: "sales.lead-gen.api"
|
|
892
|
+
},
|
|
893
|
+
{
|
|
894
|
+
systemPath: "sales.crm",
|
|
895
|
+
interfaceKey: "api",
|
|
896
|
+
readinessProfile: "sales.crm.api"
|
|
897
|
+
},
|
|
898
|
+
{
|
|
899
|
+
systemPath: "sales.lead-gen",
|
|
900
|
+
interfaceKey: "crm-handoff",
|
|
901
|
+
readinessProfile: "sales.lead-gen.crm-handoff"
|
|
902
|
+
}
|
|
903
|
+
];
|
|
904
|
+
var SYSTEM_INTERFACE_READINESS_PROFILES = SYSTEM_INTERFACE_PROFILES.map(
|
|
905
|
+
(profile) => profile.readinessProfile
|
|
906
|
+
);
|
|
907
|
+
var SystemInterfaceReadinessProfileSchema = z.enum(SYSTEM_INTERFACE_READINESS_PROFILES);
|
|
908
|
+
var SystemInterfaceResourceScopeSchema = z.array(ModelIdSchema).default([]);
|
|
909
|
+
var SystemApiInterfaceSchema = z.object({
|
|
910
|
+
lifecycle: SystemInterfaceLifecycleSchema.default("active"),
|
|
911
|
+
readinessProfile: SystemInterfaceReadinessProfileSchema.optional(),
|
|
912
|
+
/**
|
|
913
|
+
* Resource ids that participate in this API interface. This scopes readiness
|
|
914
|
+
* derivation without duplicating authored required/provided contract refs.
|
|
915
|
+
*/
|
|
916
|
+
resourceIds: SystemInterfaceResourceScopeSchema.optional()
|
|
917
|
+
}).strict();
|
|
918
|
+
var SystemInterfaceRefSchema = z.object({
|
|
919
|
+
systemPath: SystemPathSchema,
|
|
920
|
+
interfaceKey: SystemInterfaceKeySchema
|
|
921
|
+
}).strict();
|
|
922
|
+
var JsonValueSchema = z.lazy(
|
|
923
|
+
() => z.union([
|
|
924
|
+
z.string(),
|
|
925
|
+
z.number(),
|
|
926
|
+
z.boolean(),
|
|
927
|
+
z.null(),
|
|
928
|
+
z.array(JsonValueSchema),
|
|
929
|
+
z.record(z.string(), JsonValueSchema)
|
|
930
|
+
])
|
|
931
|
+
);
|
|
932
|
+
var SystemConfigSchema = z.record(z.string().trim().min(1).max(200), JsonValueSchema).default({}).optional();
|
|
933
|
+
var SystemEntrySchema = z.object({
|
|
934
|
+
/** Stable tenant-defined system id (e.g. "sys.lead-gen" or "sales.crm"). */
|
|
935
|
+
id: SystemIdSchema,
|
|
936
|
+
/** Human-readable system label shown in UI, governance, and operations surfaces. */
|
|
937
|
+
label: LabelSchema.optional(),
|
|
938
|
+
/** @deprecated Use label. Accepted for pre-consolidation System declarations. */
|
|
939
|
+
title: LabelSchema.optional(),
|
|
940
|
+
/** One-paragraph purpose statement for the bounded context. */
|
|
941
|
+
description: DescriptionSchema.optional(),
|
|
942
|
+
/** Closed system shape enum; catalog values remain tenant-defined. */
|
|
943
|
+
kind: SystemKindSchema.optional(),
|
|
944
|
+
/** Optional self-reference for System hierarchy. */
|
|
945
|
+
parentSystemId: SystemIdSchema.optional(),
|
|
946
|
+
/** Optional UI presence. Systems without UI omit this. */
|
|
947
|
+
ui: SystemUiSchema.optional(),
|
|
948
|
+
/** Canonical lifecycle state. Replaces Feature.enabled/devOnly and System.status. */
|
|
949
|
+
lifecycle: SystemLifecycleSchema.optional(),
|
|
950
|
+
/** Optional role responsible for this system. */
|
|
951
|
+
responsibleRoleId: ModelIdSchema.meta({ ref: "role" }).optional(),
|
|
952
|
+
/** Optional knowledge nodes that govern this system. */
|
|
953
|
+
governedByKnowledge: z.array(ModelIdSchema.meta({ ref: "knowledge" })).default([]).optional(),
|
|
954
|
+
/** Optional actions this system exposes or consumes. */
|
|
955
|
+
actions: z.array(ActionRefSchema).optional(),
|
|
956
|
+
/** Optional operational policies that apply to this system. */
|
|
957
|
+
policies: z.array(ModelIdSchema.meta({ ref: "policy" })).default([]).optional(),
|
|
958
|
+
/** Optional goals this system contributes to. */
|
|
959
|
+
drivesGoals: z.array(ModelIdSchema.meta({ ref: "goal" })).default([]).optional(),
|
|
960
|
+
/** Thin API runtime-boundary marker. Readiness is derived from scoped resources and topology. */
|
|
961
|
+
apiInterface: SystemApiInterfaceSchema.optional(),
|
|
962
|
+
/** @deprecated Use lifecycle. Accepted for one publish cycle. */
|
|
963
|
+
status: SystemStatusSchema.optional(),
|
|
964
|
+
/** @deprecated Use ui.path. Kept for one-cycle Feature compatibility. */
|
|
965
|
+
path: PathSchema.optional(),
|
|
966
|
+
/** @deprecated Use ui.icon. Kept for one-cycle Feature compatibility. */
|
|
967
|
+
icon: IconNameSchema.optional(),
|
|
968
|
+
/** @deprecated Feature color token, retained for one-cycle compatibility. */
|
|
969
|
+
color: ColorTokenSchema.optional(),
|
|
970
|
+
/** @deprecated UI placement hint, retained for one-cycle compatibility. */
|
|
971
|
+
uiPosition: UiPositionSchema.optional(),
|
|
972
|
+
/** @deprecated Use lifecycle. */
|
|
973
|
+
enabled: z.boolean().optional(),
|
|
974
|
+
/** @deprecated Use lifecycle: "beta". */
|
|
975
|
+
devOnly: z.boolean().optional(),
|
|
976
|
+
requiresAdmin: z.boolean().optional(),
|
|
977
|
+
/** Domain-map iteration order. Convention: multiples of 10 (10, 20, 30, ...) to allow easy insertion. */
|
|
978
|
+
order: z.number(),
|
|
979
|
+
/**
|
|
980
|
+
* System-local JSON settings and defaults. Strongly typed OM fields,
|
|
981
|
+
* secrets, credentials, and runtime state stay outside this bucket.
|
|
982
|
+
*/
|
|
983
|
+
config: SystemConfigSchema,
|
|
984
|
+
/**
|
|
985
|
+
* System-owned ontology declarations. `systems` is now the canonical child
|
|
986
|
+
* key; this scope holds the object, action, catalog, link, event, and
|
|
987
|
+
* shared contract records owned by this system.
|
|
988
|
+
*/
|
|
989
|
+
ontology: OntologyScopeSchema.optional(),
|
|
990
|
+
/**
|
|
991
|
+
* Recursive child systems, authored via nesting (per L11).
|
|
992
|
+
* The key is the local system id; the full path is computed by joining
|
|
993
|
+
* ancestor keys with `.` (e.g. parent key `'sales'` + child key `'crm'` → `'sales.crm'`).
|
|
994
|
+
* Per Phase 4: `id` and `parentSystemId` fields will be removed in favour of
|
|
995
|
+
* position-derived paths. Both still exist on this schema for backward compat.
|
|
996
|
+
*/
|
|
997
|
+
systems: z.lazy(() => z.record(z.string().trim().min(1).max(100), SystemEntrySchema)).optional(),
|
|
998
|
+
/** @deprecated Use systems. Accepted as a compatibility alias during the ontology bridge. */
|
|
999
|
+
subsystems: z.lazy(() => z.record(z.string().trim().min(1).max(100), SystemEntrySchema)).optional()
|
|
1000
|
+
}).strict().refine((system) => system.label !== void 0 || system.title !== void 0, {
|
|
1001
|
+
path: ["label"],
|
|
1002
|
+
message: "System must provide label or title"
|
|
1003
|
+
}).transform((system) => {
|
|
1004
|
+
const normalizedSystem = system.systems !== void 0 && system.subsystems === void 0 ? { ...system, subsystems: system.systems } : system;
|
|
1005
|
+
if (normalizedSystem.status === void 0) return normalizedSystem;
|
|
1006
|
+
console.warn("[organization-model] System.status is deprecated; use System.lifecycle instead.");
|
|
1007
|
+
return normalizedSystem.lifecycle === void 0 ? { ...normalizedSystem, lifecycle: normalizedSystem.status } : normalizedSystem;
|
|
1008
|
+
});
|
|
1009
|
+
var SystemsDomainSchema = z.record(z.string(), SystemEntrySchema).refine((record) => Object.entries(record).every(([key, entry]) => entry.id === key), {
|
|
1010
|
+
message: "Each system entry id must match its map key"
|
|
1011
|
+
}).default({});
|
|
1012
|
+
var DEFAULT_ORGANIZATION_MODEL_SYSTEMS = {};
|
|
1013
|
+
|
|
1014
|
+
// ../core/src/organization-model/migration-helpers.ts
|
|
1015
|
+
function catalogRecords(model) {
|
|
1016
|
+
return Object.values(compileOrganizationOntology(model).ontology.catalogTypes);
|
|
1017
|
+
}
|
|
1018
|
+
function localId(id) {
|
|
1019
|
+
return parseOntologyId(id).localId;
|
|
1020
|
+
}
|
|
1021
|
+
function systemScope(id, fallback) {
|
|
1022
|
+
return parseOntologyId(id).scope || fallback || "";
|
|
1023
|
+
}
|
|
1024
|
+
function entriesOf(catalog) {
|
|
1025
|
+
return Object.entries(catalog.entries ?? {}).map(([id, value]) => [
|
|
1026
|
+
id,
|
|
1027
|
+
value && typeof value === "object" && !Array.isArray(value) ? value : {}
|
|
1028
|
+
]);
|
|
1029
|
+
}
|
|
1030
|
+
function stringValue(value) {
|
|
1031
|
+
return typeof value === "string" ? value : void 0;
|
|
1032
|
+
}
|
|
1033
|
+
function stringArray(value) {
|
|
1034
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
1035
|
+
}
|
|
1036
|
+
function numberValue(value, fallback = 0) {
|
|
1037
|
+
return typeof value === "number" ? value : fallback;
|
|
1038
|
+
}
|
|
1039
|
+
function stageFromEntry(entryId, entry) {
|
|
1040
|
+
return {
|
|
1041
|
+
id: entryId,
|
|
1042
|
+
label: stringValue(entry.label) ?? entryId,
|
|
1043
|
+
order: numberValue(entry.order),
|
|
1044
|
+
semanticClass: stringValue(entry.semanticClass) ?? "open",
|
|
1045
|
+
surfaceIds: stringArray(entry.surfaceIds),
|
|
1046
|
+
resourceIds: stringArray(entry.resourceIds),
|
|
1047
|
+
color: stringValue(entry.color)
|
|
1048
|
+
};
|
|
1049
|
+
}
|
|
1050
|
+
function appliesToLocalId(catalog) {
|
|
1051
|
+
const appliesTo = catalog.appliesTo;
|
|
1052
|
+
if (appliesTo === void 0) return void 0;
|
|
1053
|
+
return parseOntologyId(appliesTo).localId;
|
|
1054
|
+
}
|
|
1055
|
+
function appliesToEntityKind(catalog) {
|
|
1056
|
+
const id = appliesToLocalId(catalog);
|
|
1057
|
+
if (id === "company" || id === "contact" || id === "project" || id === "milestone" || id === "task") return id;
|
|
1058
|
+
if (id === "deal") return void 0;
|
|
1059
|
+
return void 0;
|
|
1060
|
+
}
|
|
1061
|
+
function getAllPipelines(model) {
|
|
1062
|
+
return catalogRecords(model).filter((catalog) => catalog.kind === "pipeline").map((catalog) => {
|
|
1063
|
+
const pipeline = {
|
|
1064
|
+
id: localId(catalog.id),
|
|
1065
|
+
label: catalog.label ?? localId(catalog.id),
|
|
1066
|
+
...catalog.description ? { description: catalog.description } : {},
|
|
1067
|
+
entityId: appliesToLocalId(catalog) ?? "",
|
|
1068
|
+
stages: entriesOf(catalog).map(([entryId, entry]) => stageFromEntry(entryId, entry)).sort((a, b) => a.order - b.order || a.id.localeCompare(b.id))
|
|
1069
|
+
};
|
|
1070
|
+
return { systemPath: catalog.ownerSystemId ?? systemScope(catalog.id), pipeline };
|
|
1071
|
+
}).sort((a, b) => a.systemPath.localeCompare(b.systemPath) || a.pipeline.id.localeCompare(b.pipeline.id));
|
|
1072
|
+
}
|
|
1073
|
+
function getAllBuildTemplates(model) {
|
|
1074
|
+
const catalogs = catalogRecords(model);
|
|
1075
|
+
const stepCatalogs = new Map(
|
|
1076
|
+
catalogs.filter((catalog) => catalog.kind === "template-step").map((catalog) => [catalog.id, catalog])
|
|
1077
|
+
);
|
|
1078
|
+
return catalogs.filter((catalog) => catalog.kind === "template").flatMap(
|
|
1079
|
+
(catalog) => entriesOf(catalog).map(([templateId, templateEntry]) => {
|
|
1080
|
+
const stepCatalogId = stringValue(templateEntry.stepCatalog);
|
|
1081
|
+
const stepCatalog = stepCatalogId !== void 0 ? stepCatalogs.get(stepCatalogId) : void 0;
|
|
1082
|
+
const steps = stepCatalog === void 0 ? [] : entriesOf(stepCatalog).sort(
|
|
1083
|
+
([leftId, left], [rightId, right]) => numberValue(left.order, Number.MAX_SAFE_INTEGER) - numberValue(right.order, Number.MAX_SAFE_INTEGER) || leftId.localeCompare(rightId)
|
|
1084
|
+
);
|
|
1085
|
+
return {
|
|
1086
|
+
order: numberValue(templateEntry.order, Number.MAX_SAFE_INTEGER),
|
|
1087
|
+
template: {
|
|
1088
|
+
id: templateId,
|
|
1089
|
+
label: stringValue(templateEntry.label) ?? templateId,
|
|
1090
|
+
...stringValue(templateEntry.description) ? { description: stringValue(templateEntry.description) } : {},
|
|
1091
|
+
...stringValue(templateEntry.color) ? { color: stringValue(templateEntry.color) } : {},
|
|
1092
|
+
steps: steps.map(([stepId, step]) => ({
|
|
1093
|
+
id: stepId,
|
|
1094
|
+
label: stringValue(step.label) ?? stepId,
|
|
1095
|
+
...stringValue(step.description) ? { description: stringValue(step.description) } : {},
|
|
1096
|
+
...step
|
|
1097
|
+
}))
|
|
1098
|
+
}
|
|
1099
|
+
};
|
|
1100
|
+
})
|
|
1101
|
+
).sort((a, b) => a.order - b.order || a.template.id.localeCompare(b.template.id)).map(({ template }) => template);
|
|
1102
|
+
}
|
|
1103
|
+
function getAllProspectingStages(model, kind) {
|
|
1104
|
+
return catalogRecords(model).filter((catalog) => catalog.kind === "stage" && appliesToEntityKind(catalog) === kind).flatMap(
|
|
1105
|
+
(catalog) => entriesOf(catalog).map(([entryId, entry]) => ({
|
|
1106
|
+
id: entryId,
|
|
1107
|
+
label: stringValue(entry.label) ?? entryId,
|
|
1108
|
+
order: numberValue(entry.order),
|
|
1109
|
+
...stringValue(entry.color) ? { color: stringValue(entry.color) } : {},
|
|
1110
|
+
...stringValue(entry.description) ? { description: stringValue(entry.description) } : {}
|
|
1111
|
+
}))
|
|
1112
|
+
).sort((a, b) => a.order - b.order || a.id.localeCompare(b.id));
|
|
1113
|
+
}
|
|
1114
|
+
function getLeadGenStageCatalog(model) {
|
|
1115
|
+
const results = {};
|
|
1116
|
+
for (const catalog of catalogRecords(model).filter(
|
|
1117
|
+
(record) => record.kind === "stage" && (record.ownerSystemId ?? systemScope(record.id)) === "sales.lead-gen"
|
|
1118
|
+
)) {
|
|
1119
|
+
const catalogEntity = appliesToEntityKind(catalog);
|
|
1120
|
+
if (catalogEntity !== "company" && catalogEntity !== "contact") continue;
|
|
1121
|
+
for (const [entryId, entry] of entriesOf(catalog)) {
|
|
1122
|
+
const entity = entry.entity === "contact" ? "contact" : entry.entity === "company" ? "company" : catalogEntity;
|
|
1123
|
+
const additionalEntities = stringArray(entry.additionalEntities).filter(
|
|
1124
|
+
(item) => item === "company" || item === "contact"
|
|
1125
|
+
);
|
|
1126
|
+
const recordEntity = entry.recordEntity === "company" || entry.recordEntity === "contact" ? entry.recordEntity : void 0;
|
|
1127
|
+
const recordStageKey = stringValue(entry.recordStageKey);
|
|
1128
|
+
results[entryId] = {
|
|
1129
|
+
key: entryId,
|
|
1130
|
+
label: stringValue(entry.label) ?? entryId,
|
|
1131
|
+
description: stringValue(entry.description) ?? "",
|
|
1132
|
+
order: numberValue(entry.order),
|
|
1133
|
+
entity,
|
|
1134
|
+
...additionalEntities.length > 0 ? { additionalEntities } : {},
|
|
1135
|
+
...recordEntity ? { recordEntity } : {},
|
|
1136
|
+
...recordStageKey ? { recordStageKey } : {}
|
|
1137
|
+
};
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
return Object.fromEntries(
|
|
1141
|
+
Object.entries(results).sort(([, a], [, b]) => a.order - b.order || a.key.localeCompare(b.key))
|
|
1142
|
+
);
|
|
1143
|
+
}
|
|
1144
|
+
function getAllProjectStatuses(model, appliesTo) {
|
|
1145
|
+
return catalogRecords(model).filter((catalog) => catalog.kind === "status-flow" && appliesToEntityKind(catalog) === appliesTo).flatMap(
|
|
1146
|
+
(catalog) => entriesOf(catalog).map(([entryId, entry]) => ({
|
|
1147
|
+
id: entryId,
|
|
1148
|
+
label: stringValue(entry.label) ?? entryId,
|
|
1149
|
+
order: numberValue(entry.order),
|
|
1150
|
+
...stringValue(entry.color) ? { color: stringValue(entry.color) } : {},
|
|
1151
|
+
...stringValue(entry.description) ? { description: stringValue(entry.description) } : {}
|
|
1152
|
+
}))
|
|
1153
|
+
).sort((a, b) => a.order - b.order || a.id.localeCompare(b.id));
|
|
1154
|
+
}
|
|
1155
|
+
|
|
1156
|
+
// ../core/src/business/acquisition/ontology-validation.ts
|
|
1157
|
+
var LEAD_GEN_API_INTERFACE = SYSTEM_INTERFACE_PROFILES[0];
|
|
1158
|
+
var CRM_API_INTERFACE = SYSTEM_INTERFACE_PROFILES[1];
|
|
1159
|
+
var LEAD_GEN_CRM_HANDOFF_INTERFACE = SYSTEM_INTERFACE_PROFILES[2];
|
|
1160
|
+
LEAD_GEN_API_INTERFACE.readinessProfile;
|
|
1161
|
+
CRM_API_INTERFACE.readinessProfile;
|
|
1162
|
+
LEAD_GEN_CRM_HANDOFF_INTERFACE.readinessProfile;
|
|
1163
|
+
var LEAD_GEN_LIST_OBJECT_ONTOLOGY_ID = formatOntologyId({
|
|
1164
|
+
scope: "sales.lead-gen",
|
|
1165
|
+
kind: "object",
|
|
1166
|
+
localId: "list"
|
|
1167
|
+
});
|
|
1168
|
+
var LEAD_GEN_COMPANY_OBJECT_ONTOLOGY_ID = formatOntologyId({
|
|
1169
|
+
scope: "sales.lead-gen",
|
|
1170
|
+
kind: "object",
|
|
1171
|
+
localId: "company"
|
|
1172
|
+
});
|
|
1173
|
+
var LEAD_GEN_CONTACT_OBJECT_ONTOLOGY_ID = formatOntologyId({
|
|
1174
|
+
scope: "sales.lead-gen",
|
|
1175
|
+
kind: "object",
|
|
1176
|
+
localId: "contact"
|
|
1177
|
+
});
|
|
1178
|
+
var LEAD_GEN_BUILD_TEMPLATE_CATALOG_ONTOLOGY_ID = formatOntologyId({
|
|
1179
|
+
scope: "sales.lead-gen",
|
|
1180
|
+
kind: "catalog",
|
|
1181
|
+
localId: "build-template"
|
|
1182
|
+
});
|
|
1183
|
+
var LEAD_GEN_COMPANY_STAGE_CATALOG_ONTOLOGY_ID = formatOntologyId({
|
|
1184
|
+
scope: "sales.lead-gen",
|
|
1185
|
+
kind: "catalog",
|
|
1186
|
+
localId: "company-stage"
|
|
1187
|
+
});
|
|
1188
|
+
var LEAD_GEN_CONTACT_STAGE_CATALOG_ONTOLOGY_ID = formatOntologyId({
|
|
1189
|
+
scope: "sales.lead-gen",
|
|
1190
|
+
kind: "catalog",
|
|
1191
|
+
localId: "contact-stage"
|
|
1192
|
+
});
|
|
1193
|
+
var CRM_PIPELINE_CATALOG_ONTOLOGY_ID = formatOntologyId({
|
|
1194
|
+
scope: "sales.crm",
|
|
1195
|
+
kind: "catalog",
|
|
1196
|
+
localId: "crm.pipeline"
|
|
1197
|
+
});
|
|
1198
|
+
var LEAD_GEN_STAGE_CATALOG_ONTOLOGY_ID = formatOntologyId({
|
|
1199
|
+
scope: "sales.lead-gen",
|
|
1200
|
+
kind: "catalog",
|
|
1201
|
+
localId: "lead-gen.stage-catalog"
|
|
1202
|
+
});
|
|
1203
|
+
function createLeadGenStageCatalog(model) {
|
|
1204
|
+
return {
|
|
1205
|
+
id: LEAD_GEN_STAGE_CATALOG_ONTOLOGY_ID,
|
|
1206
|
+
label: "Lead Gen Processing Stages",
|
|
1207
|
+
ownerSystemId: "sales.lead-gen",
|
|
1208
|
+
kind: "processing-stage-catalog",
|
|
1209
|
+
entries: Object.fromEntries(
|
|
1210
|
+
Object.entries(getLeadGenStageCatalog(model)).map(([key, entry]) => [
|
|
1211
|
+
key,
|
|
1212
|
+
{
|
|
1213
|
+
...entry
|
|
1214
|
+
}
|
|
1215
|
+
])
|
|
1216
|
+
),
|
|
1217
|
+
legacyCatalogKey: "LEAD_GEN_STAGE_CATALOG"
|
|
1218
|
+
};
|
|
1219
|
+
}
|
|
1220
|
+
function isPlainRecord(value) {
|
|
1221
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
1222
|
+
}
|
|
1223
|
+
function addReadinessIssue(issues, family, code, message, details = {}) {
|
|
1224
|
+
issues.push({
|
|
1225
|
+
family,
|
|
1226
|
+
code,
|
|
1227
|
+
message,
|
|
1228
|
+
...details
|
|
1229
|
+
});
|
|
1230
|
+
}
|
|
1231
|
+
function formatInterfaceIdentity(systemPath, interfaceKey) {
|
|
1232
|
+
return `${systemPath}/${interfaceKey}`;
|
|
1233
|
+
}
|
|
1234
|
+
function profileForInterface(systemPath, interfaceKey, readinessProfile) {
|
|
1235
|
+
return readinessProfile ?? `${systemPath}.${interfaceKey}`;
|
|
1236
|
+
}
|
|
1237
|
+
function readinessMarkerPath(context) {
|
|
1238
|
+
return context.interfaceKey === "api" ? `systems.${context.systemPath}.apiInterface` : `systems.${context.systemPath}.derivedCrmHandoffReadiness`;
|
|
1239
|
+
}
|
|
1240
|
+
function formatSupportedReadinessProfiles() {
|
|
1241
|
+
return SYSTEM_INTERFACE_READINESS_PROFILES.map((profile) => `"${profile}"`).join(", ");
|
|
1242
|
+
}
|
|
1243
|
+
function getActiveScopedResources(model, resourceIds, issues, context) {
|
|
1244
|
+
const resources = [];
|
|
1245
|
+
for (const [index, resourceId] of resourceIds.entries()) {
|
|
1246
|
+
const resource = model.resources?.[resourceId];
|
|
1247
|
+
const path = `${readinessMarkerPath(context)}.resourceIds.${index}`;
|
|
1248
|
+
if (resource === void 0) {
|
|
1249
|
+
addReadinessIssue(
|
|
1250
|
+
issues,
|
|
1251
|
+
"SYSTEM_INTERFACE_NOT_READY",
|
|
1252
|
+
"missing-resource",
|
|
1253
|
+
`System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" scopes missing resource "${resourceId}".`,
|
|
1254
|
+
{ path, ref: resourceId }
|
|
1255
|
+
);
|
|
1256
|
+
continue;
|
|
1257
|
+
}
|
|
1258
|
+
if (resource.systemPath !== context.systemPath) {
|
|
1259
|
+
addReadinessIssue(
|
|
1260
|
+
issues,
|
|
1261
|
+
"SYSTEM_INTERFACE_INVALID",
|
|
1262
|
+
"resource-system-mismatch",
|
|
1263
|
+
`System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" scopes resource "${resourceId}" from system "${resource.systemPath}".`,
|
|
1264
|
+
{ path, ref: resourceId }
|
|
1265
|
+
);
|
|
1266
|
+
continue;
|
|
1267
|
+
}
|
|
1268
|
+
if (resource.status !== "active") {
|
|
1269
|
+
addReadinessIssue(
|
|
1270
|
+
issues,
|
|
1271
|
+
"SYSTEM_INTERFACE_NOT_READY",
|
|
1272
|
+
"inactive-resource",
|
|
1273
|
+
`System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" scopes inactive resource "${resourceId}".`,
|
|
1274
|
+
{ path, ref: resourceId }
|
|
1275
|
+
);
|
|
1276
|
+
continue;
|
|
1277
|
+
}
|
|
1278
|
+
resources.push(resource);
|
|
1279
|
+
}
|
|
1280
|
+
return resources;
|
|
1281
|
+
}
|
|
1282
|
+
function resourceBindingIds(resources, key) {
|
|
1283
|
+
return new Set(resources.flatMap((resource) => resource.ontology?.[key] ?? []));
|
|
1284
|
+
}
|
|
1285
|
+
function requireScopedBinding(issues, boundIds, bindingKey, ontologyId, context) {
|
|
1286
|
+
if (boundIds.has(ontologyId)) return;
|
|
1287
|
+
addReadinessIssue(
|
|
1288
|
+
issues,
|
|
1289
|
+
"SYSTEM_INTERFACE_NOT_READY",
|
|
1290
|
+
"missing-resource-binding",
|
|
1291
|
+
`System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" has no active scoped resource with ontology.${bindingKey} binding "${ontologyId}".`,
|
|
1292
|
+
{ path: `${readinessMarkerPath(context)}.resourceIds`, ref: ontologyId }
|
|
1293
|
+
);
|
|
1294
|
+
}
|
|
1295
|
+
function ontologyOwnerMatches(ontologyId, expectedSystemPath, catalog) {
|
|
1296
|
+
if (catalog?.ownerSystemId !== void 0) return catalog.ownerSystemId === expectedSystemPath;
|
|
1297
|
+
return parseOntologyId(ontologyId).scope === expectedSystemPath;
|
|
1298
|
+
}
|
|
1299
|
+
function requireObjectReadiness(issues, index, objectId, context) {
|
|
1300
|
+
const object = index.ontology.objectTypes[objectId];
|
|
1301
|
+
if (object === void 0) {
|
|
1302
|
+
addReadinessIssue(
|
|
1303
|
+
issues,
|
|
1304
|
+
"SYSTEM_INTERFACE_NOT_READY",
|
|
1305
|
+
"missing-object",
|
|
1306
|
+
`System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" is missing required object type "${objectId}".`,
|
|
1307
|
+
{ ref: objectId }
|
|
1308
|
+
);
|
|
1309
|
+
return;
|
|
1310
|
+
}
|
|
1311
|
+
const ownerSystemId = object.ownerSystemId ?? parseOntologyId(objectId).scope;
|
|
1312
|
+
if (ownerSystemId !== context.systemPath) {
|
|
1313
|
+
addReadinessIssue(
|
|
1314
|
+
issues,
|
|
1315
|
+
"SYSTEM_INTERFACE_INVALID",
|
|
1316
|
+
"foreign-object",
|
|
1317
|
+
`System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" requires object "${objectId}" owned by "${ownerSystemId}".`,
|
|
1318
|
+
{ ref: objectId }
|
|
1319
|
+
);
|
|
1320
|
+
}
|
|
1321
|
+
}
|
|
1322
|
+
function requireCatalogReadiness(issues, index, catalogId, context) {
|
|
1323
|
+
const catalog = index.ontology.catalogTypes[catalogId];
|
|
1324
|
+
if (catalog === void 0) {
|
|
1325
|
+
addReadinessIssue(
|
|
1326
|
+
issues,
|
|
1327
|
+
"SYSTEM_INTERFACE_NOT_READY",
|
|
1328
|
+
"missing-catalog",
|
|
1329
|
+
`System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" is missing required catalog "${catalogId}".`,
|
|
1330
|
+
{ ref: catalogId }
|
|
1331
|
+
);
|
|
1332
|
+
return void 0;
|
|
1333
|
+
}
|
|
1334
|
+
if (context.allowForeignOwner !== true && !ontologyOwnerMatches(catalogId, context.systemPath, catalog)) {
|
|
1335
|
+
addReadinessIssue(
|
|
1336
|
+
issues,
|
|
1337
|
+
"SYSTEM_INTERFACE_INVALID",
|
|
1338
|
+
"foreign-catalog",
|
|
1339
|
+
`System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" requires catalog "${catalogId}" owned by "${catalog.ownerSystemId ?? parseOntologyId(catalogId).scope}".`,
|
|
1340
|
+
{ ref: catalogId }
|
|
1341
|
+
);
|
|
1342
|
+
}
|
|
1343
|
+
if (Object.keys(getCatalogEntries(catalog)).length === 0) {
|
|
1344
|
+
addReadinessIssue(
|
|
1345
|
+
issues,
|
|
1346
|
+
"SYSTEM_INTERFACE_NOT_READY",
|
|
1347
|
+
"empty-catalog",
|
|
1348
|
+
`System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" requires catalog entries for "${catalogId}".`,
|
|
1349
|
+
{ ref: catalogId }
|
|
1350
|
+
);
|
|
1351
|
+
}
|
|
1352
|
+
return catalog;
|
|
1353
|
+
}
|
|
1354
|
+
function requireLeadGenInterfaceReadiness(issues, index, resources, context) {
|
|
1355
|
+
const reads = resourceBindingIds(resources, "reads");
|
|
1356
|
+
const catalogs = resourceBindingIds(resources, "usesCatalogs");
|
|
1357
|
+
for (const objectId of [
|
|
1358
|
+
LEAD_GEN_LIST_OBJECT_ONTOLOGY_ID,
|
|
1359
|
+
LEAD_GEN_COMPANY_OBJECT_ONTOLOGY_ID,
|
|
1360
|
+
LEAD_GEN_CONTACT_OBJECT_ONTOLOGY_ID
|
|
1361
|
+
]) {
|
|
1362
|
+
requireObjectReadiness(issues, index, objectId, context);
|
|
1363
|
+
requireScopedBinding(issues, reads, "reads", objectId, context);
|
|
1364
|
+
}
|
|
1365
|
+
for (const catalogId of [
|
|
1366
|
+
LEAD_GEN_BUILD_TEMPLATE_CATALOG_ONTOLOGY_ID,
|
|
1367
|
+
LEAD_GEN_COMPANY_STAGE_CATALOG_ONTOLOGY_ID,
|
|
1368
|
+
LEAD_GEN_CONTACT_STAGE_CATALOG_ONTOLOGY_ID
|
|
1369
|
+
]) {
|
|
1370
|
+
requireCatalogReadiness(issues, index, catalogId, context);
|
|
1371
|
+
requireScopedBinding(issues, catalogs, "usesCatalogs", catalogId, context);
|
|
1372
|
+
}
|
|
1373
|
+
const templateStepCatalog = findLeadGenTemplateStepCatalog(index);
|
|
1374
|
+
if (templateStepCatalog === void 0) {
|
|
1375
|
+
addReadinessIssue(
|
|
1376
|
+
issues,
|
|
1377
|
+
"SYSTEM_INTERFACE_NOT_READY",
|
|
1378
|
+
"missing-template-step-catalog",
|
|
1379
|
+
`System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" is missing a lead-gen template-step catalog.`
|
|
1380
|
+
);
|
|
1381
|
+
} else {
|
|
1382
|
+
requireCatalogReadiness(issues, index, templateStepCatalog.id, context);
|
|
1383
|
+
requireScopedBinding(issues, catalogs, "usesCatalogs", templateStepCatalog.id, context);
|
|
1384
|
+
}
|
|
1385
|
+
const leadGenStageCatalog = requireCatalogReadiness(issues, index, LEAD_GEN_STAGE_CATALOG_ONTOLOGY_ID, context);
|
|
1386
|
+
return leadGenStageCatalog;
|
|
1387
|
+
}
|
|
1388
|
+
function requireCrmInterfaceReadiness(issues, index, resources, context) {
|
|
1389
|
+
const catalogs = resourceBindingIds(resources, "usesCatalogs");
|
|
1390
|
+
const crmPipelineCatalog = requireCatalogReadiness(issues, index, CRM_PIPELINE_CATALOG_ONTOLOGY_ID, context);
|
|
1391
|
+
requireScopedBinding(issues, catalogs, "usesCatalogs", CRM_PIPELINE_CATALOG_ONTOLOGY_ID, context);
|
|
1392
|
+
return crmPipelineCatalog;
|
|
1393
|
+
}
|
|
1394
|
+
function findSystemInterfaceGrant(model, consumer, provider) {
|
|
1395
|
+
return Object.values(model.topology?.relationships ?? {}).find((relationship) => {
|
|
1396
|
+
const grant = relationship.metadata?.["systemInterfaceGrant"];
|
|
1397
|
+
if (!isPlainRecord(grant) || !isPlainRecord(grant.consumer) || !isPlainRecord(grant.provider)) return false;
|
|
1398
|
+
return grant.consumer.systemPath === consumer.systemPath && grant.consumer.interfaceKey === consumer.interfaceKey && grant.provider.systemPath === provider.systemPath && grant.provider.interfaceKey === provider.interfaceKey;
|
|
1399
|
+
});
|
|
1400
|
+
}
|
|
1401
|
+
function requireHandoffBridgeReadiness(issues, model, context) {
|
|
1402
|
+
const crmResult = computeInterfaceReadiness(model, CRM_API_INTERFACE);
|
|
1403
|
+
if (!crmResult.ready) {
|
|
1404
|
+
for (const issue of crmResult.issues) {
|
|
1405
|
+
addReadinessIssue(
|
|
1406
|
+
issues,
|
|
1407
|
+
"SYSTEM_BRIDGE_NOT_READY",
|
|
1408
|
+
issue.code,
|
|
1409
|
+
`Provider interface ${formatInterfaceIdentity(CRM_API_INTERFACE.systemPath, CRM_API_INTERFACE.interfaceKey)} is not ready: ${issue.message}`,
|
|
1410
|
+
{ path: issue.path, ref: issue.ref }
|
|
1411
|
+
);
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
const bridgeGrant = findSystemInterfaceGrant(model, context, CRM_API_INTERFACE);
|
|
1415
|
+
if (bridgeGrant === void 0) {
|
|
1416
|
+
addReadinessIssue(
|
|
1417
|
+
issues,
|
|
1418
|
+
"SYSTEM_BRIDGE_NOT_READY",
|
|
1419
|
+
"missing-topology-grant",
|
|
1420
|
+
`System Interface "${formatInterfaceIdentity(context.systemPath, context.interfaceKey)}" requires a scoped topology grant to "${formatInterfaceIdentity(CRM_API_INTERFACE.systemPath, CRM_API_INTERFACE.interfaceKey)}".`
|
|
1421
|
+
);
|
|
1422
|
+
}
|
|
1423
|
+
return bridgeGrant;
|
|
1424
|
+
}
|
|
1425
|
+
function getLeadGenCrmHandoffResourceIds(model) {
|
|
1426
|
+
return Object.values(model.resources ?? {}).filter(
|
|
1427
|
+
(resource) => resource.systemPath === LEAD_GEN_CRM_HANDOFF_INTERFACE.systemPath && resource.ontology?.usesCatalogs?.includes(CRM_PIPELINE_CATALOG_ONTOLOGY_ID) === true
|
|
1428
|
+
).map((resource) => resource.id);
|
|
1429
|
+
}
|
|
1430
|
+
function getSystemInterfaceReadinessMarker(model, request) {
|
|
1431
|
+
const system = getSystem(model, request.systemPath);
|
|
1432
|
+
if (system === void 0) return void 0;
|
|
1433
|
+
if (request.interfaceKey === LEAD_GEN_API_INTERFACE.interfaceKey) {
|
|
1434
|
+
return system.apiInterface;
|
|
1435
|
+
}
|
|
1436
|
+
if (request.systemPath === LEAD_GEN_CRM_HANDOFF_INTERFACE.systemPath && request.interfaceKey === LEAD_GEN_CRM_HANDOFF_INTERFACE.interfaceKey) {
|
|
1437
|
+
return {
|
|
1438
|
+
lifecycle: "active",
|
|
1439
|
+
readinessProfile: LEAD_GEN_CRM_HANDOFF_INTERFACE.readinessProfile,
|
|
1440
|
+
resourceIds: getLeadGenCrmHandoffResourceIds(model)
|
|
1441
|
+
};
|
|
1442
|
+
}
|
|
1443
|
+
return void 0;
|
|
1444
|
+
}
|
|
1445
|
+
function mergeLeadGenDerivedCatalogs(model) {
|
|
1446
|
+
const baseCatalogTypes = model.ontology?.catalogTypes ?? {};
|
|
1447
|
+
const derivedCatalogTypes = {};
|
|
1448
|
+
if (baseCatalogTypes[LEAD_GEN_STAGE_CATALOG_ONTOLOGY_ID] === void 0) {
|
|
1449
|
+
derivedCatalogTypes[LEAD_GEN_STAGE_CATALOG_ONTOLOGY_ID] = createLeadGenStageCatalog(model);
|
|
1450
|
+
}
|
|
1451
|
+
if (Object.keys(derivedCatalogTypes).length === 0) return model;
|
|
1452
|
+
return {
|
|
1453
|
+
...model,
|
|
1454
|
+
ontology: {
|
|
1455
|
+
...model.ontology ?? {},
|
|
1456
|
+
catalogTypes: {
|
|
1457
|
+
...baseCatalogTypes,
|
|
1458
|
+
...derivedCatalogTypes
|
|
1459
|
+
}
|
|
1460
|
+
}
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
1463
|
+
function compileBusinessOntology(model, contractId) {
|
|
1464
|
+
const compilation = compileOrganizationOntology(mergeLeadGenDerivedCatalogs(model));
|
|
1465
|
+
if (compilation.diagnostics.length > 0) {
|
|
1466
|
+
const summary = compilation.diagnostics.map((diagnostic) => diagnostic.message).join("; ");
|
|
1467
|
+
throw new Error(`${contractId} ontology validation index failed to compile: ${summary}`);
|
|
1468
|
+
}
|
|
1469
|
+
return {
|
|
1470
|
+
ontology: compilation.ontology,
|
|
1471
|
+
actionTypesByLegacyId: indexActionTypesByLegacyId(compilation.ontology.actionTypes)
|
|
1472
|
+
};
|
|
1473
|
+
}
|
|
1474
|
+
function tryCompileBusinessOntology(model, readinessProfile, issues) {
|
|
1475
|
+
try {
|
|
1476
|
+
return compileBusinessOntology(model, readinessProfile);
|
|
1477
|
+
} catch (error) {
|
|
1478
|
+
addReadinessIssue(
|
|
1479
|
+
issues,
|
|
1480
|
+
"SYSTEM_INTERFACE_INVALID",
|
|
1481
|
+
"ontology-compile-failed",
|
|
1482
|
+
error instanceof Error ? error.message : String(error)
|
|
1483
|
+
);
|
|
1484
|
+
return void 0;
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
function computeInterfaceReadiness(model, request) {
|
|
1488
|
+
const issues = [];
|
|
1489
|
+
const system = getSystem(model, request.systemPath);
|
|
1490
|
+
const systemInterface = getSystemInterfaceReadinessMarker(model, request);
|
|
1491
|
+
const readinessProfile = systemInterface === void 0 ? void 0 : profileForInterface(request.systemPath, request.interfaceKey, systemInterface.readinessProfile);
|
|
1492
|
+
const scopedResourceIds = systemInterface?.resourceIds ?? [];
|
|
1493
|
+
if (system === void 0) {
|
|
1494
|
+
addReadinessIssue(
|
|
1495
|
+
issues,
|
|
1496
|
+
"SYSTEM_INTERFACE_MISSING",
|
|
1497
|
+
"missing-system",
|
|
1498
|
+
`System "${request.systemPath}" is missing.`,
|
|
1499
|
+
{ ref: request.systemPath }
|
|
1500
|
+
);
|
|
1501
|
+
return { ready: false, systemPath: request.systemPath, interfaceKey: request.interfaceKey, scopedResourceIds, issues };
|
|
1502
|
+
}
|
|
1503
|
+
if (systemInterface === void 0) {
|
|
1504
|
+
addReadinessIssue(
|
|
1505
|
+
issues,
|
|
1506
|
+
"SYSTEM_INTERFACE_MISSING",
|
|
1507
|
+
"missing-interface",
|
|
1508
|
+
`System "${request.systemPath}" does not declare interface "${request.interfaceKey}".`,
|
|
1509
|
+
{ path: readinessMarkerPath(request) }
|
|
1510
|
+
);
|
|
1511
|
+
return { ready: false, systemPath: request.systemPath, interfaceKey: request.interfaceKey, scopedResourceIds, issues };
|
|
1512
|
+
}
|
|
1513
|
+
if (systemInterface.lifecycle !== "active") {
|
|
1514
|
+
addReadinessIssue(
|
|
1515
|
+
issues,
|
|
1516
|
+
"SYSTEM_INTERFACE_DISABLED",
|
|
1517
|
+
"inactive-interface",
|
|
1518
|
+
`System Interface "${formatInterfaceIdentity(request.systemPath, request.interfaceKey)}" lifecycle is "${systemInterface.lifecycle}".`,
|
|
1519
|
+
{ path: `${readinessMarkerPath(request)}.lifecycle` }
|
|
1520
|
+
);
|
|
1521
|
+
}
|
|
1522
|
+
const supportedProfile = readinessProfile !== void 0 && SYSTEM_INTERFACE_PROFILES.some((profile) => profile.readinessProfile === readinessProfile);
|
|
1523
|
+
if (!supportedProfile) {
|
|
1524
|
+
addReadinessIssue(
|
|
1525
|
+
issues,
|
|
1526
|
+
"SYSTEM_INTERFACE_INVALID",
|
|
1527
|
+
"unknown-readiness-profile",
|
|
1528
|
+
`System Interface "${formatInterfaceIdentity(request.systemPath, request.interfaceKey)}" references unknown readiness profile "${readinessProfile}". Supported profiles: ${formatSupportedReadinessProfiles()}. Custom Systems should not declare apiInterface; route custom behavior through workflows/operations plus ontology, resources, and topology.`,
|
|
1529
|
+
{ path: `${readinessMarkerPath(request)}.readinessProfile`, ref: readinessProfile }
|
|
1530
|
+
);
|
|
1531
|
+
return {
|
|
1532
|
+
ready: false,
|
|
1533
|
+
systemPath: request.systemPath,
|
|
1534
|
+
interfaceKey: request.interfaceKey,
|
|
1535
|
+
readinessProfile,
|
|
1536
|
+
scopedResourceIds,
|
|
1537
|
+
issues
|
|
1538
|
+
};
|
|
1539
|
+
}
|
|
1540
|
+
if (scopedResourceIds.length === 0) {
|
|
1541
|
+
addReadinessIssue(
|
|
1542
|
+
issues,
|
|
1543
|
+
"SYSTEM_INTERFACE_NOT_READY",
|
|
1544
|
+
"missing-scoped-resources",
|
|
1545
|
+
`System Interface "${formatInterfaceIdentity(request.systemPath, request.interfaceKey)}" must scope at least one active resource.`,
|
|
1546
|
+
{ path: `${readinessMarkerPath(request)}.resourceIds` }
|
|
1547
|
+
);
|
|
1548
|
+
}
|
|
1549
|
+
const checkedReadinessProfile = readinessProfile;
|
|
1550
|
+
if (checkedReadinessProfile === void 0) {
|
|
1551
|
+
throw new Error("Supported readiness profile unexpectedly resolved to undefined");
|
|
1552
|
+
}
|
|
1553
|
+
const resources = getActiveScopedResources(model, scopedResourceIds, issues, request);
|
|
1554
|
+
const index = tryCompileBusinessOntology(model, checkedReadinessProfile, issues);
|
|
1555
|
+
if (index !== void 0) {
|
|
1556
|
+
if (checkedReadinessProfile === LEAD_GEN_API_INTERFACE.readinessProfile) {
|
|
1557
|
+
requireLeadGenInterfaceReadiness(issues, index, resources, request);
|
|
1558
|
+
} else if (checkedReadinessProfile === CRM_API_INTERFACE.readinessProfile) {
|
|
1559
|
+
requireCrmInterfaceReadiness(issues, index, resources, request);
|
|
1560
|
+
} else if (checkedReadinessProfile === LEAD_GEN_CRM_HANDOFF_INTERFACE.readinessProfile) {
|
|
1561
|
+
requireLeadGenInterfaceReadiness(issues, index, resources, request);
|
|
1562
|
+
requireCrmInterfaceReadiness(issues, index, resources, { ...request, allowForeignOwner: true });
|
|
1563
|
+
requireHandoffBridgeReadiness(issues, model, request);
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
return {
|
|
1567
|
+
ready: issues.length === 0,
|
|
1568
|
+
systemPath: request.systemPath,
|
|
1569
|
+
interfaceKey: request.interfaceKey,
|
|
1570
|
+
readinessProfile,
|
|
1571
|
+
scopedResourceIds,
|
|
1572
|
+
issues
|
|
1573
|
+
};
|
|
1574
|
+
}
|
|
1575
|
+
function findLeadGenTemplateStepCatalog(index) {
|
|
1576
|
+
return Object.values(index.ontology.catalogTypes).find(
|
|
1577
|
+
(catalog) => catalog.ownerSystemId === "sales.lead-gen" && catalog.kind === "template-step" && catalog.appliesTo === LEAD_GEN_LIST_OBJECT_ONTOLOGY_ID
|
|
1578
|
+
);
|
|
1579
|
+
}
|
|
1580
|
+
function indexActionTypesByLegacyId(actionTypes) {
|
|
1581
|
+
const byLegacyId = {};
|
|
1582
|
+
for (const actionType of Object.values(actionTypes)) {
|
|
1583
|
+
const legacyActionId = actionType["legacyActionId"];
|
|
1584
|
+
if (typeof legacyActionId === "string") {
|
|
1585
|
+
byLegacyId[legacyActionId] = actionType;
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
return byLegacyId;
|
|
1589
|
+
}
|
|
1590
|
+
function getCatalogEntries(catalog) {
|
|
1591
|
+
return isPlainRecord(catalog.entries) ? catalog.entries : {};
|
|
1592
|
+
}
|
|
1593
|
+
|
|
1594
|
+
// ../core/src/platform/registry/resource-metadata.ts
|
|
1595
|
+
var resourceTypeIconNames = {
|
|
1596
|
+
agent: "IconBrain",
|
|
1597
|
+
workflow: "IconGitBranch",
|
|
1598
|
+
trigger: "IconBolt",
|
|
1599
|
+
integration: "IconPlug",
|
|
1600
|
+
external: "IconExternalLink",
|
|
1601
|
+
human: "IconUser"
|
|
1602
|
+
};
|
|
1603
|
+
var resourceTypeColors = {
|
|
1604
|
+
agent: "violet",
|
|
1605
|
+
workflow: "blue",
|
|
1606
|
+
trigger: "orange",
|
|
1607
|
+
integration: "teal",
|
|
1608
|
+
external: "gray",
|
|
1609
|
+
human: "yellow"
|
|
1610
|
+
};
|
|
1611
|
+
function getResourceIconName(type) {
|
|
1612
|
+
return resourceTypeIconNames[type];
|
|
1613
|
+
}
|
|
1614
|
+
function getResourceColor(type) {
|
|
1615
|
+
return resourceTypeColors[type];
|
|
1616
|
+
}
|
|
1617
|
+
|
|
1618
|
+
// ../core/src/operations/observability/utils.ts
|
|
1619
|
+
function getTimeRangeDates(range) {
|
|
1620
|
+
const end = /* @__PURE__ */ new Date();
|
|
1621
|
+
const start = /* @__PURE__ */ new Date();
|
|
1622
|
+
switch (range) {
|
|
1623
|
+
case "1h":
|
|
1624
|
+
start.setHours(start.getHours() - 1);
|
|
1625
|
+
break;
|
|
1626
|
+
case "24h":
|
|
1627
|
+
start.setHours(start.getHours() - 24);
|
|
1628
|
+
break;
|
|
1629
|
+
case "7d":
|
|
1630
|
+
start.setDate(start.getDate() - 7);
|
|
1631
|
+
break;
|
|
1632
|
+
case "30d":
|
|
1633
|
+
start.setDate(start.getDate() - 30);
|
|
1634
|
+
break;
|
|
1635
|
+
}
|
|
1636
|
+
return {
|
|
1637
|
+
startDate: start.toISOString(),
|
|
1638
|
+
endDate: end.toISOString()
|
|
1639
|
+
};
|
|
1640
|
+
}
|
|
1641
|
+
function getTimeRangeLabel(range) {
|
|
1642
|
+
switch (range) {
|
|
1643
|
+
case "1h":
|
|
1644
|
+
return "Last 1 hour";
|
|
1645
|
+
case "24h":
|
|
1646
|
+
return "Last 24 hours";
|
|
1647
|
+
case "7d":
|
|
1648
|
+
return "Last 7 days";
|
|
1649
|
+
case "30d":
|
|
1650
|
+
return "Last 30 days";
|
|
1651
|
+
}
|
|
1652
|
+
}
|
|
1653
|
+
function formatBucketTime(timestamp, granularity) {
|
|
1654
|
+
const date = new Date(timestamp);
|
|
1655
|
+
if (granularity === "hour") {
|
|
1656
|
+
return date.toLocaleTimeString("en-US", {
|
|
1657
|
+
hour: "2-digit",
|
|
1658
|
+
minute: "2-digit",
|
|
1659
|
+
hour12: false
|
|
1660
|
+
});
|
|
1661
|
+
}
|
|
1662
|
+
return date.toLocaleDateString("en-US", {
|
|
1663
|
+
month: "short",
|
|
1664
|
+
day: "numeric"
|
|
1665
|
+
});
|
|
1666
|
+
}
|
|
1667
|
+
|
|
1668
|
+
// src/utils/resource-icons.tsx
|
|
1669
|
+
var iconNameToComponent = {
|
|
1670
|
+
IconBrain,
|
|
1671
|
+
IconGitBranch,
|
|
1672
|
+
IconBolt,
|
|
1673
|
+
IconPlug,
|
|
1674
|
+
IconExternalLink,
|
|
1675
|
+
IconUser
|
|
1676
|
+
};
|
|
1677
|
+
function getResourceIcon(type) {
|
|
1678
|
+
const iconName = getResourceIconName(type);
|
|
1679
|
+
return iconNameToComponent[iconName];
|
|
1680
|
+
}
|
|
1681
|
+
|
|
1682
|
+
// src/utils/constants/api.ts
|
|
1683
|
+
var API_URL = import.meta.env.PROD ? "https://api.elevasis.io" : "http://localhost:5170";
|
|
1684
|
+
|
|
1685
|
+
// src/utils/constants/cache.ts
|
|
1686
|
+
var STALE_TIME_MONITORING = 3e4;
|
|
1687
|
+
var STALE_TIME_ADMIN = 6e4;
|
|
1688
|
+
var STALE_TIME_DEFAULT = 3e5;
|
|
1689
|
+
var GC_TIME_SHORT = 3e5;
|
|
1690
|
+
var GC_TIME_MEDIUM = 6e5;
|
|
1691
|
+
var GC_TIME_LONG = 18e5;
|
|
1692
|
+
|
|
1693
|
+
// src/utils/constants/polling.ts
|
|
1694
|
+
var REFETCH_INTERVAL_DASHBOARD = 6e4;
|
|
1695
|
+
var REFETCH_INTERVAL_REALTIME = 3e4;
|
|
1696
|
+
var REFETCH_INTERVAL_RUNNING = 2e3;
|
|
1697
|
+
var REFETCH_INTERVAL_RUNNING_FAST = 1e3;
|
|
1698
|
+
|
|
1699
|
+
// src/utils/constants/reconnection.ts
|
|
1700
|
+
var SSE_TOKEN_REFRESH_DELAY = 2e3;
|
|
1701
|
+
var SSE_CLOSE_GRACE_PERIOD = 5e3;
|
|
1702
|
+
var WS_RECONNECT_BASE_DELAY = 1e3;
|
|
1703
|
+
var WS_RECONNECT_MAX_DELAY = 3e4;
|
|
1704
|
+
var WS_MAX_RETRIES_BEFORE_ERROR = 3;
|
|
1705
|
+
|
|
1706
|
+
// src/utils/constants/ui.ts
|
|
1707
|
+
var PAGE_SIZE_DEFAULT = 20;
|
|
1708
|
+
var LIMIT_ACTIVITY_FEED = 50;
|
|
1709
|
+
var DEBOUNCE_FILTER = 150;
|
|
1710
|
+
var DEBOUNCE_SLIDER = 500;
|
|
1711
|
+
var OAUTH_FLOW_TIMEOUT = 3e5;
|
|
1712
|
+
|
|
1713
|
+
// src/utils/suppress-warnings.ts
|
|
1714
|
+
var SUPPRESSED_WARNINGS = [
|
|
1715
|
+
// Mantine SegmentedControl data-orientation deprecation warnings
|
|
1716
|
+
/Unsupported style property.*data-orientation/i,
|
|
1717
|
+
/Did you mean.*dataOrientation/i,
|
|
1718
|
+
// Recharts/Mantine Charts dimension warnings (charts work fine after layout completes)
|
|
1719
|
+
/The width\(-1\) and height\(-1\) of chart should be greater than 0/i
|
|
1720
|
+
];
|
|
1721
|
+
var originalConsoleError = console.error;
|
|
1722
|
+
var originalConsoleWarn = console.warn;
|
|
1723
|
+
function suppressKnownWarnings() {
|
|
1724
|
+
console.error = (...args) => {
|
|
1725
|
+
const message = args.join(" ");
|
|
1726
|
+
const shouldSuppress = SUPPRESSED_WARNINGS.some((pattern) => pattern.test(message));
|
|
1727
|
+
if (!shouldSuppress) {
|
|
1728
|
+
originalConsoleError(...args);
|
|
1729
|
+
}
|
|
1730
|
+
};
|
|
1731
|
+
console.warn = (...args) => {
|
|
1732
|
+
const message = args.join(" ");
|
|
1733
|
+
const shouldSuppress = SUPPRESSED_WARNINGS.some((pattern) => pattern.test(message));
|
|
1734
|
+
if (!shouldSuppress) {
|
|
1735
|
+
originalConsoleWarn(...args);
|
|
1736
|
+
}
|
|
1737
|
+
};
|
|
1738
|
+
}
|
|
1739
|
+
function restoreConsole() {
|
|
1740
|
+
console.error = originalConsoleError;
|
|
1741
|
+
console.warn = originalConsoleWarn;
|
|
1742
|
+
}
|
|
1743
|
+
|
|
1744
|
+
// src/utils/browser-mocks.ts
|
|
1745
|
+
function setupMatchMedia() {
|
|
1746
|
+
const win = globalThis.window;
|
|
1747
|
+
if (typeof win === "undefined") return;
|
|
1748
|
+
Object.defineProperty(win, "matchMedia", {
|
|
1749
|
+
writable: true,
|
|
1750
|
+
value: (query) => ({
|
|
1751
|
+
matches: false,
|
|
1752
|
+
media: query,
|
|
1753
|
+
onchange: null,
|
|
1754
|
+
addListener: () => {
|
|
1755
|
+
},
|
|
1756
|
+
removeListener: () => {
|
|
1757
|
+
},
|
|
1758
|
+
addEventListener: () => {
|
|
1759
|
+
},
|
|
1760
|
+
removeEventListener: () => {
|
|
1761
|
+
},
|
|
1762
|
+
dispatchEvent: () => true
|
|
1763
|
+
})
|
|
1764
|
+
});
|
|
1765
|
+
}
|
|
1766
|
+
function setupResizeObserver() {
|
|
1767
|
+
globalThis.ResizeObserver = class ResizeObserver {
|
|
1768
|
+
observe() {
|
|
1769
|
+
}
|
|
1770
|
+
unobserve() {
|
|
1771
|
+
}
|
|
1772
|
+
disconnect() {
|
|
1773
|
+
}
|
|
1774
|
+
};
|
|
1775
|
+
}
|
|
1776
|
+
function setupBrowserMocks() {
|
|
1777
|
+
setupMatchMedia();
|
|
1778
|
+
setupResizeObserver();
|
|
1779
|
+
}
|
|
1780
|
+
|
|
1781
|
+
export { APIClientError, API_URL, ActionInvocationSchema, ActionSchema, ActionsDomainSchema, DEBOUNCE_FILTER, DEBOUNCE_SLIDER, DEFAULT_ONTOLOGY_SCOPE, DEFAULT_ORGANIZATION_MODEL_ACTIONS, DEFAULT_ORGANIZATION_MODEL_SYSTEMS, DescriptionSchema, EntitiesDomainSchema, EntitySchema, GC_TIME_LONG, GC_TIME_MEDIUM, GC_TIME_SHORT, IconNameSchema, JsonValueSchema, LEAD_GEN_API_INTERFACE, LIMIT_ACTIVITY_FEED, LabelSchema, ModelIdSchema, NodeIdStringSchema, OAUTH_FLOW_TIMEOUT, OntologyIdSchema, OntologyScopeSchema, PAGE_SIZE_DEFAULT, PathSchema, REFETCH_INTERVAL_DASHBOARD, REFETCH_INTERVAL_REALTIME, REFETCH_INTERVAL_RUNNING, REFETCH_INTERVAL_RUNNING_FAST, SSE_CLOSE_GRACE_PERIOD, SSE_TOKEN_REFRESH_DELAY, STALE_TIME_ADMIN, STALE_TIME_DEFAULT, STALE_TIME_MONITORING, SystemEntrySchema, SystemIdSchema, SystemInterfaceRefSchema, SystemLifecycleSchema, SystemPathSchema, SystemsDomainSchema, WS_MAX_RETRIES_BEFORE_ERROR, WS_RECONNECT_BASE_DELAY, WS_RECONNECT_MAX_DELAY, ancestorsOf, childrenOf, compileOrganizationOntology, computeInterfaceReadiness, debounce, defaultPathFor, defineActions, defineEntities, devOnlyFor, findById, findByPath, formatBucketTime, formatChartAxisDate, formatDate, formatDateTime, formatErrorMessage, formatRelativeTime, formatTimeAgo, getAllBuildTemplates, getAllPipelines, getAllProjectStatuses, getAllProspectingStages, getErrorInfo, getErrorTitle, getLeadGenStageCatalog, getResourceColor, getResourceIcon, getResourcesForSystem, getSystem, getSystemAncestors, getTimeRangeDates, getTimeRangeLabel, isAPIClientError, listAllSystems, listResolvedOntologyRecords, ontologyGraphNodeId, parentOf, parseOntologyId, requiresAdminFor, resolveSystemConfig, restoreConsole, setupBrowserMocks, suppressKnownWarnings, topLevel, validateEmail };
|