(
input: SaveArtifactInput,
)
| 6132 | }); |
| 6133 | |
| 6134 | const artifactsSave = ( |
| 6135 | input: SaveArtifactInput, |
| 6136 | ): Effect.Effect<Artifact, ArtifactNotFoundError | StorageFailure> => |
| 6137 | Effect.gen(function* () { |
| 6138 | const now = new Date(); |
| 6139 | const description = input.description ?? null; |
| 6140 | // An explicit id overwrites in place (v1 keeps no version history). It |
| 6141 | // must already resolve to a visible row: minting a caller-chosen id |
| 6142 | // would let a stale client resurrect a deleted artifact silently. |
| 6143 | if (input.id !== undefined) { |
| 6144 | const where = artifactById(input.id); |
| 6145 | const existing = yield* core.findFirst("artifact", { where }); |
| 6146 | if (!existing) { |
| 6147 | return yield* new ArtifactNotFoundError({ |
| 6148 | id: ArtifactId.make(input.id), |
| 6149 | }); |
| 6150 | } |
| 6151 | // `bindings` is written on every overwrite, including back to null: |
| 6152 | // it interprets `code`, so carrying the previous value forward under |
| 6153 | // new source would bind roles the new code never declares. `preview` |
| 6154 | // is written for exactly the same reason, and the case it prevents is |
| 6155 | // visible: an image preview captured from the OLD render would |
| 6156 | // otherwise keep advertising a version of the artifact that no longer |
| 6157 | // exists. |
| 6158 | const set = { |
| 6159 | title: input.title, |
| 6160 | description, |
| 6161 | code: input.code, |
| 6162 | bindings: input.bindings ?? null, |
| 6163 | preview: input.preview ?? null, |
| 6164 | updated_at: now, |
| 6165 | }; |
| 6166 | yield* core.updateMany("artifact", { where, set }); |
| 6167 | return rowToArtifact({ ...existing, ...set }); |
| 6168 | } |
| 6169 | yield* requireUserSubject("user"); |
| 6170 | const keys = yield* Effect.try({ |
| 6171 | try: () => ownedKeys("user"), |
| 6172 | catch: (cause) => storageFailureFromUnknown("invalid owner", cause), |
| 6173 | }); |
| 6174 | const created = yield* core.create("artifact", { |
| 6175 | tenant: keys.tenant, |
| 6176 | owner: keys.owner, |
| 6177 | subject: keys.subject, |
| 6178 | id: `art_${Math.random().toString(36).slice(2)}${Date.now().toString(36)}`, |
| 6179 | title: input.title, |
| 6180 | description, |
| 6181 | code: input.code, |
| 6182 | bindings: input.bindings ?? null, |
| 6183 | preview: input.preview ?? null, |
| 6184 | created_at: now, |
| 6185 | updated_at: now, |
| 6186 | }); |
| 6187 | return rowToArtifact(created); |
| 6188 | }); |
| 6189 | |
| 6190 | /** |
| 6191 | * Replace an artifact's preview with a snapshot of a settled render. |
nothing calls this directly
no test coverage detected