@todu/engine 0.1.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/dist/change-observer.d.ts +15 -0
- package/dist/change-observer.d.ts.map +1 -0
- package/dist/change-observer.js +64 -0
- package/dist/change-observer.js.map +1 -0
- package/dist/habits.d.ts +6 -0
- package/dist/habits.d.ts.map +1 -0
- package/dist/habits.js +397 -0
- package/dist/habits.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +104 -0
- package/dist/index.js.map +1 -0
- package/dist/labels.d.ts +5 -0
- package/dist/labels.d.ts.map +1 -0
- package/dist/labels.js +113 -0
- package/dist/labels.js.map +1 -0
- package/dist/notes.d.ts +5 -0
- package/dist/notes.d.ts.map +1 -0
- package/dist/notes.js +163 -0
- package/dist/notes.js.map +1 -0
- package/dist/projects.d.ts +5 -0
- package/dist/projects.d.ts.map +1 -0
- package/dist/projects.js +117 -0
- package/dist/projects.js.map +1 -0
- package/dist/recurring.d.ts +23 -0
- package/dist/recurring.d.ts.map +1 -0
- package/dist/recurring.js +372 -0
- package/dist/recurring.js.map +1 -0
- package/dist/schedule.d.ts +38 -0
- package/dist/schedule.d.ts.map +1 -0
- package/dist/schedule.js +330 -0
- package/dist/schedule.js.map +1 -0
- package/dist/scheduling.d.ts +62 -0
- package/dist/scheduling.d.ts.map +1 -0
- package/dist/scheduling.js +58 -0
- package/dist/scheduling.js.map +1 -0
- package/dist/storage.d.ts +34 -0
- package/dist/storage.d.ts.map +1 -0
- package/dist/storage.js +204 -0
- package/dist/storage.js.map +1 -0
- package/dist/sync-client.d.ts +16 -0
- package/dist/sync-client.d.ts.map +1 -0
- package/dist/sync-client.js +54 -0
- package/dist/sync-client.js.map +1 -0
- package/dist/sync-server.d.ts +12 -0
- package/dist/sync-server.d.ts.map +1 -0
- package/dist/sync-server.js +21 -0
- package/dist/sync-server.js.map +1 -0
- package/dist/tasks.d.ts +18 -0
- package/dist/tasks.d.ts.map +1 -0
- package/dist/tasks.js +396 -0
- package/dist/tasks.js.map +1 -0
- package/dist/todu.d.ts +115 -0
- package/dist/todu.d.ts.map +1 -0
- package/dist/todu.js +76 -0
- package/dist/todu.js.map +1 -0
- package/package.json +52 -0
package/dist/storage.js
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { Repo } from "@automerge/automerge-repo";
|
|
4
|
+
import { NodeFSStorageAdapter } from "@automerge/automerge-repo-storage-nodefs";
|
|
5
|
+
import { CATALOG_DOC_KEY, createEmptyCatalog, SCHEMA_VERSION, } from "@todu/core";
|
|
6
|
+
// ============================================================================
|
|
7
|
+
// Storage layer — Automerge repo + document management
|
|
8
|
+
// ============================================================================
|
|
9
|
+
/** Sync message throttle interval in Automerge (see helpers/throttle.js) */
|
|
10
|
+
const SYNC_THROTTLE_MS = 100;
|
|
11
|
+
/** Extra time after sync message generation for WebSocket delivery */
|
|
12
|
+
const SYNC_DELIVERY_MS = 20;
|
|
13
|
+
/**
|
|
14
|
+
* Wait for pending sync messages to be generated and delivered.
|
|
15
|
+
*
|
|
16
|
+
* Automerge batches sync messages on a ~100ms throttle. After a mutation,
|
|
17
|
+
* we wait for the "generate-sync-message" event (confirming the change was
|
|
18
|
+
* packaged for sending), then yield briefly for the WebSocket to deliver
|
|
19
|
+
* the bytes. If no sync message arrives within the throttle window,
|
|
20
|
+
* there's nothing pending — return immediately.
|
|
21
|
+
*/
|
|
22
|
+
async function waitForSyncFlush(repo, documentId) {
|
|
23
|
+
return new Promise((resolve) => {
|
|
24
|
+
const timeout = setTimeout(() => {
|
|
25
|
+
repo.off("doc-metrics", onMetrics);
|
|
26
|
+
resolve();
|
|
27
|
+
}, SYNC_THROTTLE_MS + SYNC_DELIVERY_MS);
|
|
28
|
+
function onMetrics(m) {
|
|
29
|
+
if (m.type === "generate-sync-message" && m.documentId === documentId) {
|
|
30
|
+
repo.off("doc-metrics", onMetrics);
|
|
31
|
+
clearTimeout(timeout);
|
|
32
|
+
// Yield for WebSocket to deliver the bytes
|
|
33
|
+
setTimeout(resolve, SYNC_DELIVERY_MS);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
repo.on("doc-metrics", onMetrics);
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Initialize storage: create data directory, set up Automerge repo,
|
|
41
|
+
* and load or create the catalog document.
|
|
42
|
+
*/
|
|
43
|
+
export async function initStorage(storagePath) {
|
|
44
|
+
// Ensure data directory exists
|
|
45
|
+
fs.mkdirSync(storagePath, { recursive: true });
|
|
46
|
+
// Create Automerge repo with filesystem storage
|
|
47
|
+
const repo = new Repo({
|
|
48
|
+
storage: new NodeFSStorageAdapter(storagePath),
|
|
49
|
+
});
|
|
50
|
+
// Load or create catalog document
|
|
51
|
+
const catalog = await loadOrCreateCatalog(repo, storagePath);
|
|
52
|
+
return {
|
|
53
|
+
repo,
|
|
54
|
+
catalog,
|
|
55
|
+
ephemeral: false,
|
|
56
|
+
async close() {
|
|
57
|
+
await repo.flush();
|
|
58
|
+
await repo.shutdown();
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Initialize ephemeral storage with no filesystem persistence.
|
|
64
|
+
* Used by CLI when syncing with a running Electron instance.
|
|
65
|
+
*
|
|
66
|
+
* Creates an in-memory Automerge repo and reads the catalog document ID
|
|
67
|
+
* from the marker file (written by the persistent owner).
|
|
68
|
+
*
|
|
69
|
+
* IMPORTANT: The caller must connect a sync adapter to the repo BEFORE
|
|
70
|
+
* calling `findCatalog()`. The ephemeral repo has no local data — it
|
|
71
|
+
* needs a sync peer to provide the document contents.
|
|
72
|
+
*/
|
|
73
|
+
export async function initEphemeralStorage(storagePath) {
|
|
74
|
+
// Read the catalog document ID from the marker file
|
|
75
|
+
const markerPath = path.join(storagePath, `${CATALOG_DOC_KEY}.id`);
|
|
76
|
+
if (!fs.existsSync(markerPath)) {
|
|
77
|
+
throw new Error("No catalog marker found. Run the Electron app or CLI standalone first to create data.");
|
|
78
|
+
}
|
|
79
|
+
const docId = fs.readFileSync(markerPath, "utf-8").trim();
|
|
80
|
+
// Create repo with no storage — purely in-memory
|
|
81
|
+
const repo = new Repo({});
|
|
82
|
+
return {
|
|
83
|
+
repo,
|
|
84
|
+
ephemeral: true,
|
|
85
|
+
async close() {
|
|
86
|
+
try {
|
|
87
|
+
await repo.shutdown();
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
// Safe to ignore — adapters may already be disconnected
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
async findCatalog() {
|
|
94
|
+
// Now that sync is connected, find the catalog document.
|
|
95
|
+
// Allow "requesting" state so find() doesn't fail while waiting
|
|
96
|
+
// for the sync peer to deliver the document data.
|
|
97
|
+
const catalog = await repo.find(docId, {
|
|
98
|
+
allowableStates: ["ready", "requesting", "loading"],
|
|
99
|
+
});
|
|
100
|
+
// Wait for the document to actually be ready (populated via sync)
|
|
101
|
+
await catalog.whenReady();
|
|
102
|
+
return {
|
|
103
|
+
repo,
|
|
104
|
+
catalog,
|
|
105
|
+
ephemeral: true,
|
|
106
|
+
async close() {
|
|
107
|
+
// Wait for any pending mutations to be synced to the server.
|
|
108
|
+
// Automerge throttles sync messages (~100ms batches), so after
|
|
109
|
+
// a change() call the sync message won't be sent immediately.
|
|
110
|
+
// We listen for the "generate-sync-message" event to know the
|
|
111
|
+
// message was queued, then yield briefly for the WebSocket to
|
|
112
|
+
// deliver the bytes before shutting down.
|
|
113
|
+
await waitForSyncFlush(repo, docId);
|
|
114
|
+
try {
|
|
115
|
+
await repo.shutdown();
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// Safe to ignore — adapters may already be disconnected
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
},
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Load existing catalog document or create a new one.
|
|
127
|
+
* Uses a marker file to store the document ID between sessions.
|
|
128
|
+
*/
|
|
129
|
+
async function loadOrCreateCatalog(repo, storagePath) {
|
|
130
|
+
const markerPath = path.join(storagePath, `${CATALOG_DOC_KEY}.id`);
|
|
131
|
+
// Try to load existing catalog
|
|
132
|
+
if (fs.existsSync(markerPath)) {
|
|
133
|
+
const docId = fs.readFileSync(markerPath, "utf-8").trim();
|
|
134
|
+
const handle = await repo.find(docId);
|
|
135
|
+
migrateCatalog(handle);
|
|
136
|
+
return handle;
|
|
137
|
+
}
|
|
138
|
+
// Create new catalog
|
|
139
|
+
const handle = repo.create();
|
|
140
|
+
handle.change((doc) => {
|
|
141
|
+
const empty = createEmptyCatalog();
|
|
142
|
+
doc.version = empty.version;
|
|
143
|
+
doc.projects = empty.projects;
|
|
144
|
+
doc.labels = empty.labels;
|
|
145
|
+
doc.recurringTemplates = empty.recurringTemplates;
|
|
146
|
+
doc.habits = empty.habits;
|
|
147
|
+
doc.habitLogDocIds = empty.habitLogDocIds;
|
|
148
|
+
doc.taskListDocIds = empty.taskListDocIds;
|
|
149
|
+
doc.settings = empty.settings;
|
|
150
|
+
});
|
|
151
|
+
// Save document ID for next session
|
|
152
|
+
fs.writeFileSync(markerPath, handle.documentId, "utf-8");
|
|
153
|
+
return handle;
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Migrate an existing catalog document to the current schema.
|
|
157
|
+
* Backfills any missing fields that were added in later versions.
|
|
158
|
+
* This ensures engine code can always assume catalog fields exist.
|
|
159
|
+
*/
|
|
160
|
+
function migrateCatalog(handle) {
|
|
161
|
+
const doc = handle.doc();
|
|
162
|
+
if (!doc)
|
|
163
|
+
return;
|
|
164
|
+
const defaults = createEmptyCatalog();
|
|
165
|
+
let needsMigration = false;
|
|
166
|
+
// Check for missing fields
|
|
167
|
+
if (!Array.isArray(doc.projects))
|
|
168
|
+
needsMigration = true;
|
|
169
|
+
if (!Array.isArray(doc.labels))
|
|
170
|
+
needsMigration = true;
|
|
171
|
+
if (!Array.isArray(doc.recurringTemplates))
|
|
172
|
+
needsMigration = true;
|
|
173
|
+
if (!Array.isArray(doc.habits))
|
|
174
|
+
needsMigration = true;
|
|
175
|
+
if (doc.taskListDocIds === undefined || doc.taskListDocIds === null)
|
|
176
|
+
needsMigration = true;
|
|
177
|
+
if (doc.habitLogDocIds === undefined || doc.habitLogDocIds === null)
|
|
178
|
+
needsMigration = true;
|
|
179
|
+
if (doc.settings === undefined || doc.settings === null)
|
|
180
|
+
needsMigration = true;
|
|
181
|
+
if (doc.version === undefined || doc.version === null)
|
|
182
|
+
needsMigration = true;
|
|
183
|
+
if (!needsMigration)
|
|
184
|
+
return;
|
|
185
|
+
handle.change((d) => {
|
|
186
|
+
if (!Array.isArray(d.projects))
|
|
187
|
+
d.projects = defaults.projects;
|
|
188
|
+
if (!Array.isArray(d.labels))
|
|
189
|
+
d.labels = defaults.labels;
|
|
190
|
+
if (!Array.isArray(d.recurringTemplates))
|
|
191
|
+
d.recurringTemplates = defaults.recurringTemplates;
|
|
192
|
+
if (!Array.isArray(d.habits))
|
|
193
|
+
d.habits = defaults.habits;
|
|
194
|
+
if (d.taskListDocIds === undefined || d.taskListDocIds === null)
|
|
195
|
+
d.taskListDocIds = defaults.taskListDocIds;
|
|
196
|
+
if (d.habitLogDocIds === undefined || d.habitLogDocIds === null)
|
|
197
|
+
d.habitLogDocIds = defaults.habitLogDocIds;
|
|
198
|
+
if (d.settings === undefined || d.settings === null)
|
|
199
|
+
d.settings = defaults.settings;
|
|
200
|
+
if (d.version === undefined || d.version === null)
|
|
201
|
+
d.version = SCHEMA_VERSION;
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../src/storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { Repo } from "@automerge/automerge-repo";
|
|
2
|
+
export declare const DEFAULT_SYNC_URL = "ws://127.0.0.1:24377";
|
|
3
|
+
/**
|
|
4
|
+
* Add a sync client adapter to a Repo, connecting to a remote sync server.
|
|
5
|
+
* Waits for the adapter to be ready before returning.
|
|
6
|
+
* Returns a disconnect function.
|
|
7
|
+
*
|
|
8
|
+
* @param retryInterval - Retry interval in ms if connection fails (default: 500ms for local)
|
|
9
|
+
*/
|
|
10
|
+
export declare function connectSyncClient(repo: Repo, url?: string, retryInterval?: number): Promise<() => void>;
|
|
11
|
+
/**
|
|
12
|
+
* Check if a sync server is reachable at the given URL.
|
|
13
|
+
* Returns true if the connection succeeds within the timeout.
|
|
14
|
+
*/
|
|
15
|
+
export declare function isSyncServerAvailable(url?: string, timeoutMs?: number): Promise<boolean>;
|
|
16
|
+
//# sourceMappingURL=sync-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-client.d.ts","sourceRoot":"","sources":["../src/sync-client.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,2BAA2B,CAAC;AAGtD,eAAO,MAAM,gBAAgB,yBAAyB,CAAC;AAEvD;;;;;;GAMG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,IAAI,EACV,GAAG,GAAE,MAAyB,EAC9B,aAAa,SAAM,GAClB,OAAO,CAAC,MAAM,IAAI,CAAC,CAcrB;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,GAAG,GAAE,MAAyB,EAC9B,SAAS,SAAM,GACd,OAAO,CAAC,OAAO,CAAC,CA2BlB"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { WebSocketClientAdapter } from "@automerge/automerge-repo-network-websocket";
|
|
2
|
+
export const DEFAULT_SYNC_URL = "ws://127.0.0.1:24377";
|
|
3
|
+
/**
|
|
4
|
+
* Add a sync client adapter to a Repo, connecting to a remote sync server.
|
|
5
|
+
* Waits for the adapter to be ready before returning.
|
|
6
|
+
* Returns a disconnect function.
|
|
7
|
+
*
|
|
8
|
+
* @param retryInterval - Retry interval in ms if connection fails (default: 500ms for local)
|
|
9
|
+
*/
|
|
10
|
+
export async function connectSyncClient(repo, url = DEFAULT_SYNC_URL, retryInterval = 500) {
|
|
11
|
+
const adapter = new WebSocketClientAdapter(url, retryInterval);
|
|
12
|
+
repo.networkSubsystem.addNetworkAdapter(adapter);
|
|
13
|
+
// Wait for the WebSocket connection to establish
|
|
14
|
+
await adapter.whenReady();
|
|
15
|
+
return () => {
|
|
16
|
+
try {
|
|
17
|
+
adapter.disconnect();
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
// WebSocket may already be closed — safe to ignore
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Check if a sync server is reachable at the given URL.
|
|
26
|
+
* Returns true if the connection succeeds within the timeout.
|
|
27
|
+
*/
|
|
28
|
+
export async function isSyncServerAvailable(url = DEFAULT_SYNC_URL, timeoutMs = 200) {
|
|
29
|
+
return new Promise((resolve) => {
|
|
30
|
+
const timer = setTimeout(() => {
|
|
31
|
+
resolve(false);
|
|
32
|
+
}, timeoutMs);
|
|
33
|
+
try {
|
|
34
|
+
// Use dynamic import for ws since it's CJS
|
|
35
|
+
import("ws").then(({ default: WebSocket }) => {
|
|
36
|
+
const ws = new WebSocket(url);
|
|
37
|
+
ws.onopen = () => {
|
|
38
|
+
clearTimeout(timer);
|
|
39
|
+
ws.close();
|
|
40
|
+
resolve(true);
|
|
41
|
+
};
|
|
42
|
+
ws.onerror = () => {
|
|
43
|
+
clearTimeout(timer);
|
|
44
|
+
resolve(false);
|
|
45
|
+
};
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
clearTimeout(timer);
|
|
50
|
+
resolve(false);
|
|
51
|
+
}
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=sync-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-client.js","sourceRoot":"","sources":["../src/sync-client.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,6CAA6C,CAAC;AAErF,MAAM,CAAC,MAAM,gBAAgB,GAAG,sBAAsB,CAAC;AAEvD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,IAAU,EACV,GAAG,GAAW,gBAAgB,EAC9B,aAAa,GAAG,GAAG,EACE;IACrB,MAAM,OAAO,GAAG,IAAI,sBAAsB,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IAC/D,IAAI,CAAC,gBAAgB,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAEjD,iDAAiD;IACjD,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC;IAE1B,OAAO,GAAG,EAAE,CAAC;QACX,IAAI,CAAC;YACH,OAAO,CAAC,UAAU,EAAE,CAAC;QACvB,CAAC;QAAC,MAAM,CAAC;YACP,qDAAmD;QACrD,CAAC;IAAA,CACF,CAAC;AAAA,CACH;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,GAAG,GAAW,gBAAgB,EAC9B,SAAS,GAAG,GAAG,EACG;IAClB,OAAO,IAAI,OAAO,CAAU,CAAC,OAAO,EAAE,EAAE,CAAC;QACvC,MAAM,KAAK,GAAG,UAAU,CAAC,GAAG,EAAE,CAAC;YAC7B,OAAO,CAAC,KAAK,CAAC,CAAC;QAAA,CAChB,EAAE,SAAS,CAAC,CAAC;QAEd,IAAI,CAAC;YACH,2CAA2C;YAC3C,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,EAAE,CAAC;gBAC5C,MAAM,EAAE,GAAG,IAAI,SAAS,CAAC,GAAG,CAAC,CAAC;gBAE9B,EAAE,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;oBAChB,YAAY,CAAC,KAAK,CAAC,CAAC;oBACpB,EAAE,CAAC,KAAK,EAAE,CAAC;oBACX,OAAO,CAAC,IAAI,CAAC,CAAC;gBAAA,CACf,CAAC;gBAEF,EAAE,CAAC,OAAO,GAAG,GAAG,EAAE,CAAC;oBACjB,YAAY,CAAC,KAAK,CAAC,CAAC;oBACpB,OAAO,CAAC,KAAK,CAAC,CAAC;gBAAA,CAChB,CAAC;YAAA,CACH,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,YAAY,CAAC,KAAK,CAAC,CAAC;YACpB,OAAO,CAAC,KAAK,CAAC,CAAC;QACjB,CAAC;IAAA,CACF,CAAC,CAAC;AAAA,CACJ"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { Repo } from "@automerge/automerge-repo";
|
|
2
|
+
export declare const DEFAULT_SYNC_PORT = 24377;
|
|
3
|
+
export interface SyncServer {
|
|
4
|
+
port: number;
|
|
5
|
+
close(): Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Start a WebSocket sync server and attach it to an Automerge Repo.
|
|
9
|
+
* Other Repo instances (CLI, other devices) can connect and sync documents.
|
|
10
|
+
*/
|
|
11
|
+
export declare function startSyncServer(repo: Repo, port?: number): SyncServer;
|
|
12
|
+
//# sourceMappingURL=sync-server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-server.d.ts","sourceRoot":"","sources":["../src/sync-server.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,2BAA2B,CAAC;AAKtD,eAAO,MAAM,iBAAiB,QAAQ,CAAC;AAEvC,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,GAAE,MAA0B,GAAG,UAAU,CAaxF"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { WebSocketServerAdapter } from "@automerge/automerge-repo-network-websocket";
|
|
2
|
+
import { WebSocketServer } from "ws";
|
|
3
|
+
export const DEFAULT_SYNC_PORT = 24377;
|
|
4
|
+
/**
|
|
5
|
+
* Start a WebSocket sync server and attach it to an Automerge Repo.
|
|
6
|
+
* Other Repo instances (CLI, other devices) can connect and sync documents.
|
|
7
|
+
*/
|
|
8
|
+
export function startSyncServer(repo, port = DEFAULT_SYNC_PORT) {
|
|
9
|
+
const wss = new WebSocketServer({ host: "127.0.0.1", port });
|
|
10
|
+
const adapter = new WebSocketServerAdapter(wss);
|
|
11
|
+
repo.networkSubsystem.addNetworkAdapter(adapter);
|
|
12
|
+
return {
|
|
13
|
+
port,
|
|
14
|
+
close() {
|
|
15
|
+
return new Promise((resolve) => {
|
|
16
|
+
wss.close(() => resolve());
|
|
17
|
+
});
|
|
18
|
+
},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=sync-server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sync-server.js","sourceRoot":"","sources":["../src/sync-server.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,sBAAsB,EAAE,MAAM,6CAA6C,CAAC;AAErF,OAAO,EAAE,eAAe,EAAE,MAAM,IAAI,CAAC;AAErC,MAAM,CAAC,MAAM,iBAAiB,GAAG,KAAK,CAAC;AAOvC;;;GAGG;AACH,MAAM,UAAU,eAAe,CAAC,IAAU,EAAE,IAAI,GAAW,iBAAiB,EAAc;IACxF,MAAM,GAAG,GAAG,IAAI,eAAe,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7D,MAAM,OAAO,GAAG,IAAI,sBAAsB,CAAC,GAAoC,CAAC,CAAC;IACjF,IAAI,CAAC,gBAAgB,CAAC,iBAAiB,CAAC,OAAO,CAAC,CAAC;IAEjD,OAAO;QACL,IAAI;QACJ,KAAK,GAAG;YACN,OAAO,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,EAAE,CAAC;gBACpC,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;YAAA,CAC5B,CAAC,CAAC;QAAA,CACJ;KACF,CAAC;AAAA,CACH"}
|
package/dist/tasks.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { DocHandle, Repo } from "@automerge/automerge-repo";
|
|
2
|
+
import { type CatalogDocument, type CreateTaskInput, type Result, type TaskId, type TaskWithDetail } from "@todu/core";
|
|
3
|
+
import type { TaskNamespace } from "./todu.js";
|
|
4
|
+
/**
|
|
5
|
+
* Extended task namespace with internal methods for use by recurring templates.
|
|
6
|
+
* The public TaskNamespace is a subset of this.
|
|
7
|
+
*/
|
|
8
|
+
export interface InternalTaskNamespace extends TaskNamespace {
|
|
9
|
+
/**
|
|
10
|
+
* Create a task with a specific ID and optional templateId.
|
|
11
|
+
* Used by recurring template task generation.
|
|
12
|
+
*/
|
|
13
|
+
create(input: CreateTaskInput, taskId?: TaskId, templateId?: string): Promise<Result<TaskWithDetail>>;
|
|
14
|
+
/** Expose repo for recurring template task lookup */
|
|
15
|
+
_repo: Repo;
|
|
16
|
+
}
|
|
17
|
+
export declare function createTaskNamespace(catalog: DocHandle<CatalogDocument>, repo: Repo): InternalTaskNamespace;
|
|
18
|
+
//# sourceMappingURL=tasks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../src/tasks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAc,IAAI,EAAE,MAAM,2BAA2B,CAAC;AAC7E,OAAO,EACL,KAAK,eAAe,EACpB,KAAK,eAAe,EAQpB,KAAK,MAAM,EAIX,KAAK,MAAM,EAIX,KAAK,cAAc,EAKpB,MAAM,YAAY,CAAC;AACpB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAM/C;;;GAGG;AACH,MAAM,WAAW,qBAAsB,SAAQ,aAAa;IAC1D;;;OAGG;IACH,MAAM,CACJ,KAAK,EAAE,eAAe,EACtB,MAAM,CAAC,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC,CAAC;IAEnC,qDAAqD;IACrD,KAAK,EAAE,IAAI,CAAC;CACb;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,SAAS,CAAC,eAAe,CAAC,EACnC,IAAI,EAAE,IAAI,GACT,qBAAqB,CAsYvB"}
|