@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.
Files changed (71) hide show
  1. package/README.md +141 -0
  2. package/dist/API/create.d.ts +33 -0
  3. package/dist/API/create.js +91 -0
  4. package/dist/API/delete.d.ts +31 -0
  5. package/dist/API/delete.js +38 -0
  6. package/dist/API/index.d.ts +13 -0
  7. package/dist/API/index.js +9 -0
  8. package/dist/API/init.d.ts +3 -0
  9. package/dist/API/init.js +8 -0
  10. package/dist/API/read.d.ts +60 -0
  11. package/dist/API/read.js +95 -0
  12. package/dist/API/search.d.ts +4 -0
  13. package/dist/API/search.js +4 -0
  14. package/dist/API/types.d.ts +23 -0
  15. package/dist/API/types.js +0 -0
  16. package/dist/API/validation.d.ts +8 -0
  17. package/dist/API/validation.js +103 -0
  18. package/dist/API/write.d.ts +30 -0
  19. package/dist/API/write.js +90 -0
  20. package/dist/CLI/arguments.d.ts +45 -0
  21. package/dist/CLI/arguments.js +263 -0
  22. package/dist/CLI/dispatch.d.ts +3 -0
  23. package/dist/CLI/dispatch.js +143 -0
  24. package/dist/CLI/errors.d.ts +10 -0
  25. package/dist/CLI/errors.js +20 -0
  26. package/dist/CLI/index.d.ts +6 -0
  27. package/dist/CLI/index.js +70 -0
  28. package/dist/CLI/io.d.ts +6 -0
  29. package/dist/CLI/io.js +28 -0
  30. package/dist/CLI/json.d.ts +9 -0
  31. package/dist/CLI/json.js +42 -0
  32. package/dist/CLI/properties.d.ts +3 -0
  33. package/dist/CLI/properties.js +22 -0
  34. package/dist/index.d.ts +1 -0
  35. package/dist/index.js +1 -0
  36. package/dist/src/storage/db/blocks.d.ts +68 -0
  37. package/dist/src/storage/db/blocks.js +185 -0
  38. package/dist/src/storage/db/edges.d.ts +61 -0
  39. package/dist/src/storage/db/edges.js +306 -0
  40. package/dist/src/storage/db/init.d.ts +22 -0
  41. package/dist/src/storage/db/init.js +108 -0
  42. package/dist/src/storage/db/nodes.d.ts +34 -0
  43. package/dist/src/storage/db/nodes.js +91 -0
  44. package/dist/src/storage/db/objects.d.ts +55 -0
  45. package/dist/src/storage/db/objects.js +123 -0
  46. package/dist/src/storage/db/schema.sql +59 -0
  47. package/dist/src/storage/index.d.ts +47 -0
  48. package/dist/src/storage/index.js +315 -0
  49. package/dist/src/storage/types.d.ts +15 -0
  50. package/dist/src/storage/types.js +1 -0
  51. package/dist/src/types/block.d.ts +12 -0
  52. package/dist/src/types/block.js +0 -0
  53. package/dist/src/types/database.d.ts +6 -0
  54. package/dist/src/types/database.js +0 -0
  55. package/dist/src/types/graph.d.ts +11 -0
  56. package/dist/src/types/graph.js +0 -0
  57. package/dist/src/types/json.d.ts +7 -0
  58. package/dist/src/types/json.js +0 -0
  59. package/dist/src/types/object.d.ts +12 -0
  60. package/dist/src/types/object.js +0 -0
  61. package/dist/src/types/workspace.d.ts +6 -0
  62. package/dist/src/types/workspace.js +0 -0
  63. package/dist/src/utils/id.d.ts +6 -0
  64. package/dist/src/utils/id.js +18 -0
  65. package/dist/src/utils/yaml.d.ts +3 -0
  66. package/dist/src/utils/yaml.js +26 -0
  67. package/dist/src/workspace/index.d.ts +1 -0
  68. package/dist/src/workspace/index.js +1 -0
  69. package/dist/src/workspace/resolution.d.ts +20 -0
  70. package/dist/src/workspace/resolution.js +90 -0
  71. 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";
@@ -0,0 +1,3 @@
1
+ import type { Database } from "bun:sqlite";
2
+ export declare function initializeWorkspace(name: string): Database;
3
+ export declare function openWorkspace(name: string): Database;
@@ -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[];
@@ -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,4 @@
1
+ import { searchEntities } from "../src/storage";
2
+ export function search(db, query, type) {
3
+ return searchEntities(db, query, type);
4
+ }
@@ -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
+ }