@highstate/cli 0.9.18 → 0.9.19
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/assets/tsconfig.base.json +7 -2
- package/dist/highstate.manifest.json +1 -1
- package/dist/{library-loader-ZABUULFB.js → library-loader-6TJTW2HX.js} +3 -3
- package/dist/library-loader-6TJTW2HX.js.map +1 -0
- package/dist/main.js +391 -170
- package/dist/main.js.map +1 -1
- package/package.json +5 -5
- package/src/commands/backend/identity.ts +1 -1
- package/src/commands/package/create.ts +35 -0
- package/src/commands/package/index.ts +4 -0
- package/src/commands/package/list.ts +41 -0
- package/src/commands/package/remove.ts +40 -0
- package/src/commands/package/update-references.ts +18 -0
- package/src/main.ts +10 -0
- package/src/shared/index.ts +1 -0
- package/src/shared/library-loader.ts +1 -1
- package/src/shared/schema-transformer.spec.ts +178 -35
- package/src/shared/schema-transformer.ts +250 -244
- package/src/shared/source-hash-calculator.ts +0 -1
- package/src/shared/workspace.ts +240 -0
- package/dist/library-loader-ZABUULFB.js.map +0 -1
|
@@ -2,7 +2,7 @@ import { describe, it, expect } from "vitest"
|
|
|
2
2
|
import { applySchemaTransformations } from "./schema-transformer"
|
|
3
3
|
|
|
4
4
|
describe("applySchemaTransformations", () => {
|
|
5
|
-
it("should transform simple values
|
|
5
|
+
it("should transform simple values using helper functions", async () => {
|
|
6
6
|
const input = `
|
|
7
7
|
const spec = {
|
|
8
8
|
inputs: {
|
|
@@ -21,13 +21,16 @@ const spec = {
|
|
|
21
21
|
|
|
22
22
|
const result = await applySchemaTransformations(input)
|
|
23
23
|
|
|
24
|
-
expect(result).toContain(
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
expect(result).toContain("
|
|
24
|
+
expect(result).toContain(
|
|
25
|
+
"$addInputDescription(clusterEntity, `The Kubernetes cluster to deploy on.`)",
|
|
26
|
+
)
|
|
27
|
+
expect(result).toContain("$addArgumentDescription(Type.Number(), `The port number to use.`)")
|
|
28
|
+
expect(result).toContain(
|
|
29
|
+
'import { $addArgumentDescription, $addInputDescription } from "@highstate/contract"',
|
|
30
|
+
)
|
|
28
31
|
})
|
|
29
32
|
|
|
30
|
-
it("should
|
|
33
|
+
it("should wrap existing structured objects with helper functions", async () => {
|
|
31
34
|
const input = `
|
|
32
35
|
const spec = {
|
|
33
36
|
args: {
|
|
@@ -46,14 +49,13 @@ const spec = {
|
|
|
46
49
|
|
|
47
50
|
const result = await applySchemaTransformations(input)
|
|
48
51
|
|
|
52
|
+
expect(result).toContain("$addArgumentDescription({")
|
|
49
53
|
expect(result).toContain('displayName: "API Token"')
|
|
50
54
|
expect(result).toContain("sensitive: true")
|
|
51
|
-
expect(result).toContain("
|
|
52
|
-
expect(result).not.toContain("...obj")
|
|
53
|
-
expect(result).not.toContain("((obj) =>")
|
|
55
|
+
expect(result).toContain("}, `The API token for authentication.`)")
|
|
54
56
|
})
|
|
55
57
|
|
|
56
|
-
it("should
|
|
58
|
+
it("should wrap structured objects with helper functions", async () => {
|
|
57
59
|
const input = `
|
|
58
60
|
const spec = {
|
|
59
61
|
inputs: {
|
|
@@ -69,10 +71,10 @@ const spec = {
|
|
|
69
71
|
|
|
70
72
|
const result = await applySchemaTransformations(input)
|
|
71
73
|
|
|
74
|
+
expect(result).toContain("$addInputDescription({")
|
|
72
75
|
expect(result).toContain("entity: endpointEntity")
|
|
73
76
|
expect(result).toContain("required: false")
|
|
74
|
-
expect(result).toContain("
|
|
75
|
-
expect(result).toContain("description: `The target endpoint.`")
|
|
77
|
+
expect(result).toContain("}, `The target endpoint.`)")
|
|
76
78
|
})
|
|
77
79
|
|
|
78
80
|
it("should handle $args marker function", async () => {
|
|
@@ -88,8 +90,9 @@ const spec = {
|
|
|
88
90
|
|
|
89
91
|
const result = await applySchemaTransformations(input)
|
|
90
92
|
|
|
91
|
-
expect(result).toContain(
|
|
92
|
-
|
|
93
|
+
expect(result).toContain(
|
|
94
|
+
"$addArgumentDescription(Type.String(), `The configuration file path.`)",
|
|
95
|
+
)
|
|
93
96
|
})
|
|
94
97
|
|
|
95
98
|
it("should handle $inputs marker function", async () => {
|
|
@@ -105,8 +108,7 @@ const spec = {
|
|
|
105
108
|
|
|
106
109
|
const result = await applySchemaTransformations(input)
|
|
107
110
|
|
|
108
|
-
expect(result).toContain("
|
|
109
|
-
expect(result).toContain("description: `The source data.`")
|
|
111
|
+
expect(result).toContain("$addInputDescription(dataEntity, `The source data.`)")
|
|
110
112
|
})
|
|
111
113
|
|
|
112
114
|
it("should handle $outputs marker function", async () => {
|
|
@@ -122,8 +124,7 @@ const spec = {
|
|
|
122
124
|
|
|
123
125
|
const result = await applySchemaTransformations(input)
|
|
124
126
|
|
|
125
|
-
expect(result).toContain("
|
|
126
|
-
expect(result).toContain("description: `The processed result.`")
|
|
127
|
+
expect(result).toContain("$addInputDescription(resultEntity, `The processed result.`)")
|
|
127
128
|
})
|
|
128
129
|
|
|
129
130
|
it("should handle $secrets marker function", async () => {
|
|
@@ -139,8 +140,7 @@ const spec = {
|
|
|
139
140
|
|
|
140
141
|
const result = await applySchemaTransformations(input)
|
|
141
142
|
|
|
142
|
-
expect(result).toContain("
|
|
143
|
-
expect(result).toContain("description: `The database password.`")
|
|
143
|
+
expect(result).toContain("$addArgumentDescription(Type.String(), `The database password.`)")
|
|
144
144
|
})
|
|
145
145
|
|
|
146
146
|
it("should ignore properties without JSDoc comments", async () => {
|
|
@@ -158,8 +158,7 @@ const spec = {
|
|
|
158
158
|
const result = await applySchemaTransformations(input)
|
|
159
159
|
|
|
160
160
|
expect(result).toContain("cluster: clusterEntity") // unchanged
|
|
161
|
-
expect(result).toContain("
|
|
162
|
-
expect(result).toContain("description: `Only this one has a comment.`")
|
|
161
|
+
expect(result).toContain("$addInputDescription(endpointEntity, `Only this one has a comment.`)") // transformed
|
|
163
162
|
})
|
|
164
163
|
|
|
165
164
|
it("should ignore properties not in target objects", async () => {
|
|
@@ -183,7 +182,7 @@ const spec = {
|
|
|
183
182
|
const result = await applySchemaTransformations(input)
|
|
184
183
|
|
|
185
184
|
expect(result).toContain('someProperty: "value"') // unchanged
|
|
186
|
-
expect(result).toContain("
|
|
185
|
+
expect(result).toContain("$addInputDescription(clusterEntity, `This should be transformed.`)") // transformed
|
|
187
186
|
})
|
|
188
187
|
|
|
189
188
|
it("should clean JSDoc comments properly", async () => {
|
|
@@ -200,11 +199,10 @@ const spec = {
|
|
|
200
199
|
|
|
201
200
|
const result = await applySchemaTransformations(input)
|
|
202
201
|
|
|
203
|
-
expect(result).toContain("schema: Type.String()")
|
|
204
202
|
expect(result).toContain(
|
|
205
|
-
"
|
|
203
|
+
"$addArgumentDescription(Type.String(), `This is a description with \\`backticks\\` and \\${template} literals.",
|
|
206
204
|
)
|
|
207
|
-
expect(result).toContain("It also has multiple lines.`")
|
|
205
|
+
expect(result).toContain("It also has multiple lines.`)")
|
|
208
206
|
})
|
|
209
207
|
|
|
210
208
|
it("should add .meta() to z.object fields with JSDoc comments", async () => {
|
|
@@ -445,13 +443,10 @@ var virtualMachine = defineUnit({
|
|
|
445
443
|
|
|
446
444
|
const result = await applySchemaTransformations(input)
|
|
447
445
|
|
|
448
|
-
// Should transform args.ipv4
|
|
449
|
-
expect(result).toContain("ipv4: {")
|
|
446
|
+
// Should transform args.ipv4 using $addArgumentDescription helper
|
|
447
|
+
expect(result).toContain("ipv4: $addArgumentDescription({")
|
|
450
448
|
expect(result).toContain("schema: z.discriminatedUnion")
|
|
451
|
-
expect(result).toContain("
|
|
452
|
-
expect(result).toContain(
|
|
453
|
-
"description: `The IPv4 address configuration for the virtual machine.`",
|
|
454
|
-
)
|
|
449
|
+
expect(result).toContain("}, `The IPv4 address configuration for the virtual machine.`)")
|
|
455
450
|
|
|
456
451
|
// Should add .meta() to z.object fields with JSDoc comments inside the discriminated union
|
|
457
452
|
expect(result).toContain(
|
|
@@ -476,14 +471,162 @@ var virtualMachine = defineUnit({
|
|
|
476
471
|
expect(result).toContain('type: z.literal("dhcp")') // unchanged
|
|
477
472
|
expect(result).toContain('type: z.literal("static"),') // unchanged
|
|
478
473
|
|
|
479
|
-
// Should properly handle inputs.vendorData
|
|
480
|
-
expect(result).toContain("vendorData: {")
|
|
474
|
+
// Should properly handle inputs.vendorData using $addInputDescription helper
|
|
475
|
+
expect(result).toContain("vendorData: $addInputDescription({")
|
|
481
476
|
expect(result).toContain("entity: fileEntity,")
|
|
482
477
|
expect(result).toContain("required: false,")
|
|
483
|
-
expect(result).toContain("
|
|
478
|
+
expect(result).toContain("}, `The cloud-init vendor data to use for the virtual machine.")
|
|
479
|
+
expect(result).toContain("You can provide a cloud-config from the distribution component.`)")
|
|
484
480
|
|
|
485
481
|
// Should NOT transform other inputs without JSDoc
|
|
486
482
|
expect(result).toContain("proxmoxCluster: clusterEntity,") // unchanged
|
|
487
483
|
expect(result).toContain("image: imageEntity,") // unchanged
|
|
488
484
|
})
|
|
485
|
+
|
|
486
|
+
it("should add description to defineUnit function with JSDoc", async () => {
|
|
487
|
+
const input = `
|
|
488
|
+
/**
|
|
489
|
+
* Installs the Gateway API CRDs to the cluster.
|
|
490
|
+
*/
|
|
491
|
+
export const gatewayApi = defineUnit({
|
|
492
|
+
type: "k8s.gateway-api",
|
|
493
|
+
inputs: {
|
|
494
|
+
k8sCluster: clusterEntity,
|
|
495
|
+
},
|
|
496
|
+
outputs: {
|
|
497
|
+
k8sCluster: clusterEntity,
|
|
498
|
+
},
|
|
499
|
+
meta: {
|
|
500
|
+
title: "Gateway API",
|
|
501
|
+
icon: "devicon:kubernetes",
|
|
502
|
+
secondaryIcon: "mdi:api",
|
|
503
|
+
secondaryIconColor: "#4CAF50",
|
|
504
|
+
category: "Kubernetes",
|
|
505
|
+
},
|
|
506
|
+
source: {
|
|
507
|
+
package: "@highstate/k8s",
|
|
508
|
+
path: "units/gateway-api",
|
|
509
|
+
},
|
|
510
|
+
})`
|
|
511
|
+
|
|
512
|
+
const result = await applySchemaTransformations(input)
|
|
513
|
+
|
|
514
|
+
expect(result).toContain("description: `Installs the Gateway API CRDs to the cluster.`")
|
|
515
|
+
expect(result).toContain('title: "Gateway API"')
|
|
516
|
+
expect(result).toContain('icon: "devicon:kubernetes"')
|
|
517
|
+
})
|
|
518
|
+
|
|
519
|
+
it("should add meta field to defineEntity without existing meta", async () => {
|
|
520
|
+
const input = `
|
|
521
|
+
/**
|
|
522
|
+
* Represents a Kubernetes cluster.
|
|
523
|
+
*/
|
|
524
|
+
export const clusterEntity = defineEntity({
|
|
525
|
+
type: "k8s.cluster",
|
|
526
|
+
schema: z.object({
|
|
527
|
+
name: z.string(),
|
|
528
|
+
endpoint: z.string(),
|
|
529
|
+
}),
|
|
530
|
+
})`
|
|
531
|
+
|
|
532
|
+
const result = await applySchemaTransformations(input)
|
|
533
|
+
|
|
534
|
+
expect(result).toContain("meta: {")
|
|
535
|
+
expect(result).toContain("description: `Represents a Kubernetes cluster.`")
|
|
536
|
+
expect(result).toContain("schema: z.object({")
|
|
537
|
+
})
|
|
538
|
+
|
|
539
|
+
it("should add description to defineComponent function", async () => {
|
|
540
|
+
const input = `
|
|
541
|
+
/**
|
|
542
|
+
* A reusable database component.
|
|
543
|
+
*/
|
|
544
|
+
export const database = defineComponent({
|
|
545
|
+
type: "database",
|
|
546
|
+
components: {
|
|
547
|
+
server: serverUnit,
|
|
548
|
+
storage: storageUnit,
|
|
549
|
+
},
|
|
550
|
+
meta: {
|
|
551
|
+
category: "Database",
|
|
552
|
+
},
|
|
553
|
+
})`
|
|
554
|
+
|
|
555
|
+
const result = await applySchemaTransformations(input)
|
|
556
|
+
|
|
557
|
+
expect(result).toContain("description: `A reusable database component.`")
|
|
558
|
+
expect(result).toContain('category: "Database"')
|
|
559
|
+
})
|
|
560
|
+
|
|
561
|
+
it("should not transform define functions without JSDoc", async () => {
|
|
562
|
+
const input = `
|
|
563
|
+
export const simpleUnit = defineUnit({
|
|
564
|
+
type: "simple",
|
|
565
|
+
meta: {
|
|
566
|
+
title: "Simple Unit",
|
|
567
|
+
},
|
|
568
|
+
})`
|
|
569
|
+
|
|
570
|
+
const result = await applySchemaTransformations(input)
|
|
571
|
+
|
|
572
|
+
expect(result).not.toContain("description:")
|
|
573
|
+
expect(result).toContain('title: "Simple Unit"')
|
|
574
|
+
})
|
|
575
|
+
|
|
576
|
+
it("should not create nested meta.meta.description structure", async () => {
|
|
577
|
+
const input = `
|
|
578
|
+
/**
|
|
579
|
+
* Traefik Gateway unit definition for Kubernetes.
|
|
580
|
+
*/
|
|
581
|
+
var traefikGateway = defineUnit({
|
|
582
|
+
type: "k8s.apps.traefik-gateway.v1",
|
|
583
|
+
meta: {
|
|
584
|
+
title: "Traefik Gateway",
|
|
585
|
+
icon: "simple-icons:traefikproxy",
|
|
586
|
+
category: "Network",
|
|
587
|
+
},
|
|
588
|
+
})`
|
|
589
|
+
|
|
590
|
+
const result = await applySchemaTransformations(input)
|
|
591
|
+
|
|
592
|
+
// Should have description at the correct level
|
|
593
|
+
expect(result).toContain("meta: {")
|
|
594
|
+
expect(result).toContain("description: `Traefik Gateway unit definition for Kubernetes.`")
|
|
595
|
+
expect(result).toContain('title: "Traefik Gateway"')
|
|
596
|
+
expect(result).toContain('icon: "simple-icons:traefikproxy"')
|
|
597
|
+
expect(result).toContain('category: "Network"')
|
|
598
|
+
|
|
599
|
+
// Should NOT have nested meta.meta structure
|
|
600
|
+
expect(result).not.toContain("meta: {\\s*meta: {")
|
|
601
|
+
|
|
602
|
+
// Verify the structure more precisely - should only have one meta object
|
|
603
|
+
const metaMatches = (result.match(/meta\s*:\s*\{/g) || []).length
|
|
604
|
+
expect(metaMatches).toBe(1)
|
|
605
|
+
})
|
|
606
|
+
|
|
607
|
+
it("should replace existing description in defineUnit meta object", async () => {
|
|
608
|
+
const input = `
|
|
609
|
+
/**
|
|
610
|
+
* Updated description for the unit.
|
|
611
|
+
*/
|
|
612
|
+
export const testUnit = defineUnit({
|
|
613
|
+
type: "test",
|
|
614
|
+
meta: {
|
|
615
|
+
title: "Test Unit",
|
|
616
|
+
description: \`Old description.\`,
|
|
617
|
+
category: "Test",
|
|
618
|
+
},
|
|
619
|
+
})`
|
|
620
|
+
|
|
621
|
+
const result = await applySchemaTransformations(input)
|
|
622
|
+
|
|
623
|
+
expect(result).toContain("description: `Updated description for the unit.`")
|
|
624
|
+
expect(result).not.toContain("Old description")
|
|
625
|
+
expect(result).toContain('title: "Test Unit"')
|
|
626
|
+
expect(result).toContain('category: "Test"')
|
|
627
|
+
|
|
628
|
+
// Should still only have one meta object
|
|
629
|
+
const metaMatches = (result.match(/meta\s*:\s*\{/g) || []).length
|
|
630
|
+
expect(metaMatches).toBe(1)
|
|
631
|
+
})
|
|
489
632
|
})
|