@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
@@ -0,0 +1,6 @@
1
+ export type ConfirmationOptions = {
2
+ isTTY?: boolean;
3
+ readResponse?: (prompt: string) => Promise<string | null>;
4
+ };
5
+ export declare function readStdin(required: boolean, isTTY?: boolean | undefined, read?: () => Promise<string>): Promise<string>;
6
+ export declare function confirmationDialogue(prompt: string, options?: ConfirmationOptions): Promise<boolean>;
package/dist/CLI/io.js ADDED
@@ -0,0 +1,28 @@
1
+ import { createInterface } from "node:readline/promises";
2
+ export async function readStdin(required, isTTY = process.stdin.isTTY, read = () => Bun.stdin.text()) {
3
+ return required && isTTY !== true ? await read() : "";
4
+ }
5
+ export async function confirmationDialogue(prompt, options = {}) {
6
+ const isTTY = options.isTTY ?? process.stdin.isTTY;
7
+ if (isTTY !== true) {
8
+ return true;
9
+ }
10
+ const response = await (options.readResponse ?? readInteractiveResponse)(`${prompt} (y/n)`);
11
+ const normalized = response?.trim().toLowerCase();
12
+ return normalized === "y" || normalized === "yes";
13
+ }
14
+ async function readInteractiveResponse(prompt) {
15
+ const readline = createInterface({
16
+ input: process.stdin,
17
+ output: process.stderr,
18
+ });
19
+ try {
20
+ return await readline.question(prompt);
21
+ }
22
+ catch {
23
+ return null;
24
+ }
25
+ finally {
26
+ readline.close();
27
+ }
28
+ }
@@ -0,0 +1,9 @@
1
+ import type { BlockWrite, ObjectWrite } from "../index.js";
2
+ export type WriteInput = {
3
+ entity: "block";
4
+ value: BlockWrite;
5
+ } | {
6
+ entity: "object";
7
+ value: ObjectWrite;
8
+ };
9
+ export declare function parseWriteInput(input: string): WriteInput;
@@ -0,0 +1,42 @@
1
+ import { TabulaInputError, validateWriteInput } from "../index.js";
2
+ import { CLIInputError } from "./errors.js";
3
+ export function parseWriteInput(input) {
4
+ const value = parseJSONObject(input);
5
+ let entity;
6
+ try {
7
+ entity = validateWriteInput(value);
8
+ }
9
+ catch (error) {
10
+ if (error instanceof TabulaInputError) {
11
+ throw inputError(error.code, error.message, error.details);
12
+ }
13
+ throw error;
14
+ }
15
+ return entity.type === "object"
16
+ ? { entity: "object", value: entity }
17
+ : { entity: "block", value: entity };
18
+ }
19
+ function parseJSONObject(input) {
20
+ if (input.trim().length === 0) {
21
+ throw inputError("MISSING_INPUT", "JSON input is required on stdin");
22
+ }
23
+ let value;
24
+ try {
25
+ value = JSON.parse(input);
26
+ }
27
+ catch (error) {
28
+ throw inputError("INVALID_JSON", "stdin is not valid JSON", {
29
+ message: error instanceof Error ? error.message : String(error),
30
+ });
31
+ }
32
+ return requireObject(value, "$");
33
+ }
34
+ function requireObject(value, path) {
35
+ if (value === null || typeof value !== "object" || Array.isArray(value)) {
36
+ throw inputError("INVALID_OBJECT", `Expected object at ${path}`, { path });
37
+ }
38
+ return value;
39
+ }
40
+ function inputError(code, message, details) {
41
+ return new CLIInputError(code, message, details);
42
+ }
@@ -0,0 +1,3 @@
1
+ import type { JSONRecord } from "../index.js";
2
+ export type Properties = JSONRecord;
3
+ export declare function parseProperties(values: string[]): Properties;
@@ -0,0 +1,22 @@
1
+ import { CLIInputError } from "./errors.js";
2
+ export function parseProperties(values) {
3
+ const properties = {};
4
+ for (const property of values) {
5
+ const separator = property.indexOf("=");
6
+ if (separator < 1) {
7
+ throw new CLIInputError("INVALID_PROPERTY", `Property must use key=value syntax: ${property}`);
8
+ }
9
+ const key = property.slice(0, separator);
10
+ if (Object.hasOwn(properties, key)) {
11
+ throw new CLIInputError("DUPLICATE_PROPERTY", `Duplicate property: ${key}`);
12
+ }
13
+ const value = property.slice(separator + 1);
14
+ try {
15
+ properties[key] = JSON.parse(value);
16
+ }
17
+ catch {
18
+ properties[key] = value;
19
+ }
20
+ }
21
+ return properties;
22
+ }
@@ -0,0 +1 @@
1
+ export * from "./API/index.js";
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export * from "./API/index.js";
@@ -0,0 +1,68 @@
1
+ import type { Database } from "bun:sqlite";
2
+ import type { BlockID, BlockMetadata } from "../../types/block";
3
+ import type { StoredBlock } from "../types";
4
+ /**
5
+ * Inserts a block row.
6
+ * @param db - The database containing the block
7
+ * @param block - The block metadata and content to insert
8
+ */
9
+ export declare function insertStoredBlock(db: Database, block: StoredBlock): void;
10
+ /**
11
+ * Inserts multiple block rows atomically.
12
+ * @param db - The database containing the blocks
13
+ * @param blocks - The blocks to insert
14
+ */
15
+ export declare function insertStoredBlocks(db: Database, blocks: StoredBlock[]): void;
16
+ /**
17
+ * Inserts or updates a block row without changing its containment children.
18
+ * @param db - The database containing the block
19
+ * @param block - The block metadata and content to persist
20
+ */
21
+ export declare function upsertStoredBlock(db: Database, block: StoredBlock): void;
22
+ /**
23
+ * Reads block metadata without content or children.
24
+ * @param db - The database containing the block
25
+ * @param blockID - The block ID to read
26
+ * @returns The block metadata
27
+ */
28
+ export declare function getBlockMetadata(db: Database, blockID: BlockID): BlockMetadata;
29
+ /**
30
+ * Reads the stored block row without recursive children.
31
+ * @param db - The database containing the block
32
+ * @param blockID - The block ID to read
33
+ * @returns The stored block metadata and content
34
+ */
35
+ export declare function getStoredBlock(db: Database, blockID: BlockID): StoredBlock;
36
+ /**
37
+ * Updates one existing block row without changing its containment children.
38
+ * @param db - The database containing the block
39
+ * @param block - The block metadata and content to update
40
+ */
41
+ export declare function updateStoredBlock(db: Database, block: StoredBlock): void;
42
+ /**
43
+ * Updates multiple existing block rows atomically.
44
+ * @param db - The database containing the blocks
45
+ * @param blocks - The block rows to update
46
+ */
47
+ export declare function updateStoredBlocks(db: Database, blocks: StoredBlock[]): void;
48
+ /**
49
+ * Deletes one block row without deleting its graph node or containment subtree.
50
+ * @param db - The database containing the block
51
+ * @param blockID - The block ID to delete
52
+ * @returns True if the block existed and was deleted
53
+ */
54
+ export declare function deleteStoredBlock(db: Database, blockID: BlockID): boolean;
55
+ /**
56
+ * Deletes block rows without deleting graph nodes or containment subtrees.
57
+ * @param db - The database containing the blocks
58
+ * @param blockIDs - The block IDs to delete
59
+ * @returns The number of requested block IDs that existed
60
+ */
61
+ export declare function deleteStoredBlocks(db: Database, blockIDs: BlockID[]): number;
62
+ /**
63
+ * Checks whether a block row exists.
64
+ * @param db - The database to check
65
+ * @param blockID - The block ID to check
66
+ * @returns True if the block exists
67
+ */
68
+ export declare function isStoredBlock(db: Database, blockID: BlockID): boolean;
@@ -0,0 +1,185 @@
1
+ import { BlockPrefix } from "../../utils/id";
2
+ /**
3
+ * Inserts a block row.
4
+ * @param db - The database containing the block
5
+ * @param block - The block metadata and content to insert
6
+ */
7
+ export function insertStoredBlock(db, block) {
8
+ validateBlock(block);
9
+ db.query(`
10
+ INSERT INTO blocks (id, content, properties)
11
+ VALUES ($id, $content, $properties)
12
+ `).run(blockParameters(block));
13
+ }
14
+ /**
15
+ * Inserts multiple block rows atomically.
16
+ * @param db - The database containing the blocks
17
+ * @param blocks - The blocks to insert
18
+ */
19
+ export function insertStoredBlocks(db, blocks) {
20
+ if (blocks.length === 0) {
21
+ return;
22
+ }
23
+ validateBlockBatch(blocks);
24
+ const insert = db.transaction(() => {
25
+ for (const block of blocks) {
26
+ insertStoredBlock(db, block);
27
+ }
28
+ });
29
+ insert();
30
+ }
31
+ /**
32
+ * Inserts or updates a block row without changing its containment children.
33
+ * @param db - The database containing the block
34
+ * @param block - The block metadata and content to persist
35
+ */
36
+ export function upsertStoredBlock(db, block) {
37
+ validateBlock(block);
38
+ if (isStoredBlock(db, block.id)) {
39
+ db.query(`
40
+ UPDATE blocks
41
+ SET content = $content,
42
+ properties = $properties
43
+ WHERE id = $id
44
+ `).run(blockParameters(block));
45
+ return;
46
+ }
47
+ insertStoredBlock(db, block);
48
+ }
49
+ /**
50
+ * Reads block metadata without content or children.
51
+ * @param db - The database containing the block
52
+ * @param blockID - The block ID to read
53
+ * @returns The block metadata
54
+ */
55
+ export function getBlockMetadata(db, blockID) {
56
+ const row = db.query(`
57
+ SELECT id, properties
58
+ FROM blocks
59
+ WHERE id = $id
60
+ `).get({ $id: blockID });
61
+ if (!row) {
62
+ throw new Error(`Block not found: ${blockID}`);
63
+ }
64
+ return {
65
+ id: row.id,
66
+ type: "block",
67
+ properties: JSON.parse(row.properties),
68
+ };
69
+ }
70
+ /**
71
+ * Reads the stored block row without recursive children.
72
+ * @param db - The database containing the block
73
+ * @param blockID - The block ID to read
74
+ * @returns The stored block metadata and content
75
+ */
76
+ export function getStoredBlock(db, blockID) {
77
+ const row = db.query(`
78
+ SELECT id, content, properties
79
+ FROM blocks
80
+ WHERE id = $id
81
+ `).get({ $id: blockID });
82
+ if (!row) {
83
+ throw new Error(`Block not found: ${blockID}`);
84
+ }
85
+ return {
86
+ id: row.id,
87
+ type: "block",
88
+ properties: JSON.parse(row.properties),
89
+ content: row.content,
90
+ };
91
+ }
92
+ /**
93
+ * Updates one existing block row without changing its containment children.
94
+ * @param db - The database containing the block
95
+ * @param block - The block metadata and content to update
96
+ */
97
+ export function updateStoredBlock(db, block) {
98
+ updateStoredBlocks(db, [block]);
99
+ }
100
+ /**
101
+ * Updates multiple existing block rows atomically.
102
+ * @param db - The database containing the blocks
103
+ * @param blocks - The block rows to update
104
+ */
105
+ export function updateStoredBlocks(db, blocks) {
106
+ if (blocks.length === 0) {
107
+ return;
108
+ }
109
+ validateBlockBatch(blocks);
110
+ const missingID = blocks.find((block) => !isStoredBlock(db, block.id))?.id;
111
+ if (missingID) {
112
+ throw new Error(`Block not found: ${missingID}`);
113
+ }
114
+ const update = db.transaction(() => {
115
+ const statement = db.query(`
116
+ UPDATE blocks
117
+ SET content = $content,
118
+ properties = $properties
119
+ WHERE id = $id
120
+ `);
121
+ for (const block of blocks) {
122
+ statement.run(blockParameters(block));
123
+ }
124
+ });
125
+ update();
126
+ }
127
+ /**
128
+ * Deletes one block row without deleting its graph node or containment subtree.
129
+ * @param db - The database containing the block
130
+ * @param blockID - The block ID to delete
131
+ * @returns True if the block existed and was deleted
132
+ */
133
+ export function deleteStoredBlock(db, blockID) {
134
+ return deleteStoredBlocks(db, [blockID]) > 0;
135
+ }
136
+ /**
137
+ * Deletes block rows without deleting graph nodes or containment subtrees.
138
+ * @param db - The database containing the blocks
139
+ * @param blockIDs - The block IDs to delete
140
+ * @returns The number of requested block IDs that existed
141
+ */
142
+ export function deleteStoredBlocks(db, blockIDs) {
143
+ if (blockIDs.length === 0) {
144
+ return 0;
145
+ }
146
+ const result = db.query(`
147
+ DELETE FROM blocks
148
+ WHERE id IN (SELECT value FROM json_each($ids))
149
+ `).run({ $ids: JSON.stringify([...new Set(blockIDs)]) });
150
+ return result.changes;
151
+ }
152
+ /**
153
+ * Checks whether a block row exists.
154
+ * @param db - The database to check
155
+ * @param blockID - The block ID to check
156
+ * @returns True if the block exists
157
+ */
158
+ export function isStoredBlock(db, blockID) {
159
+ return db.query("SELECT 1 FROM blocks WHERE id = $id").get({ $id: blockID }) !== null;
160
+ }
161
+ /** Validates a block ID. */
162
+ function validateBlock(block) {
163
+ if (!block.id.startsWith(`${BlockPrefix}_`)) {
164
+ throw new Error(`Invalid block id: ${block.id}`);
165
+ }
166
+ }
167
+ /** Validates block IDs and duplicate IDs within a batch. */
168
+ function validateBlockBatch(blocks) {
169
+ const ids = new Set();
170
+ for (const block of blocks) {
171
+ validateBlock(block);
172
+ if (ids.has(block.id)) {
173
+ throw new Error(`Duplicate block id: ${block.id}`);
174
+ }
175
+ ids.add(block.id);
176
+ }
177
+ }
178
+ /** Maps a stored block to SQLite named parameters. */
179
+ function blockParameters(block) {
180
+ return {
181
+ $id: block.id,
182
+ $content: block.content,
183
+ $properties: JSON.stringify(block.properties ?? {}),
184
+ };
185
+ }
@@ -0,0 +1,61 @@
1
+ import type { Database } from "bun:sqlite";
2
+ import type { EntityReference } from "../../types/graph";
3
+ import type { ObjID } from "../../types/object";
4
+ import type { StoredEntityReference } from "../types";
5
+ /**
6
+ * Reads the current direct parent for an object or block node.
7
+ * @param db - The database containing the child node
8
+ * @param childID - The object or block ID whose parent should be read
9
+ * @returns The parent reference, or undefined when the child is unattached
10
+ */
11
+ export declare function getEntityParent(db: Database, childID: string): StoredEntityReference | undefined;
12
+ /**
13
+ * Adds an object as a root child of the database.
14
+ * @param db - The database containing the object
15
+ * @param databaseID - The database parent ID
16
+ * @param objectID - The object child ID
17
+ */
18
+ export declare function appendDatabaseRootObject(db: Database, databaseID: string, objectID: ObjID): void;
19
+ /**
20
+ * Appends an existing object or block under a database, object, or block parent.
21
+ * If the child already has a parent, it is moved to the new parent.
22
+ * @param db - The database containing the parent and child
23
+ * @param parentID - The database, object, or block parent ID
24
+ * @param child - The object or block child to attach
25
+ */
26
+ export declare function appendEntityChild(db: Database, parentID: string, child: EntityReference): void;
27
+ /**
28
+ * Reads root object IDs ordered under the database.
29
+ * @param db - The database to read
30
+ * @param databaseID - The database ID whose root objects should be listed
31
+ * @returns Ordered root object IDs
32
+ */
33
+ export declare function getDatabaseRootObjects(db: Database, databaseID: string): ObjID[];
34
+ /**
35
+ * Reads direct child entity references for a database, object, or block parent.
36
+ * @param db - The database containing the parent
37
+ * @param parentID - The database or entity parent ID
38
+ * @returns Ordered child entity references
39
+ */
40
+ export declare function getDirectEntityChildren(db: Database, parentID: string): EntityReference[];
41
+ /**
42
+ * Replaces direct children for one or more parents.
43
+ * @param db - The database containing the parents and children
44
+ * @param desiredChildrenByParent - Complete desired child lists keyed by parent ID
45
+ */
46
+ export declare function replaceEntityChildren(db: Database, desiredChildrenByParent: Map<string, EntityReference[]>): void;
47
+ /**
48
+ * Reads direct child IDs without joining subtype rows.
49
+ * @param db - The database containing the parent
50
+ * @param parentID - The parent ID to read
51
+ * @returns Ordered child IDs
52
+ */
53
+ export declare function getDirectEntityChildIDs(db: Database, parentID: string): string[];
54
+ /**
55
+ * Removes the current parent edge for a child so it can be reparented or detached.
56
+ * @param db - The database containing the edge
57
+ * @param childID - The child ID to detach
58
+ */
59
+ export declare function detachEntityParent(db: Database, childID: string): void;
60
+ /** Checks whether any parent edge references an entity. */
61
+ export declare function hasEntityParent(db: Database, id: string): boolean;