@uipath/packager-tool-datafabric 1.201.0-preview.134

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.js ADDED
@@ -0,0 +1,815 @@
1
+ // src/pack/entity-project-tool.ts
2
+ import {
3
+ ProjectTool,
4
+ ToolResult
5
+ } from "@uipath/solutionpackager-tool-core";
6
+
7
+ // ../../common/dist/entity-name-rules.js
8
+ var RESERVED_SQL_KEYWORDS_LOWER = new Set(["group", "order"]);
9
+ var RESERVED_FIELD_NAMES_LOWER = new Set([
10
+ "id",
11
+ "createdby",
12
+ "createtime",
13
+ "updatedby",
14
+ "updatetime",
15
+ "recordowner",
16
+ "version"
17
+ ]);
18
+ var RESERVED_KEYWORDS_LOWER = new Set([
19
+ "abstract",
20
+ "as",
21
+ "base",
22
+ "bool",
23
+ "break",
24
+ "byte",
25
+ "case",
26
+ "catch",
27
+ "char",
28
+ "checked",
29
+ "class",
30
+ "const",
31
+ "continue",
32
+ "decimal",
33
+ "default",
34
+ "delegate",
35
+ "do",
36
+ "double",
37
+ "else",
38
+ "enum",
39
+ "event",
40
+ "explicit",
41
+ "extern",
42
+ "false",
43
+ "finally",
44
+ "fixed",
45
+ "float",
46
+ "for",
47
+ "foreach",
48
+ "goto",
49
+ "if",
50
+ "implicit",
51
+ "in",
52
+ "int",
53
+ "interface",
54
+ "internal",
55
+ "is",
56
+ "lock",
57
+ "long",
58
+ "namespace",
59
+ "new",
60
+ "null",
61
+ "object",
62
+ "operator",
63
+ "out",
64
+ "override",
65
+ "params",
66
+ "private",
67
+ "protected",
68
+ "public",
69
+ "readonly",
70
+ "ref",
71
+ "return",
72
+ "sbyte",
73
+ "sealed",
74
+ "short",
75
+ "sizeof",
76
+ "stackalloc",
77
+ "static",
78
+ "string",
79
+ "struct",
80
+ "switch",
81
+ "this",
82
+ "throw",
83
+ "true",
84
+ "try",
85
+ "typeof",
86
+ "uint",
87
+ "ulong",
88
+ "unchecked",
89
+ "unsafe",
90
+ "ushort",
91
+ "using",
92
+ "virtual",
93
+ "void",
94
+ "volatile",
95
+ "while",
96
+ "addhandler",
97
+ "addressof",
98
+ "alias",
99
+ "and",
100
+ "andalso",
101
+ "boolean",
102
+ "byref",
103
+ "byval",
104
+ "call",
105
+ "cbool",
106
+ "cbyte",
107
+ "cchar",
108
+ "cdate",
109
+ "cdbl",
110
+ "cdec",
111
+ "cint",
112
+ "clng",
113
+ "cobj",
114
+ "csbyte",
115
+ "cshort",
116
+ "csng",
117
+ "cstr",
118
+ "ctype",
119
+ "cuint",
120
+ "culng",
121
+ "cushort",
122
+ "date",
123
+ "declare",
124
+ "dim",
125
+ "directcast",
126
+ "each",
127
+ "elseif",
128
+ "end",
129
+ "endif",
130
+ "erase",
131
+ "error",
132
+ "exit",
133
+ "friend",
134
+ "function",
135
+ "get",
136
+ "gettype",
137
+ "getxmlnamespace",
138
+ "global",
139
+ "gosub",
140
+ "goto",
141
+ "handles",
142
+ "implements",
143
+ "imports",
144
+ "inherits",
145
+ "integer",
146
+ "isnot",
147
+ "let",
148
+ "lib",
149
+ "like",
150
+ "loop",
151
+ "me",
152
+ "mod",
153
+ "module",
154
+ "mustinherit",
155
+ "mustoverride",
156
+ "mybase",
157
+ "myclass",
158
+ "narrowing",
159
+ "next",
160
+ "not",
161
+ "nothing",
162
+ "notinheritable",
163
+ "notoverridable",
164
+ "of",
165
+ "on",
166
+ "option",
167
+ "optional",
168
+ "or",
169
+ "orelse",
170
+ "overloads",
171
+ "overridable",
172
+ "overrides",
173
+ "paramarray",
174
+ "partial",
175
+ "property",
176
+ "raiseevent",
177
+ "redim",
178
+ "rem",
179
+ "removehandler",
180
+ "resume",
181
+ "select",
182
+ "set",
183
+ "shadows",
184
+ "shared",
185
+ "single",
186
+ "step",
187
+ "stop",
188
+ "structure",
189
+ "sub",
190
+ "synclock",
191
+ "then",
192
+ "to",
193
+ "trycast",
194
+ "uinteger",
195
+ "ushort",
196
+ "variant",
197
+ "wend",
198
+ "when",
199
+ "widening",
200
+ "with",
201
+ "withevents",
202
+ "writeonly",
203
+ "xor"
204
+ ]);
205
+ function findReservedNameError(name, kind) {
206
+ if (kind === "field" && RESERVED_FIELD_NAMES_LOWER.has(name.toLowerCase())) {
207
+ return {
208
+ message: `Field name '${name}' is reserved`,
209
+ instructions: "The following field names are reserved by the platform (case-insensitive): Id, CreatedBy, CreateTime, UpdatedBy, UpdateTime, RecordOwner, Version. Pick a different name."
210
+ };
211
+ }
212
+ if (RESERVED_KEYWORDS_LOWER.has(name.toLowerCase())) {
213
+ return {
214
+ message: `${kind === "entity" ? "Entity" : kind === "field" ? "Field" : "Choice set"} name '${name}' is a reserved C# or VB keyword`,
215
+ instructions: "Reserved keywords are rejected with RESERVED_LANGUAGE_KEYWORDS. Pick a domain-specific rename, for example 'Case' -> 'WorkItem', 'Class' -> 'Category', 'New' -> 'IsNew'."
216
+ };
217
+ }
218
+ if (kind === "entity" && RESERVED_SQL_KEYWORDS_LOWER.has(name.toLowerCase())) {
219
+ return {
220
+ message: `Entity name '${name}' is a reserved SQL keyword`,
221
+ instructions: "The platform rejects 'Order' and 'Group' as entity names. Pick a domain-specific name such as 'PurchaseOrder' or 'TeamGroup'."
222
+ };
223
+ }
224
+ return null;
225
+ }
226
+
227
+ // ../../../node_modules/@uipath/entity-modeler/dist/chunk-FJZXCOIG.js
228
+ var SqlTypeName = {
229
+ UNIQUEIDENTIFIER: "UNIQUEIDENTIFIER",
230
+ NVARCHAR: "NVARCHAR",
231
+ INT: "INT",
232
+ DATETIME2: "DATETIME2",
233
+ DATETIMEOFFSET: "DATETIMEOFFSET",
234
+ FLOAT: "FLOAT",
235
+ REAL: "REAL",
236
+ BIGINT: "BIGINT",
237
+ DATE: "DATE",
238
+ BIT: "BIT",
239
+ DECIMAL: "DECIMAL",
240
+ MULTILINE: "MULTILINE",
241
+ MULTILINE_MAX: "MULTILINE_MAX",
242
+ UNKNOWN: "UNKNOWN"
243
+ };
244
+ var FieldDisplayType = {
245
+ Basic: "Basic",
246
+ Relationship: "Relationship",
247
+ File: "File",
248
+ ChoiceSetSingle: "ChoiceSetSingle",
249
+ ChoiceSetMultiple: "ChoiceSetMultiple",
250
+ AutoNumber: "AutoNumber",
251
+ MultiLineText: "MultiLineText"
252
+ };
253
+ var NAME_PATTERN = /^[a-zA-Z][a-zA-Z0-9]{2,99}$/;
254
+ var GA_TYPE_PAIRS = [
255
+ [FieldDisplayType.Basic, SqlTypeName.NVARCHAR],
256
+ [FieldDisplayType.Basic, SqlTypeName.DECIMAL],
257
+ [FieldDisplayType.Basic, SqlTypeName.FLOAT],
258
+ [FieldDisplayType.Basic, SqlTypeName.BIT],
259
+ [FieldDisplayType.Basic, SqlTypeName.DATE],
260
+ [FieldDisplayType.Basic, SqlTypeName.DATETIMEOFFSET],
261
+ [FieldDisplayType.Basic, SqlTypeName.UNIQUEIDENTIFIER],
262
+ [FieldDisplayType.AutoNumber, SqlTypeName.DECIMAL],
263
+ [FieldDisplayType.Basic, SqlTypeName.MULTILINE],
264
+ [FieldDisplayType.Basic, SqlTypeName.MULTILINE_MAX],
265
+ [FieldDisplayType.File, SqlTypeName.UNIQUEIDENTIFIER]
266
+ ];
267
+ var ALLOWED_TYPE_PAIRS = new Set(GA_TYPE_PAIRS.map(([display, sql]) => `${display}:${sql}`));
268
+ // src/schema/create-entity-skeleton.ts
269
+ var UNASSIGNED_FOLDER_ID = "99999999-9999-9999-9999-999999999999";
270
+ var SYSTEM_FIELD_DEFAULTS = {
271
+ IsPrimaryKey: false,
272
+ IsForeignKey: false,
273
+ IsExternalField: false,
274
+ IsHiddenField: true,
275
+ FieldCategoryId: 0,
276
+ IsUnique: false,
277
+ ReferenceType: "ManyToOne",
278
+ Transformation: null,
279
+ IsRequired: false,
280
+ IsEncrypted: false,
281
+ Description: "",
282
+ IsSystemField: true,
283
+ FieldDisplayType: FieldDisplayType.Basic,
284
+ IsAttachment: false,
285
+ IsRbacEnabled: false,
286
+ IsModelReserved: false
287
+ };
288
+ var SYSTEM_FIELD_TEMPLATES = [
289
+ {
290
+ ...SYSTEM_FIELD_DEFAULTS,
291
+ Name: "Id",
292
+ DisplayName: "Id",
293
+ IsPrimaryKey: true,
294
+ SqlType: { Name: SqlTypeName.UNIQUEIDENTIFIER }
295
+ },
296
+ {
297
+ ...SYSTEM_FIELD_DEFAULTS,
298
+ Name: "CreateTime",
299
+ DisplayName: "CreateTime",
300
+ SqlType: { Name: SqlTypeName.DATETIMEOFFSET }
301
+ },
302
+ {
303
+ ...SYSTEM_FIELD_DEFAULTS,
304
+ Name: "CreatedBy",
305
+ DisplayName: "CreatedBy",
306
+ FieldDisplayType: FieldDisplayType.Relationship,
307
+ SqlType: { Name: SqlTypeName.UNIQUEIDENTIFIER }
308
+ },
309
+ {
310
+ ...SYSTEM_FIELD_DEFAULTS,
311
+ Name: "UpdateTime",
312
+ DisplayName: "UpdateTime",
313
+ SqlType: { Name: SqlTypeName.DATETIMEOFFSET }
314
+ },
315
+ {
316
+ ...SYSTEM_FIELD_DEFAULTS,
317
+ Name: "UpdatedBy",
318
+ DisplayName: "UpdatedBy",
319
+ FieldDisplayType: FieldDisplayType.Relationship,
320
+ SqlType: { Name: SqlTypeName.UNIQUEIDENTIFIER }
321
+ }
322
+ ];
323
+ function createEntitySkeleton(name) {
324
+ return {
325
+ Id: globalThis.crypto.randomUUID(),
326
+ Name: name,
327
+ DisplayName: name,
328
+ EntityTypeId: 0,
329
+ EntityType: "Entity",
330
+ Description: "",
331
+ FolderId: UNASSIGNED_FOLDER_ID,
332
+ Fields: SYSTEM_FIELD_TEMPLATES.map((template) => ({
333
+ ...template,
334
+ SqlType: { ...template.SqlType }
335
+ })),
336
+ Data: null,
337
+ ExternalFields: null,
338
+ SourceJoinCriterias: null,
339
+ RecordCount: 0,
340
+ StorageSizeInMB: null,
341
+ UsedStorageSizeInMB: null,
342
+ AttachmentSizeInByte: null,
343
+ IsRbacEnabled: false,
344
+ InvalidIdentifiers: [],
345
+ IsModelReserved: false,
346
+ CategoryId: null
347
+ };
348
+ }
349
+
350
+ // src/schema/validate-entity.ts
351
+ var DIAGNOSTIC_CODES = {
352
+ entityNameInvalid: "ENTITY_NAME_INVALID",
353
+ entityNameNotUnique: "ENTITY_NAME_NOT_UNIQUE",
354
+ fieldNameInvalid: "FIELD_NAME_INVALID",
355
+ duplicateFieldName: "DUPLICATE_FIELD_NAME",
356
+ fieldTypeNotAllowed: "FIELD_TYPE_NOT_ALLOWED",
357
+ nameReserved: "NAME_RESERVED",
358
+ systemFieldMissing: "SYSTEM_FIELD_MISSING",
359
+ systemFieldModified: "SYSTEM_FIELD_MODIFIED"
360
+ };
361
+ function validateEntity(entity, ctx) {
362
+ const diagnostics = [];
363
+ if (!NAME_PATTERN.test(entity.Name)) {
364
+ diagnostics.push({
365
+ code: DIAGNOSTIC_CODES.entityNameInvalid,
366
+ message: `Entity name '${entity.Name}' must start with a letter and contain 3-100 letters or digits.`,
367
+ severity: "error"
368
+ });
369
+ }
370
+ const reservedEntityName = findReservedNameError(entity.Name, "entity");
371
+ if (reservedEntityName) {
372
+ diagnostics.push({
373
+ code: DIAGNOSTIC_CODES.nameReserved,
374
+ message: `${reservedEntityName.message}. ${reservedEntityName.instructions}`,
375
+ severity: "error"
376
+ });
377
+ }
378
+ const entityNameLower = entity.Name.toLowerCase();
379
+ if (ctx.siblingNames.some((sibling) => sibling.toLowerCase() === entityNameLower)) {
380
+ diagnostics.push({
381
+ code: DIAGNOSTIC_CODES.entityNameNotUnique,
382
+ message: `An entity named '${entity.Name}' already exists in this solution. Entity names are solution-wide unique.`,
383
+ severity: "error"
384
+ });
385
+ }
386
+ const seenFieldNames = new Map;
387
+ for (const field of entity.Fields) {
388
+ if (!field.IsSystemField && !NAME_PATTERN.test(field.Name)) {
389
+ diagnostics.push({
390
+ code: DIAGNOSTIC_CODES.fieldNameInvalid,
391
+ message: `Field name '${field.Name}' must start with a letter and contain 3-100 letters or digits.`,
392
+ severity: "error",
393
+ fieldName: field.Name
394
+ });
395
+ }
396
+ if (!field.IsSystemField) {
397
+ const reservedFieldName = findReservedNameError(field.Name, "field");
398
+ if (reservedFieldName) {
399
+ diagnostics.push({
400
+ code: DIAGNOSTIC_CODES.nameReserved,
401
+ message: `${reservedFieldName.message}. ${reservedFieldName.instructions}`,
402
+ severity: "error",
403
+ fieldName: field.Name
404
+ });
405
+ }
406
+ }
407
+ const fieldNameLower = field.Name.toLowerCase();
408
+ const firstWithName = seenFieldNames.get(fieldNameLower);
409
+ if (firstWithName !== undefined) {
410
+ diagnostics.push({
411
+ code: DIAGNOSTIC_CODES.duplicateFieldName,
412
+ message: `Duplicate field name '${field.Name}' (names are case-insensitive; first defined as '${firstWithName}').`,
413
+ severity: "error",
414
+ fieldName: field.Name
415
+ });
416
+ } else {
417
+ seenFieldNames.set(fieldNameLower, field.Name);
418
+ }
419
+ if (!field.IsSystemField && !ALLOWED_TYPE_PAIRS.has(`${field.FieldDisplayType}:${field.SqlType.Name}`)) {
420
+ diagnostics.push({
421
+ code: DIAGNOSTIC_CODES.fieldTypeNotAllowed,
422
+ message: `Field '${field.Name}' uses an unsupported type combination ` + `(${field.FieldDisplayType} / ${field.SqlType.Name}).`,
423
+ severity: "error",
424
+ fieldName: field.Name
425
+ });
426
+ }
427
+ }
428
+ diagnostics.push(...validateSystemFields(entity));
429
+ return diagnostics;
430
+ }
431
+ var SERVER_ASSIGNED_FIELD_PROPS = new Set([
432
+ "Id",
433
+ "CreateTime",
434
+ "CreatedBy",
435
+ "UpdateTime",
436
+ "UpdatedBy"
437
+ ]);
438
+ function isUnmodifiedSystemField(template, actual) {
439
+ const equal = (left, right) => JSON.stringify(left ?? null) === JSON.stringify(right ?? null);
440
+ for (const key of Object.keys(template)) {
441
+ if (!equal(template[key], actual[key])) {
442
+ return false;
443
+ }
444
+ }
445
+ return Object.keys(actual).every((key) => (key in template) || SERVER_ASSIGNED_FIELD_PROPS.has(key));
446
+ }
447
+ function validateSystemFields(entity) {
448
+ const diagnostics = [];
449
+ for (const template of SYSTEM_FIELD_TEMPLATES) {
450
+ const actual = entity.Fields.find((field) => field.Name === template.Name);
451
+ if (!actual) {
452
+ diagnostics.push({
453
+ code: DIAGNOSTIC_CODES.systemFieldMissing,
454
+ message: `System field '${template.Name}' is missing. System fields cannot be removed.`,
455
+ severity: "error",
456
+ fieldName: template.Name
457
+ });
458
+ continue;
459
+ }
460
+ if (!isUnmodifiedSystemField(template, actual)) {
461
+ diagnostics.push({
462
+ code: DIAGNOSTIC_CODES.systemFieldModified,
463
+ message: `System field '${template.Name}' was modified. System fields are read-only.`,
464
+ severity: "error",
465
+ fieldName: template.Name
466
+ });
467
+ }
468
+ }
469
+ return diagnostics;
470
+ }
471
+
472
+ // src/pack/constants.ts
473
+ var ENTITY_EXTENSION_CONST = ".entity";
474
+ var ENTITY_VALIDATION_FAILED = "ENTITY_VALIDATION_FAILED";
475
+ var PACK_DIAGNOSTIC_CODES = {
476
+ misplacedMainFile: "MISPLACED_MAIN_FILE",
477
+ noParentSolution: "NO_PARENT_SOLUTION"
478
+ };
479
+
480
+ // src/resource/unified-resource-file.ts
481
+ var ENTITY_RESOURCE_KIND = "entity";
482
+ var ENTITY_RESOURCE_TYPE = "native";
483
+ var ENTITY_RESOURCE_API_VERSION = "dataservice.uipath.com/v2";
484
+ var ENTITY_RESOURCE_DOC_VERSION = "1.0.0";
485
+ var SOLUTION_FOLDER = "solution_folder";
486
+ function entityResourcePath(entityName) {
487
+ return `resources/${SOLUTION_FOLDER}/entity/native/${entityName}.json`;
488
+ }
489
+
490
+ // src/resource/resource-io.ts
491
+ function serialize(value) {
492
+ return `${JSON.stringify(value, null, 2)}
493
+ `;
494
+ }
495
+ function serializeEntityJson(entity) {
496
+ return JSON.stringify(entity);
497
+ }
498
+
499
+ class EntityResourceError extends Error {
500
+ failure;
501
+ constructor(failure, message) {
502
+ super(message);
503
+ this.failure = failure;
504
+ this.name = "EntityResourceError";
505
+ }
506
+ }
507
+ function wrapEntityJson(entity) {
508
+ return {
509
+ docVersion: ENTITY_RESOURCE_DOC_VERSION,
510
+ resource: {
511
+ name: entity.Name,
512
+ kind: ENTITY_RESOURCE_KIND,
513
+ type: ENTITY_RESOURCE_TYPE,
514
+ apiVersion: ENTITY_RESOURCE_API_VERSION,
515
+ isOverridable: true,
516
+ dependencies: [],
517
+ runtimeDependencies: [],
518
+ folders: [{ fullyQualifiedName: SOLUTION_FOLDER }],
519
+ spec: {
520
+ resourceJson: serializeEntityJson(entity),
521
+ name: entity.Name,
522
+ displayName: entity.DisplayName,
523
+ description: entity.Description
524
+ },
525
+ locks: [],
526
+ key: entity.Id,
527
+ files: []
528
+ }
529
+ };
530
+ }
531
+ function unwrapResource(file) {
532
+ if (typeof file !== "object" || file === null || Array.isArray(file)) {
533
+ throw new EntityResourceError("NotAnObject", "Resource file is not a JSON object.");
534
+ }
535
+ const resource = file.resource;
536
+ if (typeof resource !== "object" || resource === null) {
537
+ throw new EntityResourceError("MissingResource", "Resource file has no 'resource' object.");
538
+ }
539
+ if (resource.kind !== ENTITY_RESOURCE_KIND) {
540
+ throw new EntityResourceError("NotAnEntityResource", `Resource kind is '${String(resource.kind)}', expected '${ENTITY_RESOURCE_KIND}'.`);
541
+ }
542
+ const spec = resource.spec;
543
+ if (typeof spec !== "object" || spec === null || typeof spec.resourceJson !== "string") {
544
+ throw new EntityResourceError("MissingSpec", "Resource file has no 'spec.resourceJson' string.");
545
+ }
546
+ try {
547
+ return JSON.parse(spec.resourceJson);
548
+ } catch {
549
+ throw new EntityResourceError("ResourceJsonUnparsable", "'spec.resourceJson' is not valid JSON.");
550
+ }
551
+ }
552
+ function updateResourceSchema(file, entity) {
553
+ return {
554
+ ...file,
555
+ resource: {
556
+ ...file.resource,
557
+ name: entity.Name,
558
+ spec: {
559
+ ...file.resource.spec,
560
+ resourceJson: serializeEntityJson(entity),
561
+ name: entity.Name,
562
+ displayName: entity.DisplayName,
563
+ description: entity.Description
564
+ }
565
+ }
566
+ };
567
+ }
568
+
569
+ // src/pack/verify-pair.ts
570
+ async function verifyPair(stubFile, solutionDir, fs) {
571
+ const parsedStub = await readStub(stubFile, fs);
572
+ if ("failure" in parsedStub) {
573
+ return { ok: false, failure: parsedStub.failure };
574
+ }
575
+ const stub = parsedStub.stub;
576
+ const baseName = fs.path.basename(stubFile, ENTITY_EXTENSION_CONST);
577
+ if (baseName !== stub.name) {
578
+ return fail("StubFileNameMismatch", `Stub file '${baseName}${ENTITY_EXTENSION_CONST}' names entity '${stub.name}' — the filename and the entity name must match.`);
579
+ }
580
+ const solutionRoot = fs.path.resolve(solutionDir);
581
+ const resourceFile = fs.path.resolve(solutionRoot, ...stub.resourcePath.split("/"));
582
+ const boundary = resourceFile.charAt(solutionRoot.length);
583
+ if (!resourceFile.startsWith(solutionRoot) || boundary !== "/" && boundary !== "\\") {
584
+ return fail("ResourcePathEscapes", `'${stub.name}': resourcePath '${stub.resourcePath}' points outside the solution.`);
585
+ }
586
+ const parsedResource = await readResource(resourceFile, stub.name, stub.resourcePath, fs);
587
+ if ("failure" in parsedResource) {
588
+ return { ok: false, failure: parsedResource.failure };
589
+ }
590
+ const resourceRaw = parsedResource.raw;
591
+ let entity;
592
+ try {
593
+ entity = unwrapResource(resourceRaw);
594
+ } catch (error) {
595
+ const detail = error instanceof EntityResourceError ? ` (${error.failure})` : "";
596
+ return fail("ResourceInvalid", `'${stub.name}': resource '${stub.resourcePath}' is not a valid entity resource${detail}.`);
597
+ }
598
+ if (entity.Name !== stub.name) {
599
+ return fail("NameCoupling", `Stub '${stub.name}' points at a resource defining entity '${entity.Name}' — the names must match.`);
600
+ }
601
+ return { ok: true, entityName: stub.name, resourceFile, entity };
602
+ }
603
+ function fail(code, message) {
604
+ return { ok: false, failure: { code, message } };
605
+ }
606
+ async function readStub(stubFile, fs) {
607
+ let parsed;
608
+ try {
609
+ const raw = await fs.readFile(stubFile);
610
+ if (raw === null) {
611
+ return failure("StubUnparsable", `'${stubFile}' could not be read.`);
612
+ }
613
+ parsed = JSON.parse(decode(raw));
614
+ } catch {
615
+ return failure("StubUnparsable", `'${stubFile}' is not valid JSON.`);
616
+ }
617
+ if (typeof parsed !== "object" || parsed === null || typeof parsed.name !== "string" || typeof parsed.resourcePath !== "string") {
618
+ return failure("StubUnparsable", `'${stubFile}' must be an object with string 'name' and 'resourcePath'.`);
619
+ }
620
+ return { stub: { name: parsed.name, resourcePath: parsed.resourcePath } };
621
+ }
622
+ async function readResource(resourceFile, entityName, resourcePath, fs) {
623
+ const missing = failure("ResourceMissing", `'${entityName}': definition resource '${resourcePath}' is missing. Restore it (check trash/backup) or delete the entity.`);
624
+ try {
625
+ const raw = await fs.readFile(resourceFile);
626
+ if (raw === null) {
627
+ return missing;
628
+ }
629
+ return { raw: JSON.parse(decode(raw)) };
630
+ } catch (error) {
631
+ if (error instanceof SyntaxError) {
632
+ return failure("ResourceInvalid", `'${entityName}': resource '${resourcePath}' is not valid JSON.`);
633
+ }
634
+ return missing;
635
+ }
636
+ }
637
+ function decode(raw) {
638
+ return typeof raw === "string" ? raw : new TextDecoder().decode(raw);
639
+ }
640
+ function failure(code, message) {
641
+ return { failure: { code, message } };
642
+ }
643
+ async function findSolutionRoot(startDir, fs) {
644
+ let dir = fs.path.resolve(startDir);
645
+ for (;; ) {
646
+ let entries = [];
647
+ try {
648
+ entries = await fs.readdir(dir);
649
+ } catch {
650
+ entries = [];
651
+ }
652
+ if (entries.some((name) => name.endsWith(".uipx"))) {
653
+ return dir;
654
+ }
655
+ const parent = fs.path.dirname(dir);
656
+ if (parent === dir) {
657
+ return;
658
+ }
659
+ dir = parent;
660
+ }
661
+ }
662
+
663
+ // src/pack/entity-project-tool.ts
664
+ class EntityProjectTool extends ProjectTool {
665
+ async validateAsync(options, _cancellationToken) {
666
+ return this.checkProjectAsync(options.projectPath);
667
+ }
668
+ async packAsync(options, _cancellationToken) {
669
+ const result = await this.checkProjectAsync(options.projectPath);
670
+ if (result.isSuccess) {
671
+ this.logger.info("Entity project is valid — no package emitted; the entity resources are the deployable payload.");
672
+ }
673
+ return result;
674
+ }
675
+ async checkProjectAsync(projectPath) {
676
+ const fs = this.fileSystem;
677
+ const diagnostics = [];
678
+ let entries = [];
679
+ try {
680
+ entries = await fs.readdir(projectPath);
681
+ } catch {
682
+ return new ToolResult(ENTITY_VALIDATION_FAILED, `Entity project directory '${projectPath}' could not be read.`, [], "Check that the project path in the solution manifest points at an existing directory.");
683
+ }
684
+ const stubs = sorted(entries.filter((name) => name.endsWith(ENTITY_EXTENSION_CONST)));
685
+ for (const foreign of sorted(entries.filter((name) => name.endsWith(".flow")))) {
686
+ diagnostics.push({
687
+ file: fs.path.join(projectPath, foreign),
688
+ code: PACK_DIAGNOSTIC_CODES.misplacedMainFile,
689
+ message: `'${foreign}' is a Flow main file inside an Entity project — move it to a Flow project.`
690
+ });
691
+ }
692
+ const solutionDir = await findSolutionRoot(projectPath, fs);
693
+ if (solutionDir === undefined) {
694
+ if (stubs.length > 0) {
695
+ diagnostics.push({
696
+ file: projectPath,
697
+ code: PACK_DIAGNOSTIC_CODES.noParentSolution,
698
+ message: "Entity project has no parent solution (.uipx not found) — the stubs' solution-relative resource pointers cannot resolve."
699
+ });
700
+ }
701
+ } else {
702
+ diagnostics.push(...await this.checkStubsAsync(projectPath, solutionDir, stubs));
703
+ }
704
+ if (diagnostics.length === 0) {
705
+ return ToolResult.success();
706
+ }
707
+ const result = new ToolResult(ENTITY_VALIDATION_FAILED, `Entity validation failed with ${diagnostics.length} issue(s). First: ${diagnostics[0]?.message}`, [], "Fix the listed entities (open them in the entity editor, or restore missing resources) and pack again.");
708
+ result.details = { diagnostics };
709
+ return result;
710
+ }
711
+ async checkStubsAsync(projectPath, solutionDir, stubs) {
712
+ const fs = this.fileSystem;
713
+ const diagnostics = [];
714
+ const allEntityNames = await collectSolutionEntityNames(solutionDir, fs);
715
+ for (const stubName of stubs) {
716
+ const stubFile = fs.path.join(projectPath, stubName);
717
+ const pair = await verifyPair(stubFile, solutionDir, fs);
718
+ if (!pair.ok) {
719
+ diagnostics.push({
720
+ file: stubFile,
721
+ code: pair.failure.code,
722
+ message: pair.failure.message
723
+ });
724
+ continue;
725
+ }
726
+ for (const diagnostic of validateEntity(pair.entity, {
727
+ siblingNames: withoutOneOccurrence(allEntityNames, pair.entityName)
728
+ })) {
729
+ diagnostics.push({
730
+ file: pair.resourceFile,
731
+ code: diagnostic.code,
732
+ message: diagnostic.message,
733
+ fieldName: diagnostic.fieldName
734
+ });
735
+ }
736
+ }
737
+ return diagnostics;
738
+ }
739
+ }
740
+ function sorted(names) {
741
+ return [...names].sort(compareCodeUnits);
742
+ }
743
+ function compareCodeUnits(left, right) {
744
+ if (left < right) {
745
+ return -1;
746
+ }
747
+ return left > right ? 1 : 0;
748
+ }
749
+ function withoutOneOccurrence(names, name) {
750
+ const siblings = [...names];
751
+ const self = siblings.indexOf(name);
752
+ if (self >= 0) {
753
+ siblings.splice(self, 1);
754
+ }
755
+ return siblings;
756
+ }
757
+ async function collectSolutionEntityNames(solutionDir, fs) {
758
+ const names = [];
759
+ let topLevel = [];
760
+ try {
761
+ topLevel = await fs.readdir(solutionDir);
762
+ } catch {
763
+ return names;
764
+ }
765
+ for (const entry of topLevel) {
766
+ if (entry === "resources" || entry.startsWith(".")) {
767
+ continue;
768
+ }
769
+ const dir = fs.path.join(solutionDir, entry);
770
+ try {
771
+ const stat = await fs.stat(dir);
772
+ if (!stat?.isDirectory()) {
773
+ continue;
774
+ }
775
+ for (const file of await fs.readdir(dir)) {
776
+ if (file.endsWith(ENTITY_EXTENSION_CONST)) {
777
+ names.push(fs.path.basename(file, ENTITY_EXTENSION_CONST));
778
+ }
779
+ }
780
+ } catch {}
781
+ }
782
+ return names;
783
+ }
784
+ // src/pack/entity-tool-factory.ts
785
+ class EntityToolFactory {
786
+ supportedTypes = ["Entity"];
787
+ async createAsync(logger, fileSystem) {
788
+ return new EntityProjectTool(fileSystem, logger);
789
+ }
790
+ }
791
+ export {
792
+ DIAGNOSTIC_CODES,
793
+ ENTITY_RESOURCE_API_VERSION,
794
+ ENTITY_RESOURCE_DOC_VERSION,
795
+ ENTITY_RESOURCE_KIND,
796
+ ENTITY_RESOURCE_TYPE,
797
+ ENTITY_VALIDATION_FAILED,
798
+ EntityProjectTool,
799
+ EntityResourceError,
800
+ EntityToolFactory,
801
+ SOLUTION_FOLDER,
802
+ SYSTEM_FIELD_TEMPLATES,
803
+ createEntitySkeleton,
804
+ entityResourcePath,
805
+ findSolutionRoot,
806
+ serialize,
807
+ serializeEntityJson,
808
+ unwrapResource,
809
+ updateResourceSchema,
810
+ validateEntity,
811
+ verifyPair,
812
+ wrapEntityJson
813
+ };
814
+
815
+ //# debugId=EEB462776EC352DB64756E2164756E21