@agent-native/core 0.154.4 → 0.154.5
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/corpus/README.md +1 -1
- package/corpus/templates/content/.agents/skills/content/SKILL.md +2 -2
- package/corpus/templates/content/.agents/skills/document-editing/references/databases.md +20 -4
- package/corpus/templates/content/actions/_database-row-mutation.ts +1490 -0
- package/corpus/templates/content/actions/_database-utils.ts +44 -0
- package/corpus/templates/content/actions/_property-utils.ts +4 -1
- package/corpus/templates/content/actions/add-database-item.ts +62 -240
- package/corpus/templates/content/actions/configure-document-property.ts +165 -1
- package/corpus/templates/content/actions/delete-document-property.ts +18 -0
- package/corpus/templates/content/actions/set-document-property.ts +78 -1
- package/corpus/templates/content/actions/update-database-item.ts +62 -0
- package/corpus/templates/content/actions/upsert-database-item-by-key.ts +43 -796
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +10 -8
- package/corpus/templates/content/app/hooks/use-content-database.ts +33 -32
- package/corpus/templates/content/changelog/2026-08-10-database-row-actions-now-validate-exact-schemas-and-safe-ret.md +6 -0
- package/corpus/templates/content/parity/matrix.md +1 -1
- package/corpus/templates/content/parity/matrix.ts +2 -0
- package/corpus/templates/content/server/db/schema.ts +39 -3
- package/corpus/templates/content/server/plugins/db.ts +30 -0
- package/corpus/templates/content/shared/api.ts +75 -2
- package/dist/action.d.ts +22 -0
- package/dist/action.js +27 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/index.browser.d.ts +1 -1
- package/dist/index.browser.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/notifications/routes.d.ts +1 -1
- package/dist/observability/routes.d.ts +6 -6
- package/dist/secrets/routes.d.ts +3 -3
- package/dist/server/action-routes.js +10 -2
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/package.json +1 -1
|
@@ -226,7 +226,10 @@ export default defineAction({
|
|
|
226
226
|
database.id,
|
|
227
227
|
);
|
|
228
228
|
const [lockedDatabase] = await tx
|
|
229
|
-
.select({
|
|
229
|
+
.select({
|
|
230
|
+
id: schema.contentDatabases.id,
|
|
231
|
+
naturalKeyPropertyId: schema.contentDatabases.naturalKeyPropertyId,
|
|
232
|
+
})
|
|
230
233
|
.from(schema.contentDatabases)
|
|
231
234
|
.where(
|
|
232
235
|
and(
|
|
@@ -272,6 +275,41 @@ export default defineAction({
|
|
|
272
275
|
if (isComputedPropertyType(lockedType)) {
|
|
273
276
|
throw new Error("Computed properties cannot be edited.");
|
|
274
277
|
}
|
|
278
|
+
const isNaturalKey = lockedDatabase.naturalKeyPropertyId === propertyId;
|
|
279
|
+
if (isNaturalKey) {
|
|
280
|
+
let parsed: unknown;
|
|
281
|
+
try {
|
|
282
|
+
parsed = JSON.parse(valueJson);
|
|
283
|
+
} catch {
|
|
284
|
+
parsed = null;
|
|
285
|
+
}
|
|
286
|
+
if (typeof parsed !== "string" || !parsed.trim()) {
|
|
287
|
+
throw new Error(
|
|
288
|
+
"A database natural key must remain a non-empty string.",
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
const [existingNaturalKeyClaim] = await tx
|
|
292
|
+
.select({
|
|
293
|
+
keyValueJson: schema.contentDatabaseItemKeyClaims.keyValueJson,
|
|
294
|
+
})
|
|
295
|
+
.from(schema.contentDatabaseItemKeyClaims)
|
|
296
|
+
.where(
|
|
297
|
+
and(
|
|
298
|
+
eq(schema.contentDatabaseItemKeyClaims.databaseId, database.id),
|
|
299
|
+
eq(schema.contentDatabaseItemKeyClaims.propertyId, propertyId),
|
|
300
|
+
eq(schema.contentDatabaseItemKeyClaims.documentId, documentId),
|
|
301
|
+
),
|
|
302
|
+
)
|
|
303
|
+
.limit(1);
|
|
304
|
+
if (
|
|
305
|
+
existingNaturalKeyClaim &&
|
|
306
|
+
existingNaturalKeyClaim.keyValueJson !== valueJson
|
|
307
|
+
) {
|
|
308
|
+
throw new Error(
|
|
309
|
+
"A claimed database natural key cannot be changed. Create a new row instead.",
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
275
313
|
const [conflictingClaim] = await tx
|
|
276
314
|
.select({ id: schema.contentDatabaseItemKeyClaims.id })
|
|
277
315
|
.from(schema.contentDatabaseItemKeyClaims)
|
|
@@ -314,6 +352,45 @@ export default defineAction({
|
|
|
314
352
|
updatedAt: now,
|
|
315
353
|
});
|
|
316
354
|
}
|
|
355
|
+
if (isNaturalKey) {
|
|
356
|
+
await tx
|
|
357
|
+
.insert(schema.contentDatabaseItemKeyClaims)
|
|
358
|
+
.values({
|
|
359
|
+
id: nanoid(),
|
|
360
|
+
ownerEmail: database.ownerEmail,
|
|
361
|
+
orgId: database.orgId,
|
|
362
|
+
databaseId: database.id,
|
|
363
|
+
propertyId,
|
|
364
|
+
keyValueJson: valueJson,
|
|
365
|
+
itemId: membership.id,
|
|
366
|
+
documentId,
|
|
367
|
+
createdAt: now,
|
|
368
|
+
updatedAt: now,
|
|
369
|
+
})
|
|
370
|
+
.onConflictDoNothing();
|
|
371
|
+
const [claim] = await tx
|
|
372
|
+
.select({
|
|
373
|
+
itemId: schema.contentDatabaseItemKeyClaims.itemId,
|
|
374
|
+
documentId: schema.contentDatabaseItemKeyClaims.documentId,
|
|
375
|
+
})
|
|
376
|
+
.from(schema.contentDatabaseItemKeyClaims)
|
|
377
|
+
.where(
|
|
378
|
+
and(
|
|
379
|
+
eq(schema.contentDatabaseItemKeyClaims.databaseId, database.id),
|
|
380
|
+
eq(schema.contentDatabaseItemKeyClaims.propertyId, propertyId),
|
|
381
|
+
eq(schema.contentDatabaseItemKeyClaims.keyValueJson, valueJson),
|
|
382
|
+
),
|
|
383
|
+
);
|
|
384
|
+
if (
|
|
385
|
+
!claim ||
|
|
386
|
+
claim.itemId !== membership.id ||
|
|
387
|
+
claim.documentId !== documentId
|
|
388
|
+
) {
|
|
389
|
+
throw new Error(
|
|
390
|
+
"This natural key is already claimed by another database row.",
|
|
391
|
+
);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
317
394
|
await tx
|
|
318
395
|
.delete(schema.contentDatabaseItemKeyClaims)
|
|
319
396
|
.where(
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { defineAction } from "@agent-native/core";
|
|
2
|
+
import { buildDeepLink } from "@agent-native/core/server";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
|
|
5
|
+
import type { ContentDatabaseRowMutationResult } from "../shared/api.js";
|
|
6
|
+
import {
|
|
7
|
+
databaseMutationEnvelopeSchema,
|
|
8
|
+
updateDatabaseRow,
|
|
9
|
+
} from "./_database-row-mutation.js";
|
|
10
|
+
|
|
11
|
+
const schema = databaseMutationEnvelopeSchema.extend({
|
|
12
|
+
itemId: z.string().min(1).describe("Exact database membership row ID"),
|
|
13
|
+
documentId: z.string().min(1).describe("Exact row page ID"),
|
|
14
|
+
expectedRowRevision: z
|
|
15
|
+
.string()
|
|
16
|
+
.min(1)
|
|
17
|
+
.describe("Row revision returned by get-content-database"),
|
|
18
|
+
title: z.string().trim().min(1).max(500).optional(),
|
|
19
|
+
propertyValues: z
|
|
20
|
+
.record(z.string(), z.unknown())
|
|
21
|
+
.optional()
|
|
22
|
+
.describe(
|
|
23
|
+
"Sparse strict patch keyed by property definition ID; omitted fields are preserved and explicit null clears a value",
|
|
24
|
+
),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
export default defineAction({
|
|
28
|
+
description:
|
|
29
|
+
"Sparsely update one exact Content database row by stable item and document IDs. Requires schema and row revisions, validates every non-Blocks property, and returns a verified idempotent receipt.",
|
|
30
|
+
schema,
|
|
31
|
+
http: { method: "PUT" },
|
|
32
|
+
audit: {
|
|
33
|
+
recordInputs: false,
|
|
34
|
+
target: (args) => ({
|
|
35
|
+
type: "document",
|
|
36
|
+
id: args.documentId,
|
|
37
|
+
visibility: "private",
|
|
38
|
+
}),
|
|
39
|
+
summary: (_args, result) => {
|
|
40
|
+
const receipt = (result as ContentDatabaseRowMutationResult | null)
|
|
41
|
+
?.receipt;
|
|
42
|
+
return receipt
|
|
43
|
+
? `${receipt.outcome === "unchanged" ? "Checked" : "Updated"} Content database row ${receipt.row.itemId}`
|
|
44
|
+
: "Updated Content database row";
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
run: updateDatabaseRow,
|
|
48
|
+
link: ({ result }) => {
|
|
49
|
+
const documentId = (result as ContentDatabaseRowMutationResult | null)
|
|
50
|
+
?.receipt.row.documentId;
|
|
51
|
+
if (!documentId) return null;
|
|
52
|
+
return {
|
|
53
|
+
url: buildDeepLink({
|
|
54
|
+
app: "content",
|
|
55
|
+
view: "editor",
|
|
56
|
+
params: { documentId },
|
|
57
|
+
}),
|
|
58
|
+
label: "Open database row",
|
|
59
|
+
view: "editor",
|
|
60
|
+
};
|
|
61
|
+
},
|
|
62
|
+
});
|