@graypirate/tabula 1.0.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 +141 -0
- package/dist/API/create.d.ts +33 -0
- package/dist/API/create.js +91 -0
- package/dist/API/delete.d.ts +31 -0
- package/dist/API/delete.js +38 -0
- package/dist/API/index.d.ts +13 -0
- package/dist/API/index.js +9 -0
- package/dist/API/init.d.ts +3 -0
- package/dist/API/init.js +8 -0
- package/dist/API/read.d.ts +60 -0
- package/dist/API/read.js +95 -0
- package/dist/API/search.d.ts +4 -0
- package/dist/API/search.js +4 -0
- package/dist/API/types.d.ts +23 -0
- package/dist/API/types.js +0 -0
- package/dist/API/validation.d.ts +8 -0
- package/dist/API/validation.js +103 -0
- package/dist/API/write.d.ts +30 -0
- package/dist/API/write.js +90 -0
- package/dist/CLI/arguments.d.ts +45 -0
- package/dist/CLI/arguments.js +263 -0
- package/dist/CLI/dispatch.d.ts +3 -0
- package/dist/CLI/dispatch.js +143 -0
- package/dist/CLI/errors.d.ts +10 -0
- package/dist/CLI/errors.js +20 -0
- package/dist/CLI/index.d.ts +6 -0
- package/dist/CLI/index.js +70 -0
- package/dist/CLI/io.d.ts +6 -0
- package/dist/CLI/io.js +28 -0
- package/dist/CLI/json.d.ts +9 -0
- package/dist/CLI/json.js +42 -0
- package/dist/CLI/properties.d.ts +3 -0
- package/dist/CLI/properties.js +22 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/src/storage/db/blocks.d.ts +68 -0
- package/dist/src/storage/db/blocks.js +185 -0
- package/dist/src/storage/db/edges.d.ts +61 -0
- package/dist/src/storage/db/edges.js +306 -0
- package/dist/src/storage/db/init.d.ts +22 -0
- package/dist/src/storage/db/init.js +108 -0
- package/dist/src/storage/db/nodes.d.ts +34 -0
- package/dist/src/storage/db/nodes.js +91 -0
- package/dist/src/storage/db/objects.d.ts +55 -0
- package/dist/src/storage/db/objects.js +123 -0
- package/dist/src/storage/db/schema.sql +59 -0
- package/dist/src/storage/index.d.ts +47 -0
- package/dist/src/storage/index.js +315 -0
- package/dist/src/storage/types.d.ts +15 -0
- package/dist/src/storage/types.js +1 -0
- package/dist/src/types/block.d.ts +12 -0
- package/dist/src/types/block.js +0 -0
- package/dist/src/types/database.d.ts +6 -0
- package/dist/src/types/database.js +0 -0
- package/dist/src/types/graph.d.ts +11 -0
- package/dist/src/types/graph.js +0 -0
- package/dist/src/types/json.d.ts +7 -0
- package/dist/src/types/json.js +0 -0
- package/dist/src/types/object.d.ts +12 -0
- package/dist/src/types/object.js +0 -0
- package/dist/src/types/workspace.d.ts +6 -0
- package/dist/src/types/workspace.js +0 -0
- package/dist/src/utils/id.d.ts +6 -0
- package/dist/src/utils/id.js +18 -0
- package/dist/src/utils/yaml.d.ts +3 -0
- package/dist/src/utils/yaml.js +26 -0
- package/dist/src/workspace/index.d.ts +1 -0
- package/dist/src/workspace/index.js +1 -0
- package/dist/src/workspace/resolution.d.ts +20 -0
- package/dist/src/workspace/resolution.js +90 -0
- package/package.json +43 -0
package/README.md
ADDED
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# Tabula Core
|
|
2
|
+
|
|
3
|
+
Core API and command-line interface for Tabula.
|
|
4
|
+
|
|
5
|
+
## Global Installation
|
|
6
|
+
|
|
7
|
+
After the package is published, install the Core package and CLI from the npm registry:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
bun add --global @graypirate/tabula
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Verify:
|
|
14
|
+
```bash
|
|
15
|
+
command -v tabula
|
|
16
|
+
tabula list
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Upgrade or remove the Core package with Bun:
|
|
20
|
+
```bash
|
|
21
|
+
bun update --global @graypirate/tabula
|
|
22
|
+
bun remove --global @graypirate/tabula
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
## CLI
|
|
26
|
+
|
|
27
|
+
Most commands follow one of these forms:
|
|
28
|
+
```bash
|
|
29
|
+
tabula <command> [arguments] [options]
|
|
30
|
+
tabula <command> <subcommand> [arguments] [options]
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Current command groups:
|
|
34
|
+
- `init [options]`
|
|
35
|
+
- `list [id] [options]`
|
|
36
|
+
- `read [id] [options]`
|
|
37
|
+
- `create object [options]`
|
|
38
|
+
- `create block [options]`
|
|
39
|
+
- `write [options]`
|
|
40
|
+
- `search <query> [options]`
|
|
41
|
+
- `delete [id] [options]`
|
|
42
|
+
|
|
43
|
+
Common argument patterns:
|
|
44
|
+
- `[id]` is an optional entity identifier for commands that can target either a root context or a specific entity.
|
|
45
|
+
- `<query>` is a required search string.
|
|
46
|
+
- `[options]` includes flags such as `--workspace`, `--parent`, `--name`, `--content`, `--type`, and repeatable `--property key=value`.
|
|
47
|
+
|
|
48
|
+
Entity IDs identify their type:
|
|
49
|
+
- `d_...`: workspace
|
|
50
|
+
- `o_...`: Object
|
|
51
|
+
- `b_...`: Block
|
|
52
|
+
|
|
53
|
+
Commands that operate inside a workspace require `--workspace NAME`.
|
|
54
|
+
|
|
55
|
+
### Workspaces
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
tabula init --workspace example
|
|
59
|
+
tabula list
|
|
60
|
+
tabula read --workspace example
|
|
61
|
+
tabula list --workspace example
|
|
62
|
+
tabula delete --workspace example
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
`init` creates or opens `~/.tabula/<name>.sqlite` and returns workspace
|
|
66
|
+
metadata. Bare `list` returns managed workspace names. `read --workspace`
|
|
67
|
+
returns metadata, while `list --workspace` returns ordered root Object IDs.
|
|
68
|
+
|
|
69
|
+
Workspace deletion removes the SQLite file and its sidecars. Interactive
|
|
70
|
+
terminals ask for confirmation; non-interactive commands do not prompt.
|
|
71
|
+
|
|
72
|
+
### Create Objects and Blocks
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
tabula create object \
|
|
76
|
+
--workspace example \
|
|
77
|
+
--name "Project" \
|
|
78
|
+
--property status=active
|
|
79
|
+
|
|
80
|
+
tabula create block \
|
|
81
|
+
--workspace example \
|
|
82
|
+
--parent o_parent \
|
|
83
|
+
--content "First task" \
|
|
84
|
+
--property complete=false
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Objects may be created at the workspace root or under an Object or Block.
|
|
88
|
+
Blocks require `--parent`. Repeat `--property key=value` to add properties;
|
|
89
|
+
values are parsed as JSON when valid and otherwise remain strings.
|
|
90
|
+
|
|
91
|
+
### Write Recursive Trees
|
|
92
|
+
|
|
93
|
+
`write` accepts exactly one recursive Object or Block JSON value from stdin:
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
tabula write --workspace example <<'JSON'
|
|
97
|
+
{
|
|
98
|
+
"type": "object",
|
|
99
|
+
"name": "Document",
|
|
100
|
+
"properties": { "status": "draft" },
|
|
101
|
+
"children": [
|
|
102
|
+
{
|
|
103
|
+
"type": "block",
|
|
104
|
+
"content": "Introduction",
|
|
105
|
+
"properties": { "level": 1 },
|
|
106
|
+
"children": []
|
|
107
|
+
}
|
|
108
|
+
]
|
|
109
|
+
}
|
|
110
|
+
JSON
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
New entities omit `id`. Supplying an existing ID replaces that entity.
|
|
114
|
+
Submitted `children` arrays are complete replacements: omitted child subtrees
|
|
115
|
+
are deleted. Existing children included by ID may be moved. Object roots may be
|
|
116
|
+
written at workspace root; Block roots require `--parent ID`.
|
|
117
|
+
|
|
118
|
+
### Read, List, Search, and Delete
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
tabula read o_example --workspace example
|
|
122
|
+
tabula list o_example --workspace example
|
|
123
|
+
tabula search "draft" --workspace example
|
|
124
|
+
tabula search "draft" --workspace example --type block
|
|
125
|
+
tabula delete b_example --workspace example
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
`read ID` returns the parent ID and complete recursive entity tree. `list ID`
|
|
129
|
+
returns ordered direct child IDs. `search` checks Object names and properties
|
|
130
|
+
plus Block content and properties. Entity deletion removes the entity and all
|
|
131
|
+
descendants.
|
|
132
|
+
|
|
133
|
+
### Output and Errors
|
|
134
|
+
|
|
135
|
+
Successful commands write one compact JSON value plus a newline to stdout and
|
|
136
|
+
exit `0`. Input and validation failures write structured JSON to stderr and
|
|
137
|
+
exit `2`; operation failures exit `1`.
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{"error":{"code":"MISSING_OPTION","message":"Required option missing: --workspace"}}
|
|
141
|
+
```
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite";
|
|
2
|
+
import type { JSONRecord } from "../src/types/json";
|
|
3
|
+
import { type BlockResult, type Create, type Result, type ObjectResult } from "./types";
|
|
4
|
+
type CreateOptions = {
|
|
5
|
+
parentID?: string;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Creates an object or block with no children.
|
|
9
|
+
* @param db - The SQLite database backing the workspace
|
|
10
|
+
* @param input - The object or block creation input
|
|
11
|
+
* @param options - Optional parent placement
|
|
12
|
+
* @returns The parent-aware created entity result
|
|
13
|
+
*/
|
|
14
|
+
export declare function create(db: Database, input: Create, options?: CreateOptions): Result;
|
|
15
|
+
/**
|
|
16
|
+
* Creates an object with no children.
|
|
17
|
+
* @param db - The SQLite database backing the workspace
|
|
18
|
+
* @param name - The object name
|
|
19
|
+
* @param properties - Optional object properties
|
|
20
|
+
* @param options - Optional parent placement
|
|
21
|
+
* @returns The created recursive object
|
|
22
|
+
*/
|
|
23
|
+
export declare function createObject(db: Database, name: string, properties?: JSONRecord, options?: CreateOptions): ObjectResult;
|
|
24
|
+
/**
|
|
25
|
+
* Creates a block with no children.
|
|
26
|
+
* @param db - The SQLite database backing the workspace
|
|
27
|
+
* @param content - The block text content
|
|
28
|
+
* @param properties - Optional block properties
|
|
29
|
+
* @param options - Optional parent placement
|
|
30
|
+
* @returns The created recursive block
|
|
31
|
+
*/
|
|
32
|
+
export declare function createBlock(db: Database, content: string, properties?: JSONRecord, options?: CreateOptions): BlockResult;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { blockExists, createEntity as createStoredEntity, objectExists, readEntityParentID, readEntityTree, } from "../src/storage";
|
|
2
|
+
import { createBlockID, createObjID } from "../src/utils/id";
|
|
3
|
+
import {} from "./types";
|
|
4
|
+
/**
|
|
5
|
+
* Creates an object or block with no children.
|
|
6
|
+
* @param db - The SQLite database backing the workspace
|
|
7
|
+
* @param input - The object or block creation input
|
|
8
|
+
* @param options - Optional parent placement
|
|
9
|
+
* @returns The parent-aware created entity result
|
|
10
|
+
*/
|
|
11
|
+
export function create(db, input, options = {}) {
|
|
12
|
+
const entity = buildEntity(db, input);
|
|
13
|
+
createStoredEntity(db, entity, options.parentID ?? undefined);
|
|
14
|
+
return entityResult(db, entity.id);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Creates an object with no children.
|
|
18
|
+
* @param db - The SQLite database backing the workspace
|
|
19
|
+
* @param name - The object name
|
|
20
|
+
* @param properties - Optional object properties
|
|
21
|
+
* @param options - Optional parent placement
|
|
22
|
+
* @returns The created recursive object
|
|
23
|
+
*/
|
|
24
|
+
export function createObject(db, name, properties = {}, options = {}) {
|
|
25
|
+
const result = create(db, {
|
|
26
|
+
type: "object",
|
|
27
|
+
name,
|
|
28
|
+
properties,
|
|
29
|
+
}, options);
|
|
30
|
+
assertObjectResult(result);
|
|
31
|
+
return result;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Creates a block with no children.
|
|
35
|
+
* @param db - The SQLite database backing the workspace
|
|
36
|
+
* @param content - The block text content
|
|
37
|
+
* @param properties - Optional block properties
|
|
38
|
+
* @param options - Optional parent placement
|
|
39
|
+
* @returns The created recursive block
|
|
40
|
+
*/
|
|
41
|
+
export function createBlock(db, content, properties = {}, options = {}) {
|
|
42
|
+
const result = create(db, {
|
|
43
|
+
type: "block",
|
|
44
|
+
content,
|
|
45
|
+
properties,
|
|
46
|
+
}, options);
|
|
47
|
+
assertBlockResult(result);
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
function buildEntity(db, input) {
|
|
51
|
+
if (input.type === "object") {
|
|
52
|
+
return {
|
|
53
|
+
id: createAvailableID(createObjID, (id) => objectExists(db, id)),
|
|
54
|
+
type: "object",
|
|
55
|
+
name: input.name,
|
|
56
|
+
properties: input.properties ?? {},
|
|
57
|
+
children: [],
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
id: createAvailableID(createBlockID, (id) => blockExists(db, id)),
|
|
62
|
+
type: "block",
|
|
63
|
+
content: input.content,
|
|
64
|
+
properties: input.properties ?? {},
|
|
65
|
+
children: [],
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function entityResult(db, entityID) {
|
|
69
|
+
return {
|
|
70
|
+
parentID: readEntityParentID(db, entityID),
|
|
71
|
+
entity: readEntityTree(db, entityID),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function assertObjectResult(result) {
|
|
75
|
+
if (result.entity.type !== "object") {
|
|
76
|
+
throw new Error("Expected object result");
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function assertBlockResult(result) {
|
|
80
|
+
if (result.entity.type !== "block") {
|
|
81
|
+
throw new Error("Expected block result");
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** Generates an entity ID that is not already stored. */
|
|
85
|
+
function createAvailableID(createID, exists) {
|
|
86
|
+
let id = createID();
|
|
87
|
+
while (exists(id)) {
|
|
88
|
+
id = createID();
|
|
89
|
+
}
|
|
90
|
+
return id;
|
|
91
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite";
|
|
2
|
+
import type { BlockID } from "../src/types/block";
|
|
3
|
+
import type { ObjID } from "../src/types/object";
|
|
4
|
+
/**
|
|
5
|
+
* Deletes the managed SQLite files for a workspace name.
|
|
6
|
+
* @param workspace - Managed workspace name
|
|
7
|
+
* @returns True when the workspace was deleted
|
|
8
|
+
* @throws If the workspace does not exist
|
|
9
|
+
*/
|
|
10
|
+
export declare function deleteWorkspace(workspace: string): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Deletes an object or block and its recursive containment subtree.
|
|
13
|
+
* @param db - The SQLite database backing the workspace
|
|
14
|
+
* @param entityID - The object or block ID to delete
|
|
15
|
+
* @returns True if the entity existed and was deleted
|
|
16
|
+
*/
|
|
17
|
+
export declare function deleteEntity(db: Database, entityID: string): boolean;
|
|
18
|
+
/**
|
|
19
|
+
* Deletes an object and its recursive containment subtree.
|
|
20
|
+
* @param db - The SQLite database backing the workspace
|
|
21
|
+
* @param objectID - The object ID to delete
|
|
22
|
+
* @returns True if the object existed and was deleted
|
|
23
|
+
*/
|
|
24
|
+
export declare function deleteObject(db: Database, objectID: ObjID): boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Deletes a block and its recursive containment subtree.
|
|
27
|
+
* @param db - The SQLite database backing the workspace
|
|
28
|
+
* @param blockID - The block ID to delete
|
|
29
|
+
* @returns True if the block existed and was deleted
|
|
30
|
+
*/
|
|
31
|
+
export declare function deleteBlock(db: Database, blockID: BlockID): boolean;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { deleteEntityTree } from "../src/storage";
|
|
2
|
+
import { deleteWorkspaceFiles } from "../src/workspace";
|
|
3
|
+
/**
|
|
4
|
+
* Deletes the managed SQLite files for a workspace name.
|
|
5
|
+
* @param workspace - Managed workspace name
|
|
6
|
+
* @returns True when the workspace was deleted
|
|
7
|
+
* @throws If the workspace does not exist
|
|
8
|
+
*/
|
|
9
|
+
export function deleteWorkspace(workspace) {
|
|
10
|
+
return deleteWorkspaceFiles(workspace);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Deletes an object or block and its recursive containment subtree.
|
|
14
|
+
* @param db - The SQLite database backing the workspace
|
|
15
|
+
* @param entityID - The object or block ID to delete
|
|
16
|
+
* @returns True if the entity existed and was deleted
|
|
17
|
+
*/
|
|
18
|
+
export function deleteEntity(db, entityID) {
|
|
19
|
+
return deleteEntityTree(db, entityID);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Deletes an object and its recursive containment subtree.
|
|
23
|
+
* @param db - The SQLite database backing the workspace
|
|
24
|
+
* @param objectID - The object ID to delete
|
|
25
|
+
* @returns True if the object existed and was deleted
|
|
26
|
+
*/
|
|
27
|
+
export function deleteObject(db, objectID) {
|
|
28
|
+
return deleteEntity(db, objectID);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Deletes a block and its recursive containment subtree.
|
|
32
|
+
* @param db - The SQLite database backing the workspace
|
|
33
|
+
* @param blockID - The block ID to delete
|
|
34
|
+
* @returns True if the block existed and was deleted
|
|
35
|
+
*/
|
|
36
|
+
export function deleteBlock(db, blockID) {
|
|
37
|
+
return deleteEntity(db, blockID);
|
|
38
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from "./create";
|
|
2
|
+
export * from "./delete";
|
|
3
|
+
export * from "./init";
|
|
4
|
+
export * from "./read";
|
|
5
|
+
export * from "./search";
|
|
6
|
+
export * from "./types";
|
|
7
|
+
export * from "./validation";
|
|
8
|
+
export * from "./write";
|
|
9
|
+
export { InvalidWorkspaceNameError, listWorkspaceNames, validateWorkspaceName, } from "../src/workspace";
|
|
10
|
+
export type { Block, BlockID, BlockMetadata, } from "../src/types/block";
|
|
11
|
+
export type { Entity, EntityID, EntityParentID, EntityReference, EntityType, } from "../src/types/graph";
|
|
12
|
+
export type { Obj, ObjID, ObjMetadata, } from "../src/types/object";
|
|
13
|
+
export type { WorkspaceID, WorkspaceMetadata, } from "../src/types/workspace";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export * from "./create";
|
|
2
|
+
export * from "./delete";
|
|
3
|
+
export * from "./init";
|
|
4
|
+
export * from "./read";
|
|
5
|
+
export * from "./search";
|
|
6
|
+
export * from "./types";
|
|
7
|
+
export * from "./validation";
|
|
8
|
+
export * from "./write";
|
|
9
|
+
export { InvalidWorkspaceNameError, listWorkspaceNames, validateWorkspaceName, } from "../src/workspace";
|
package/dist/API/init.js
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { resolveInitializedWorkspaceDatabasePath, resolveWorkspaceDatabasePath, } from "../src/workspace";
|
|
2
|
+
import { initializeStorage, openStorage, } from "../src/storage";
|
|
3
|
+
export function initializeWorkspace(name) {
|
|
4
|
+
return initializeStorage(resolveInitializedWorkspaceDatabasePath(name), name);
|
|
5
|
+
}
|
|
6
|
+
export function openWorkspace(name) {
|
|
7
|
+
return openStorage(resolveWorkspaceDatabasePath(name));
|
|
8
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite";
|
|
2
|
+
import type { BlockID } from "../src/types/block";
|
|
3
|
+
import type { EntityID } from "../src/types/graph";
|
|
4
|
+
import type { ObjID } from "../src/types/object";
|
|
5
|
+
import type { WorkspaceMetadata } from "../src/types/workspace";
|
|
6
|
+
import { type BlockResult, type Result, type ObjectResult } from "./types";
|
|
7
|
+
/**
|
|
8
|
+
* Reads an object or block as a parent-aware recursive entity result.
|
|
9
|
+
* @param db - The SQLite database backing the workspace
|
|
10
|
+
* @param id - The object or block ID to read
|
|
11
|
+
* @returns The parent-aware recursive entity result
|
|
12
|
+
*/
|
|
13
|
+
export declare function readEntity(db: Database, id: string): Result;
|
|
14
|
+
/**
|
|
15
|
+
* Reads workspace metadata.
|
|
16
|
+
* @param db - The SQLite database backing the workspace
|
|
17
|
+
* @returns The workspace metadata
|
|
18
|
+
*/
|
|
19
|
+
export declare function readWorkspace(db: Database): WorkspaceMetadata;
|
|
20
|
+
/**
|
|
21
|
+
* Reads an object as a recursive entity tree.
|
|
22
|
+
* @param db - The SQLite database backing the workspace
|
|
23
|
+
* @param objectID - The object ID to read
|
|
24
|
+
* @returns The recursive object
|
|
25
|
+
*/
|
|
26
|
+
export declare function readObject(db: Database, objectID: ObjID): ObjectResult;
|
|
27
|
+
/**
|
|
28
|
+
* Reads a block as a recursive entity tree.
|
|
29
|
+
* @param db - The SQLite database backing the workspace
|
|
30
|
+
* @param blockID - The block ID to read
|
|
31
|
+
* @returns The recursive block
|
|
32
|
+
*/
|
|
33
|
+
export declare function readBlock(db: Database, blockID: BlockID): BlockResult;
|
|
34
|
+
/**
|
|
35
|
+
* Lists the workspace shape as ordered root object IDs.
|
|
36
|
+
* @param db - The SQLite database backing the workspace
|
|
37
|
+
* @returns The ordered root object IDs
|
|
38
|
+
*/
|
|
39
|
+
export declare function listWorkspace(db: Database): ObjID[];
|
|
40
|
+
/**
|
|
41
|
+
* Lists the direct child IDs for an object or block.
|
|
42
|
+
* @param db - The SQLite database backing the workspace
|
|
43
|
+
* @param entityID - The object or block ID to list
|
|
44
|
+
* @returns The ordered direct child IDs
|
|
45
|
+
*/
|
|
46
|
+
export declare function listEntity(db: Database, entityID: EntityID): EntityID[];
|
|
47
|
+
/**
|
|
48
|
+
* Lists direct child IDs for an object.
|
|
49
|
+
* @param db - The SQLite database backing the workspace
|
|
50
|
+
* @param objectID - The object ID to list
|
|
51
|
+
* @returns The ordered direct child IDs
|
|
52
|
+
*/
|
|
53
|
+
export declare function listObject(db: Database, objectID: ObjID): EntityID[];
|
|
54
|
+
/**
|
|
55
|
+
* Lists direct child IDs for a block.
|
|
56
|
+
* @param db - The SQLite database backing the workspace
|
|
57
|
+
* @param blockID - The block ID to list
|
|
58
|
+
* @returns The ordered direct child IDs
|
|
59
|
+
*/
|
|
60
|
+
export declare function listBlock(db: Database, blockID: BlockID): EntityID[];
|
package/dist/API/read.js
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { readDatabaseRootObjects, readDirectEntityChildIDs, readDatabaseMetadata, readEntityParentID, readEntityTree, } from "../src/storage";
|
|
2
|
+
import {} from "./types";
|
|
3
|
+
// Read functions
|
|
4
|
+
/**
|
|
5
|
+
* Reads an object or block as a parent-aware recursive entity result.
|
|
6
|
+
* @param db - The SQLite database backing the workspace
|
|
7
|
+
* @param id - The object or block ID to read
|
|
8
|
+
* @returns The parent-aware recursive entity result
|
|
9
|
+
*/
|
|
10
|
+
export function readEntity(db, id) {
|
|
11
|
+
return entityResult(db, readEntityTree(db, id));
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Reads workspace metadata.
|
|
15
|
+
* @param db - The SQLite database backing the workspace
|
|
16
|
+
* @returns The workspace metadata
|
|
17
|
+
*/
|
|
18
|
+
export function readWorkspace(db) {
|
|
19
|
+
return readDatabaseMetadata(db);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Reads an object as a recursive entity tree.
|
|
23
|
+
* @param db - The SQLite database backing the workspace
|
|
24
|
+
* @param objectID - The object ID to read
|
|
25
|
+
* @returns The recursive object
|
|
26
|
+
*/
|
|
27
|
+
export function readObject(db, objectID) {
|
|
28
|
+
const result = readEntity(db, objectID);
|
|
29
|
+
assertObjectResult(result);
|
|
30
|
+
return result;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Reads a block as a recursive entity tree.
|
|
34
|
+
* @param db - The SQLite database backing the workspace
|
|
35
|
+
* @param blockID - The block ID to read
|
|
36
|
+
* @returns The recursive block
|
|
37
|
+
*/
|
|
38
|
+
export function readBlock(db, blockID) {
|
|
39
|
+
const result = readEntity(db, blockID);
|
|
40
|
+
assertBlockResult(result);
|
|
41
|
+
return result;
|
|
42
|
+
}
|
|
43
|
+
// List functions
|
|
44
|
+
/**
|
|
45
|
+
* Lists the workspace shape as ordered root object IDs.
|
|
46
|
+
* @param db - The SQLite database backing the workspace
|
|
47
|
+
* @returns The ordered root object IDs
|
|
48
|
+
*/
|
|
49
|
+
export function listWorkspace(db) {
|
|
50
|
+
return readDatabaseRootObjects(db);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Lists the direct child IDs for an object or block.
|
|
54
|
+
* @param db - The SQLite database backing the workspace
|
|
55
|
+
* @param entityID - The object or block ID to list
|
|
56
|
+
* @returns The ordered direct child IDs
|
|
57
|
+
*/
|
|
58
|
+
export function listEntity(db, entityID) {
|
|
59
|
+
return readDirectEntityChildIDs(db, entityID);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Lists direct child IDs for an object.
|
|
63
|
+
* @param db - The SQLite database backing the workspace
|
|
64
|
+
* @param objectID - The object ID to list
|
|
65
|
+
* @returns The ordered direct child IDs
|
|
66
|
+
*/
|
|
67
|
+
export function listObject(db, objectID) {
|
|
68
|
+
return listEntity(db, objectID);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Lists direct child IDs for a block.
|
|
72
|
+
* @param db - The SQLite database backing the workspace
|
|
73
|
+
* @param blockID - The block ID to list
|
|
74
|
+
* @returns The ordered direct child IDs
|
|
75
|
+
*/
|
|
76
|
+
export function listBlock(db, blockID) {
|
|
77
|
+
return listEntity(db, blockID);
|
|
78
|
+
}
|
|
79
|
+
// Internal helpers
|
|
80
|
+
function entityResult(db, entity) {
|
|
81
|
+
return {
|
|
82
|
+
parentID: readEntityParentID(db, entity.id),
|
|
83
|
+
entity,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function assertObjectResult(result) {
|
|
87
|
+
if (result.entity.type !== "object") {
|
|
88
|
+
throw new Error("Expected object result");
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function assertBlockResult(result) {
|
|
92
|
+
if (result.entity.type !== "block") {
|
|
93
|
+
throw new Error("Expected block result");
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { Database } from "bun:sqlite";
|
|
2
|
+
import { type SearchResult, type SearchType } from "../src/storage";
|
|
3
|
+
export type { SearchResult, SearchType } from "../src/storage";
|
|
4
|
+
export declare function search(db: Database, query: string, type?: SearchType): SearchResult[];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Block, BlockID } from "../src/types/block";
|
|
2
|
+
import type { Entity, EntityParentID } from "../src/types/graph";
|
|
3
|
+
import type { Obj, ObjID } from "../src/types/object";
|
|
4
|
+
export type { EntityParentID } from "../src/types/graph";
|
|
5
|
+
export type { JSONPrimitive, JSONRecord, JSONValue } from "../src/types/json";
|
|
6
|
+
export type Create = ObjectCreate | BlockCreate;
|
|
7
|
+
export type ObjectCreate = Omit<Obj, "id" | "children">;
|
|
8
|
+
export type BlockCreate = Omit<Block, "id" | "children">;
|
|
9
|
+
export interface Result<T extends Entity = Entity> {
|
|
10
|
+
parentID: EntityParentID | null;
|
|
11
|
+
entity: T;
|
|
12
|
+
}
|
|
13
|
+
export type ObjectResult = Result<Obj>;
|
|
14
|
+
export type BlockResult = Result<Block>;
|
|
15
|
+
export type Write = ObjectWrite | BlockWrite;
|
|
16
|
+
export type BlockWrite = Omit<Block, "id" | "children"> & {
|
|
17
|
+
id?: BlockID;
|
|
18
|
+
children: Write[];
|
|
19
|
+
};
|
|
20
|
+
export type ObjectWrite = Omit<Obj, "id" | "children"> & {
|
|
21
|
+
id?: ObjID;
|
|
22
|
+
children: Write[];
|
|
23
|
+
};
|
|
File without changes
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Write } from "./types";
|
|
2
|
+
export declare class TabulaInputError extends Error {
|
|
3
|
+
readonly code: string;
|
|
4
|
+
readonly details?: unknown | undefined;
|
|
5
|
+
readonly name = "TabulaInputError";
|
|
6
|
+
constructor(code: string, message: string, details?: unknown | undefined);
|
|
7
|
+
}
|
|
8
|
+
export declare function validateWriteInput(value: unknown): Write;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const blockFields = new Set(["id", "type", "content", "properties", "children"]);
|
|
2
|
+
const objectFields = new Set(["id", "type", "name", "properties", "children"]);
|
|
3
|
+
export class TabulaInputError extends Error {
|
|
4
|
+
code;
|
|
5
|
+
details;
|
|
6
|
+
name = "TabulaInputError";
|
|
7
|
+
constructor(code, message, details) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.code = code;
|
|
10
|
+
this.details = details;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
export function validateWriteInput(value) {
|
|
14
|
+
const explicitIDs = new Set();
|
|
15
|
+
return validateEntityWrite(value, "$", explicitIDs);
|
|
16
|
+
}
|
|
17
|
+
function validateEntityWrite(value, path, explicitIDs) {
|
|
18
|
+
const object = requireObject(value, path);
|
|
19
|
+
const type = requiredString(object, "type", `${path}.type`);
|
|
20
|
+
if (type === "object") {
|
|
21
|
+
return validateObjectWrite(object, path, explicitIDs);
|
|
22
|
+
}
|
|
23
|
+
if (type === "block") {
|
|
24
|
+
return validateBlockWrite(object, path, explicitIDs);
|
|
25
|
+
}
|
|
26
|
+
throw inputError("INVALID_ENTITY_TYPE", `Invalid entity type at ${path}.type: ${type}`, {
|
|
27
|
+
path: `${path}.type`,
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
function validateObjectWrite(value, path, explicitIDs) {
|
|
31
|
+
rejectUnknownFields(value, objectFields, path);
|
|
32
|
+
const id = optionalID(value, "id", "o_", `${path}.id`, explicitIDs);
|
|
33
|
+
const children = requiredArray(value, "children", `${path}.children`);
|
|
34
|
+
return {
|
|
35
|
+
...(id === undefined ? {} : { id }),
|
|
36
|
+
type: "object",
|
|
37
|
+
name: requiredString(value, "name", `${path}.name`),
|
|
38
|
+
...optionalProperties(value, `${path}.properties`),
|
|
39
|
+
children: children.map((child, index) => validateEntityWrite(child, `${path}.children[${index}]`, explicitIDs)),
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function validateBlockWrite(value, path, explicitIDs) {
|
|
43
|
+
rejectUnknownFields(value, blockFields, path);
|
|
44
|
+
const id = optionalID(value, "id", "b_", `${path}.id`, explicitIDs);
|
|
45
|
+
const children = requiredArray(value, "children", `${path}.children`);
|
|
46
|
+
return {
|
|
47
|
+
...(id === undefined ? {} : { id }),
|
|
48
|
+
type: "block",
|
|
49
|
+
content: requiredString(value, "content", `${path}.content`),
|
|
50
|
+
...optionalProperties(value, `${path}.properties`),
|
|
51
|
+
children: children.map((child, index) => validateEntityWrite(child, `${path}.children[${index}]`, explicitIDs)),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
function requireObject(value, path) {
|
|
55
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
56
|
+
throw inputError("INVALID_OBJECT", `Expected object at ${path}`, { path });
|
|
57
|
+
}
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
function rejectUnknownFields(value, allowed, path) {
|
|
61
|
+
for (const field of Object.keys(value)) {
|
|
62
|
+
if (!allowed.has(field)) {
|
|
63
|
+
throw inputError("UNKNOWN_FIELD", `Unknown field at ${path}: ${field}`, { path, field });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
function requiredString(value, field, path) {
|
|
68
|
+
const result = value[field];
|
|
69
|
+
if (typeof result !== "string") {
|
|
70
|
+
throw inputError("INVALID_FIELD", `Expected string at ${path}`, { path });
|
|
71
|
+
}
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
function requiredArray(value, field, path) {
|
|
75
|
+
const result = value[field];
|
|
76
|
+
if (!Array.isArray(result)) {
|
|
77
|
+
throw inputError("INVALID_FIELD", `Expected array at ${path}`, { path });
|
|
78
|
+
}
|
|
79
|
+
return result;
|
|
80
|
+
}
|
|
81
|
+
function optionalID(value, field, prefix, path, explicitIDs) {
|
|
82
|
+
if (!Object.hasOwn(value, field)) {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
const id = requiredString(value, field, path);
|
|
86
|
+
if (!id.startsWith(prefix)) {
|
|
87
|
+
throw inputError("INVALID_ID", `Expected ${prefix} ID at ${path}: ${id}`, { path });
|
|
88
|
+
}
|
|
89
|
+
if (explicitIDs.has(id)) {
|
|
90
|
+
throw inputError("DUPLICATE_ENTITY_ID", `Duplicate entity ID: ${id}`, { path });
|
|
91
|
+
}
|
|
92
|
+
explicitIDs.add(id);
|
|
93
|
+
return id;
|
|
94
|
+
}
|
|
95
|
+
function optionalProperties(value, path) {
|
|
96
|
+
if (!Object.hasOwn(value, "properties")) {
|
|
97
|
+
return {};
|
|
98
|
+
}
|
|
99
|
+
return { properties: requireObject(value.properties, path) };
|
|
100
|
+
}
|
|
101
|
+
function inputError(code, message, details) {
|
|
102
|
+
return new TabulaInputError(code, message, details);
|
|
103
|
+
}
|