@highstate/backend 0.9.33 → 0.9.35

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.
@@ -1,4 +1,5 @@
1
1
  import type { UnitPage, UnitTerminal, UnitTrigger } from "@highstate/contract"
2
+ import { createId } from "@paralleldrive/cuid2"
2
3
  import { describe, expect } from "vitest"
3
4
  import { test } from "../test-utils"
4
5
  import { UnitExtraService } from "./unit-extra"
@@ -537,3 +538,115 @@ To login, use the following token:`,
537
538
  })
538
539
  })
539
540
  })
541
+
542
+ describe("pruneInstanceArtifacts", () => {
543
+ test("removes artifacts not included in the keep list", async ({
544
+ database,
545
+ project,
546
+ createInstanceState,
547
+ projectDatabase,
548
+ }) => {
549
+ const service = new UnitExtraService(database)
550
+ const instance = await createInstanceState(project.id)
551
+
552
+ const artifactToKeep = await projectDatabase.artifact.create({
553
+ data: {
554
+ hash: createId(),
555
+ size: 1024,
556
+ chunkSize: 1024,
557
+ meta: { title: "keep" },
558
+ instances: {
559
+ connect: { id: instance.id },
560
+ },
561
+ },
562
+ select: { id: true },
563
+ })
564
+
565
+ const artifactToRemove = await projectDatabase.artifact.create({
566
+ data: {
567
+ hash: createId(),
568
+ size: 512,
569
+ chunkSize: 1024,
570
+ meta: { title: "remove" },
571
+ instances: {
572
+ connect: { id: instance.id },
573
+ },
574
+ },
575
+ select: { id: true },
576
+ })
577
+
578
+ await projectDatabase.$transaction(async tx => {
579
+ await service.pruneInstanceArtifacts(tx, instance.id, [artifactToKeep.id])
580
+ })
581
+
582
+ const remaining = await projectDatabase.artifact.findMany({
583
+ where: {
584
+ instances: {
585
+ some: {
586
+ id: instance.id,
587
+ },
588
+ },
589
+ },
590
+ select: { id: true },
591
+ })
592
+
593
+ expect(remaining).toHaveLength(1)
594
+ expect(remaining[0].id).toBe(artifactToKeep.id)
595
+
596
+ const removed = await projectDatabase.artifact.findUniqueOrThrow({
597
+ where: { id: artifactToRemove.id },
598
+ include: { instances: true },
599
+ })
600
+ expect(removed.instances).toHaveLength(0)
601
+ })
602
+
603
+ test("removes all artifacts when keep list is empty", async ({
604
+ database,
605
+ project,
606
+ createInstanceState,
607
+ projectDatabase,
608
+ }) => {
609
+ const service = new UnitExtraService(database)
610
+ const instance = await createInstanceState(project.id)
611
+
612
+ await projectDatabase.artifact.create({
613
+ data: {
614
+ hash: createId(),
615
+ size: 128,
616
+ chunkSize: 1024,
617
+ meta: { title: "artifact-1" },
618
+ instances: {
619
+ connect: { id: instance.id },
620
+ },
621
+ },
622
+ })
623
+
624
+ await projectDatabase.artifact.create({
625
+ data: {
626
+ hash: createId(),
627
+ size: 256,
628
+ chunkSize: 1024,
629
+ meta: { title: "artifact-2" },
630
+ instances: {
631
+ connect: { id: instance.id },
632
+ },
633
+ },
634
+ })
635
+
636
+ await projectDatabase.$transaction(async tx => {
637
+ await service.pruneInstanceArtifacts(tx, instance.id, [])
638
+ })
639
+
640
+ const remaining = await projectDatabase.artifact.findMany({
641
+ where: {
642
+ instances: {
643
+ some: {
644
+ id: instance.id,
645
+ },
646
+ },
647
+ },
648
+ })
649
+
650
+ expect(remaining).toHaveLength(0)
651
+ })
652
+ })
@@ -146,6 +146,46 @@ export class UnitExtraService {
146
146
  return triggerIds
147
147
  }
148
148
 
149
+ /**
150
+ * Disconnects artifacts that are no longer referenced by the instance.
151
+ *
152
+ * @param tx The database transaction to use.
153
+ * @param stateId The ID of the instance state.
154
+ * @param artifactIds The artifact IDs that should remain connected to the instance.
155
+ */
156
+ async pruneInstanceArtifacts(
157
+ tx: ProjectTransaction,
158
+ stateId: string,
159
+ artifactIds: string[],
160
+ ): Promise<void> {
161
+ const staleArtifacts = await tx.artifact.findMany({
162
+ where: {
163
+ instances: {
164
+ some: {
165
+ id: stateId,
166
+ },
167
+ },
168
+ id: artifactIds.length > 0 ? { notIn: artifactIds } : undefined,
169
+ },
170
+ select: {
171
+ id: true,
172
+ },
173
+ })
174
+
175
+ if (staleArtifacts.length === 0) {
176
+ return
177
+ }
178
+
179
+ await tx.instanceState.update({
180
+ where: { id: stateId },
181
+ data: {
182
+ artifacts: {
183
+ disconnect: staleArtifacts.map(artifact => ({ id: artifact.id })),
184
+ },
185
+ },
186
+ })
187
+ }
188
+
149
189
  /**
150
190
  * Gets all triggers for a specific instance.
151
191
  *