@anchrd/intel-contract 0.13.0 → 0.15.0
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/README.md +80 -0
- package/dist/contract/bundle.d.ts +85 -0
- package/dist/contract/bundle.js +63 -0
- package/dist/contract/contract.d.ts +3 -2245
- package/dist/contract/contract.js +24 -1108
- package/dist/contract/flow-run.d.ts +346 -0
- package/dist/contract/flow-run.js +181 -0
- package/dist/contract/flow.d.ts +995 -0
- package/dist/contract/flow.js +417 -0
- package/dist/contract/node.d.ts +402 -0
- package/dist/contract/node.js +310 -0
- package/dist/contract/share.d.ts +142 -0
- package/dist/contract/share.js +67 -0
- package/dist/contract/table.d.ts +162 -0
- package/dist/contract/table.js +117 -0
- package/dist/contract/tool.d.ts +122 -0
- package/dist/contract/tool.js +172 -0
- package/package.json +29 -1
package/README.md
CHANGED
|
@@ -26,6 +26,86 @@ Mutation inputs are strict: an unknown field is rejected rather than silently dr
|
|
|
26
26
|
a client never becomes a value that quietly fails to apply. Types are inferred from the schemas, so
|
|
27
27
|
`z.infer` gives you the same shape the server validates against.
|
|
28
28
|
|
|
29
|
+
Each domain has its own subpath — `/node`, `/table`, `/share`, `/tool`, `/flow`, `/flow-run`,
|
|
30
|
+
`/bundle` — and the root export carries only the shared primitives. Import from the narrowest
|
|
31
|
+
subpath that has the symbol.
|
|
32
|
+
|
|
33
|
+
## Six things that cost a first-time caller an afternoon
|
|
34
|
+
|
|
35
|
+
Every one of these was found by somebody writing against the real API and guessing.
|
|
36
|
+
|
|
37
|
+
### A refusal hides in one of two fields, never both
|
|
38
|
+
|
|
39
|
+
Intel answers failures as RFC 9457 problem details, and **which field carries the sentence depends
|
|
40
|
+
on who refused**:
|
|
41
|
+
|
|
42
|
+
| Kind of refusal | `title` | `detail` |
|
|
43
|
+
|---|---|---|
|
|
44
|
+
| Intel itself — a permission, a conflict, an invalid flow graph | the actual reason | absent |
|
|
45
|
+
| Schema validation — a bad or missing field | `Request validation failed` | the actual reason, field by field |
|
|
46
|
+
|
|
47
|
+
A client that logs only `detail` sees nothing for the first kind; one that logs only `title` sees
|
|
48
|
+
`Request validation failed` and nothing about which field. **Read both.** `code` is the stable
|
|
49
|
+
machine-readable half and is always present.
|
|
50
|
+
|
|
51
|
+
### `baseVersionId` takes `null`, and it is still required
|
|
52
|
+
|
|
53
|
+
Fields declared `nullable()` and not `optional()` are **required**: `null` is a value they take, and
|
|
54
|
+
leaving them out is refused. That is deliberate everywhere a write names the version it was made
|
|
55
|
+
against — `SaveNodeVersionInput`, `SaveAttachmentInput`, `SaveFlowVersionInput`. `null` is the only
|
|
56
|
+
way to write the first content of a node that has none, and the field stays required so a caller who
|
|
57
|
+
simply forgot it is refused instead of overwriting whatever somebody else wrote in between.
|
|
58
|
+
|
|
59
|
+
Neither `undefined` nor `""` works. Send the `currentVersionId` from `node_get`, or `null`.
|
|
60
|
+
|
|
61
|
+
### `mediaType` decides whether the browser can open what you wrote
|
|
62
|
+
|
|
63
|
+
`SaveNodeVersionInput.mediaType` defaults to `text/markdown`, and for plain text that is right. It is
|
|
64
|
+
**wrong for anything the node editor should be able to open**: the editor stores and reads
|
|
65
|
+
`BlockNoteMediaType` (`application/vnd.anchrd.intel.blocknote+json`) with a `BlockNoteDocument` body.
|
|
66
|
+
|
|
67
|
+
Content in any other shape is stored intact, indexed and searchable — and the editor opens **empty**
|
|
68
|
+
on it, because it cannot parse it. Nothing reports an error, and a save from that empty editor
|
|
69
|
+
replaces what was written. If a person is meant to edit the document afterwards, write it as a
|
|
70
|
+
`BlockNoteDocument`:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { BlockNoteDocument, BlockNoteMediaType } from "@anchrd/intel-contract/node";
|
|
74
|
+
|
|
75
|
+
const content = JSON.stringify(
|
|
76
|
+
BlockNoteDocument.parse({ format: "blocknote", schemaVersion: 1, blocks, markdown }),
|
|
77
|
+
);
|
|
78
|
+
// … then save with mediaType: BlockNoteMediaType
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### In a flow graph, a tool is a link, not a step
|
|
82
|
+
|
|
83
|
+
Flow nodes sit in three layers. `trigger` and `output` are markers, `instruction`, `condition` and
|
|
84
|
+
`subflow` are steps — and `folder`, `document`, `upload`, `table` and `tool` are **links**.
|
|
85
|
+
|
|
86
|
+
A link never stands in the chain of work. It hangs off an `instruction` or a `condition` — not off a
|
|
87
|
+
marker and not off a `subflow` — and the edge that attaches it must carry `kind: "context"`:
|
|
88
|
+
|
|
89
|
+
```jsonc
|
|
90
|
+
{ "source": "<instruction id>", "target": "<tool id>", "kind": "context" }
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
⚠️ `FlowEdge.kind` **defaults to `"flow"`**, so an edge written without it becomes an order-of-work
|
|
94
|
+
edge and the tool ends up in the chain. The graph is then refused with
|
|
95
|
+
`Node <id> is a link and must be attached to a step` — in `title`, per the first item above. A step
|
|
96
|
+
may hold several links; one link belongs to exactly one step.
|
|
97
|
+
|
|
98
|
+
### A flow is not a node, and carries no grants of its own
|
|
99
|
+
|
|
100
|
+
`node_grant_create` addresses nodes. A flow has no grants; it inherits those of the folder it lives
|
|
101
|
+
in. To change who reaches a flow, share its folder — or move the flow.
|
|
102
|
+
|
|
103
|
+
### Import takes its idempotency key as a header
|
|
104
|
+
|
|
105
|
+
Every mutation in this contract carries an `idempotencyKey` field. Bundle import is the one
|
|
106
|
+
exception, because its **body is the zip**: the key travels as the `idempotency-key` HTTP header
|
|
107
|
+
instead, and a request without it is refused.
|
|
108
|
+
|
|
29
109
|
## License
|
|
30
110
|
|
|
31
111
|
Proprietary. Published publicly for installation convenience; this is not an open-source license and
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const BundleManifestFilename = "manifest.json";
|
|
3
|
+
/**
|
|
4
|
+
* What a bundle entry can be. `flow` joins the node kinds because a flow shares the folder tree
|
|
5
|
+
* without being a node (ADR-0004), and the bundle mirrors the tree, not the tables.
|
|
6
|
+
*
|
|
7
|
+
* ⚠️ `agent` and `board` are STILL HERE, and that is the one place in this file where a value
|
|
8
|
+
* survives its feature (#390). A bundle is somebody else's file: an export written before Agents
|
|
9
|
+
* and Board were parked (#385) is a correct export, and it has to PARSE so the import can refuse it
|
|
10
|
+
* by name — with the entry, the kind and the branch the code is on. Take them out and the same
|
|
11
|
+
* bundle fails as `unexpected enum value`, which sends its holder looking for a broken file that is
|
|
12
|
+
* not broken.
|
|
13
|
+
*
|
|
14
|
+
* They belong to the wire format of a file that already exists, not to the product. Nothing may
|
|
15
|
+
* create either kind; `NodeKind` is the enum that says so.
|
|
16
|
+
*/
|
|
17
|
+
export declare const BundleEntryKind: z.ZodEnum<{
|
|
18
|
+
folder: "folder";
|
|
19
|
+
document: "document";
|
|
20
|
+
table: "table";
|
|
21
|
+
attachment: "attachment";
|
|
22
|
+
agent: "agent";
|
|
23
|
+
board: "board";
|
|
24
|
+
flow: "flow";
|
|
25
|
+
}>;
|
|
26
|
+
export type BundleEntryKind = z.infer<typeof BundleEntryKind>;
|
|
27
|
+
export declare const BundleManifestEntry: z.ZodObject<{
|
|
28
|
+
id: z.ZodString;
|
|
29
|
+
kind: z.ZodEnum<{
|
|
30
|
+
folder: "folder";
|
|
31
|
+
document: "document";
|
|
32
|
+
table: "table";
|
|
33
|
+
attachment: "attachment";
|
|
34
|
+
agent: "agent";
|
|
35
|
+
board: "board";
|
|
36
|
+
flow: "flow";
|
|
37
|
+
}>;
|
|
38
|
+
title: z.ZodString;
|
|
39
|
+
description: z.ZodNullable<z.ZodString>;
|
|
40
|
+
mediaType: z.ZodNullable<z.ZodString>;
|
|
41
|
+
path: z.ZodString;
|
|
42
|
+
}, z.core.$strict>;
|
|
43
|
+
export type BundleManifestEntry = z.infer<typeof BundleManifestEntry>;
|
|
44
|
+
export declare const BundleExclusion: z.ZodEnum<{
|
|
45
|
+
"version-history": "version-history";
|
|
46
|
+
grants: "grants";
|
|
47
|
+
"flow-runs": "flow-runs";
|
|
48
|
+
"archived-nodes": "archived-nodes";
|
|
49
|
+
}>;
|
|
50
|
+
export type BundleExclusion = z.infer<typeof BundleExclusion>;
|
|
51
|
+
export declare const BundleManifest: z.ZodObject<{
|
|
52
|
+
version: z.ZodLiteral<1>;
|
|
53
|
+
exportedAt: z.ZodISODateTime;
|
|
54
|
+
rootId: z.ZodNullable<z.ZodString>;
|
|
55
|
+
entries: z.ZodArray<z.ZodObject<{
|
|
56
|
+
id: z.ZodString;
|
|
57
|
+
kind: z.ZodEnum<{
|
|
58
|
+
folder: "folder";
|
|
59
|
+
document: "document";
|
|
60
|
+
table: "table";
|
|
61
|
+
attachment: "attachment";
|
|
62
|
+
agent: "agent";
|
|
63
|
+
board: "board";
|
|
64
|
+
flow: "flow";
|
|
65
|
+
}>;
|
|
66
|
+
title: z.ZodString;
|
|
67
|
+
description: z.ZodNullable<z.ZodString>;
|
|
68
|
+
mediaType: z.ZodNullable<z.ZodString>;
|
|
69
|
+
path: z.ZodString;
|
|
70
|
+
}, z.core.$strict>>;
|
|
71
|
+
excluded: z.ZodArray<z.ZodEnum<{
|
|
72
|
+
"version-history": "version-history";
|
|
73
|
+
grants: "grants";
|
|
74
|
+
"flow-runs": "flow-runs";
|
|
75
|
+
"archived-nodes": "archived-nodes";
|
|
76
|
+
}>>;
|
|
77
|
+
}, z.core.$strict>;
|
|
78
|
+
export type BundleManifest = z.infer<typeof BundleManifest>;
|
|
79
|
+
export declare const BundleImportResult: z.ZodObject<{
|
|
80
|
+
nodes: z.ZodNumber;
|
|
81
|
+
flows: z.ZodNumber;
|
|
82
|
+
rootNodeIds: z.ZodArray<z.ZodString>;
|
|
83
|
+
replayed: z.ZodBoolean;
|
|
84
|
+
}, z.core.$strict>;
|
|
85
|
+
export type BundleImportResult = z.infer<typeof BundleImportResult>;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { IntelId, IsoDateTime } from "./contract.js";
|
|
3
|
+
// ── Bundle export (#136) ────────────────────────────────────────────────────────────────────────
|
|
4
|
+
// The one name the importer looks for at the zip root. A different spelling would make a bundle a
|
|
5
|
+
// naked folder, so the constant lives in the contract rather than in each surface.
|
|
6
|
+
export const BundleManifestFilename = "manifest.json";
|
|
7
|
+
/**
|
|
8
|
+
* What a bundle entry can be. `flow` joins the node kinds because a flow shares the folder tree
|
|
9
|
+
* without being a node (ADR-0004), and the bundle mirrors the tree, not the tables.
|
|
10
|
+
*
|
|
11
|
+
* ⚠️ `agent` and `board` are STILL HERE, and that is the one place in this file where a value
|
|
12
|
+
* survives its feature (#390). A bundle is somebody else's file: an export written before Agents
|
|
13
|
+
* and Board were parked (#385) is a correct export, and it has to PARSE so the import can refuse it
|
|
14
|
+
* by name — with the entry, the kind and the branch the code is on. Take them out and the same
|
|
15
|
+
* bundle fails as `unexpected enum value`, which sends its holder looking for a broken file that is
|
|
16
|
+
* not broken.
|
|
17
|
+
*
|
|
18
|
+
* They belong to the wire format of a file that already exists, not to the product. Nothing may
|
|
19
|
+
* create either kind; `NodeKind` is the enum that says so.
|
|
20
|
+
*/
|
|
21
|
+
export const BundleEntryKind = z.enum([
|
|
22
|
+
"folder",
|
|
23
|
+
"document",
|
|
24
|
+
"table",
|
|
25
|
+
"attachment",
|
|
26
|
+
"agent",
|
|
27
|
+
"board",
|
|
28
|
+
"flow",
|
|
29
|
+
]);
|
|
30
|
+
// One entry of the manifest: the identity a re-import needs, next to the relative path where the
|
|
31
|
+
// bytes sit in the zip. A folder carries no media type — it has no bytes.
|
|
32
|
+
export const BundleManifestEntry = z.strictObject({
|
|
33
|
+
id: IntelId,
|
|
34
|
+
kind: BundleEntryKind,
|
|
35
|
+
title: z.string().min(1).max(240),
|
|
36
|
+
description: z.string().max(2_000).nullable(),
|
|
37
|
+
mediaType: z.string().min(1).max(160).nullable(),
|
|
38
|
+
// Relative to the zip root, forward slashes, no leading slash. Folders end with a slash so an
|
|
39
|
+
// empty folder still has an address.
|
|
40
|
+
path: z.string().min(1).max(4_000),
|
|
41
|
+
});
|
|
42
|
+
// What an export leaves out on purpose, named so a bundle says it rather than a reader guessing:
|
|
43
|
+
// version history, grants/shares, flow runs, and archived nodes are not in any bundle (#136).
|
|
44
|
+
export const BundleExclusion = z.enum(["version-history", "grants", "flow-runs", "archived-nodes"]);
|
|
45
|
+
export const BundleManifest = z.strictObject({
|
|
46
|
+
version: z.literal(1),
|
|
47
|
+
exportedAt: IsoDateTime,
|
|
48
|
+
// The node the export started at; `null` is the root of the tree — the whole installation as the
|
|
49
|
+
// exporting caller may read it.
|
|
50
|
+
rootId: IntelId.nullable(),
|
|
51
|
+
entries: z.array(BundleManifestEntry),
|
|
52
|
+
excluded: z.array(BundleExclusion),
|
|
53
|
+
});
|
|
54
|
+
// ── Bundle import (#137) ────────────────────────────────────────────────────────────────────────
|
|
55
|
+
// What one import made. Import always creates new nodes — no merge, no overwrite, no restored IDs
|
|
56
|
+
// (#137, phase 1) — so the answer is counts and the new roots, never a diff. `replayed` marks the
|
|
57
|
+
// idempotent second answer to the same key: nothing was created twice.
|
|
58
|
+
export const BundleImportResult = z.strictObject({
|
|
59
|
+
nodes: z.number().int().nonnegative(),
|
|
60
|
+
flows: z.number().int().nonnegative(),
|
|
61
|
+
rootNodeIds: z.array(IntelId),
|
|
62
|
+
replayed: z.boolean(),
|
|
63
|
+
});
|