@mnaoumov/obsidian-lib 6.3.0 → 7.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/components/incremental-file-index-component/package.json +6 -0
- package/data-stores/data-store-replica/package.json +6 -0
- package/data-stores/data-store-seeds/package.json +6 -0
- package/data-stores/data-store-transport/package.json +6 -0
- package/data-stores/index/package.json +6 -0
- package/data-stores/local-data-store/package.json +6 -0
- package/data-stores/package.json +6 -0
- package/data-stores/shared-data-store/package.json +6 -0
- package/dist/lib/cjs/__merged.cjs +36 -1
- package/dist/lib/cjs/__merged.d.cts +7 -0
- package/dist/lib/cjs/components/incremental-file-index-component.cjs +340 -0
- package/dist/lib/cjs/components/incremental-file-index-component.d.cts +152 -0
- package/dist/lib/cjs/components/index.cjs +4 -1
- package/dist/lib/cjs/components/index.d.cts +1 -0
- package/dist/lib/cjs/data-stores/data-store-replica.cjs +263 -0
- package/dist/lib/cjs/data-stores/data-store-replica.d.cts +112 -0
- package/dist/lib/cjs/data-stores/data-store-seeds.cjs +204 -0
- package/dist/lib/cjs/data-stores/data-store-seeds.d.cts +133 -0
- package/dist/lib/cjs/data-stores/data-store-transport.cjs +347 -0
- package/dist/lib/cjs/data-stores/data-store-transport.d.cts +125 -0
- package/dist/lib/cjs/data-stores/index.cjs +120 -0
- package/dist/lib/cjs/data-stores/index.d.cts +6 -0
- package/dist/lib/cjs/data-stores/local-data-store.cjs +258 -0
- package/dist/lib/cjs/data-stores/local-data-store.d.cts +119 -0
- package/dist/lib/cjs/data-stores/shared-data-store.cjs +431 -0
- package/dist/lib/cjs/data-stores/shared-data-store.d.cts +137 -0
- package/dist/lib/cjs/generated-during-build.cjs +1 -1
- package/dist/lib/cjs/index.cjs +7 -1
- package/dist/lib/cjs/index.d.cts +2 -0
- package/dist/lib/cjs/obsidian-word-characters.cjs +231 -0
- package/dist/lib/cjs/obsidian-word-characters.d.cts +59 -0
- package/dist/lib/esm/__merged.d.mts +7 -0
- package/dist/lib/esm/__merged.mjs +35 -1
- package/dist/lib/esm/components/incremental-file-index-component.d.mts +152 -0
- package/dist/lib/esm/components/incremental-file-index-component.mjs +260 -0
- package/dist/lib/esm/components/index.d.mts +1 -0
- package/dist/lib/esm/components/index.mjs +3 -1
- package/dist/lib/esm/data-stores/data-store-replica.d.mts +112 -0
- package/dist/lib/esm/data-stores/data-store-replica.mjs +173 -0
- package/dist/lib/esm/data-stores/data-store-seeds.d.mts +133 -0
- package/dist/lib/esm/data-stores/data-store-seeds.mjs +115 -0
- package/dist/lib/esm/data-stores/data-store-transport.d.mts +125 -0
- package/dist/lib/esm/data-stores/data-store-transport.mjs +259 -0
- package/dist/lib/esm/data-stores/index.d.mts +6 -0
- package/dist/lib/esm/data-stores/index.mjs +18 -0
- package/dist/lib/esm/data-stores/local-data-store.d.mts +119 -0
- package/dist/lib/esm/data-stores/local-data-store.mjs +170 -0
- package/dist/lib/esm/data-stores/shared-data-store.d.mts +137 -0
- package/dist/lib/esm/data-stores/shared-data-store.mjs +346 -0
- package/dist/lib/esm/generated-during-build.mjs +1 -1
- package/dist/lib/esm/index.d.mts +2 -0
- package/dist/lib/esm/index.mjs +5 -1
- package/dist/lib/esm/obsidian-word-characters.d.mts +59 -0
- package/dist/lib/esm/obsidian-word-characters.mjs +139 -0
- package/package.json +38 -17
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/// <reference path="../library-references.d.cts" />
|
|
2
|
+
/**
|
|
3
|
+
* @file
|
|
4
|
+
*
|
|
5
|
+
* Optional chunked seeds for expensive derived caches, with per-file validation before reuse.
|
|
6
|
+
*/
|
|
7
|
+
import type { JsonValue } from 'type-fest' with { 'resolution-mode': 'import' };
|
|
8
|
+
import type { LocalDataStore } from './local-data-store.cjs';
|
|
9
|
+
import { DataStoreTransport } from './data-store-transport.cjs';
|
|
10
|
+
/**
|
|
11
|
+
* A derived record tagged with both cheap statistics and a content hash.
|
|
12
|
+
*/
|
|
13
|
+
export interface DataStoreSeedEntry extends DataStoreSeedFile {
|
|
14
|
+
/**
|
|
15
|
+
* Content hash, using the same algorithm on every device.
|
|
16
|
+
*/
|
|
17
|
+
readonly hash: string;
|
|
18
|
+
/**
|
|
19
|
+
* Consumer-defined JSON cache record.
|
|
20
|
+
*/
|
|
21
|
+
readonly value: JsonValue;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Cheap file identity already available from the vault's file tree.
|
|
25
|
+
*/
|
|
26
|
+
export interface DataStoreSeedFile {
|
|
27
|
+
/**
|
|
28
|
+
* Last modification time.
|
|
29
|
+
*/
|
|
30
|
+
readonly mtime: number;
|
|
31
|
+
/**
|
|
32
|
+
* Vault-relative path.
|
|
33
|
+
*/
|
|
34
|
+
readonly path: string;
|
|
35
|
+
/**
|
|
36
|
+
* File size in bytes.
|
|
37
|
+
*/
|
|
38
|
+
readonly size: number;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Parameters for optional cache-seed sharing.
|
|
42
|
+
*/
|
|
43
|
+
export interface DataStoreSeedsConstructorParams {
|
|
44
|
+
/**
|
|
45
|
+
* Dedicated device-local store that owns the seed publisher identity.
|
|
46
|
+
*/
|
|
47
|
+
readonly local: LocalDataStore;
|
|
48
|
+
/**
|
|
49
|
+
* Per-store user setting. Disabled means neither publishing nor adopting seeds.
|
|
50
|
+
*/
|
|
51
|
+
readonly share: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Store-scoped, bounded Markdown transport. Include the consumer schema version in its store id.
|
|
54
|
+
*/
|
|
55
|
+
readonly transport: DataStoreTransport;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Results of validating a shared cache seed against the local vault.
|
|
59
|
+
*/
|
|
60
|
+
export interface DataStoreSeedValidation {
|
|
61
|
+
/**
|
|
62
|
+
* Files absent from the seed, or with different content.
|
|
63
|
+
*/
|
|
64
|
+
readonly rebuild: string[];
|
|
65
|
+
/**
|
|
66
|
+
* Validated seed records, with local file statistics.
|
|
67
|
+
*/
|
|
68
|
+
readonly reusable: DataStoreSeedEntry[];
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Parameters for per-file seed validation.
|
|
72
|
+
*/
|
|
73
|
+
export interface ValidateDataStoreSeedParams {
|
|
74
|
+
/**
|
|
75
|
+
* Seed entries from a recognized consumer schema.
|
|
76
|
+
*/
|
|
77
|
+
readonly entries: readonly DataStoreSeedEntry[];
|
|
78
|
+
/**
|
|
79
|
+
* Current file tree, available without reading note text.
|
|
80
|
+
*/
|
|
81
|
+
readonly files: readonly DataStoreSeedFile[];
|
|
82
|
+
/**
|
|
83
|
+
* Hashes text only when size matches but mtime differs.
|
|
84
|
+
*
|
|
85
|
+
* @param path - File requiring validation.
|
|
86
|
+
* @returns Its content hash.
|
|
87
|
+
*/
|
|
88
|
+
readonly hashFile: (path: string) => Promise<string>;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Optional performance hints. Seeds are never authoritative state and must be validated before reuse.
|
|
92
|
+
*/
|
|
93
|
+
export declare class DataStoreSeeds {
|
|
94
|
+
/**
|
|
95
|
+
* Device-local identity and serialization boundary.
|
|
96
|
+
*/
|
|
97
|
+
protected readonly local: LocalDataStore;
|
|
98
|
+
/**
|
|
99
|
+
* Whether this store shares cache seeds.
|
|
100
|
+
*/
|
|
101
|
+
protected readonly share: boolean;
|
|
102
|
+
/**
|
|
103
|
+
* Store-scoped snapshot transport.
|
|
104
|
+
*/
|
|
105
|
+
protected readonly transport: DataStoreTransport;
|
|
106
|
+
/**
|
|
107
|
+
* Creates a cache-seed publisher and reader.
|
|
108
|
+
*
|
|
109
|
+
* @param params - Local identity, transport and the per-store sharing setting.
|
|
110
|
+
*/
|
|
111
|
+
constructor(params: DataStoreSeedsConstructorParams);
|
|
112
|
+
/**
|
|
113
|
+
* Publishes a bounded seed, replacing only this device's earlier publications.
|
|
114
|
+
*
|
|
115
|
+
* @param entries - Derived records with file statistics and hashes.
|
|
116
|
+
* @returns Whether sharing was enabled and the seed was published.
|
|
117
|
+
*/
|
|
118
|
+
publish(entries: readonly DataStoreSeedEntry[]): Promise<boolean>;
|
|
119
|
+
/**
|
|
120
|
+
* Reads the newest complete seed for this transport's schema.
|
|
121
|
+
*
|
|
122
|
+
* @returns Seed records, or `undefined` if sharing is disabled or no complete seed is available yet.
|
|
123
|
+
*/
|
|
124
|
+
readLatest(): Promise<DataStoreSeedEntry[] | undefined>;
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Validates a seed using free file statistics first, hashing only size-equal mtime mismatches.
|
|
128
|
+
* Files absent locally are discarded. Consumers index only the returned rebuild paths.
|
|
129
|
+
*
|
|
130
|
+
* @param params - Seed, current file tree and content-hash reader.
|
|
131
|
+
* @returns Reusable records and the paths that still need indexing.
|
|
132
|
+
*/
|
|
133
|
+
export declare function validateDataStoreSeed(params: ValidateDataStoreSeedParams): Promise<DataStoreSeedValidation>;
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/*
|
|
2
|
+
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
|
3
|
+
if you want to view the source, please visit the github repository of this plugin
|
|
4
|
+
*/
|
|
5
|
+
(function () {
|
|
6
|
+
function ensureBrowserProcess() {
|
|
7
|
+
const browserProcess = {
|
|
8
|
+
browser: true,
|
|
9
|
+
cwd() {
|
|
10
|
+
return "/";
|
|
11
|
+
},
|
|
12
|
+
env: {},
|
|
13
|
+
platform: "android"
|
|
14
|
+
};
|
|
15
|
+
const existingProcess = globalThis.process;
|
|
16
|
+
if (!existingProcess) {
|
|
17
|
+
globalThis.process = browserProcess;
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (existingProcess.versions?.node) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const existingProcessRecord = existingProcess;
|
|
24
|
+
for (const [key, value] of Object.entries(browserProcess)) {
|
|
25
|
+
existingProcessRecord[key] ??= value;
|
|
26
|
+
}
|
|
27
|
+
existingProcessRecord["browser"] = true;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
ensureBrowserProcess();
|
|
31
|
+
})();
|
|
32
|
+
(function patchObsidianRequire() {
|
|
33
|
+
const originalRequire = require;
|
|
34
|
+
if (!originalRequire || originalRequire.__isPatched) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
require = Object.assign(
|
|
38
|
+
(id) => requirePatched(id),
|
|
39
|
+
originalRequire,
|
|
40
|
+
{
|
|
41
|
+
__isPatched: true
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
function extractDefault(module) {
|
|
45
|
+
return module && module.__esModule && "default" in module ? module.default : module;
|
|
46
|
+
}
|
|
47
|
+
const OBSIDIAN_BUILT_IN_MODULE_NAMES = /* @__PURE__ */ new Set([
|
|
48
|
+
"@codemirror/autocomplete",
|
|
49
|
+
"@codemirror/collab",
|
|
50
|
+
"@codemirror/commands",
|
|
51
|
+
"@codemirror/language",
|
|
52
|
+
"@codemirror/lint",
|
|
53
|
+
"@codemirror/search",
|
|
54
|
+
"@codemirror/state",
|
|
55
|
+
"@codemirror/text",
|
|
56
|
+
"@codemirror/view",
|
|
57
|
+
"@lezer/common",
|
|
58
|
+
"@lezer/highlight",
|
|
59
|
+
"@lezer/lr",
|
|
60
|
+
"obsidian"
|
|
61
|
+
]);
|
|
62
|
+
const DEPRECATED_OBSIDIAN_BUILT_IN_MODULE_NAMES = /* @__PURE__ */ new Set([
|
|
63
|
+
"@codemirror/closebrackets",
|
|
64
|
+
"@codemirror/comment",
|
|
65
|
+
"@codemirror/fold",
|
|
66
|
+
"@codemirror/gutter",
|
|
67
|
+
"@codemirror/highlight",
|
|
68
|
+
"@codemirror/history",
|
|
69
|
+
"@codemirror/matchbrackets",
|
|
70
|
+
"@codemirror/panel",
|
|
71
|
+
"@codemirror/rangeset",
|
|
72
|
+
"@codemirror/rectangular-selection",
|
|
73
|
+
"@codemirror/stream-parser",
|
|
74
|
+
"@codemirror/tooltip"
|
|
75
|
+
]);
|
|
76
|
+
function requirePatched(id) {
|
|
77
|
+
if (OBSIDIAN_BUILT_IN_MODULE_NAMES.has(id) || DEPRECATED_OBSIDIAN_BUILT_IN_MODULE_NAMES.has(id)) {
|
|
78
|
+
return originalRequire?.(id);
|
|
79
|
+
}
|
|
80
|
+
const app = globalThis.app;
|
|
81
|
+
if (app?.isMobile) {
|
|
82
|
+
if (id === "process" || id === "node:process") {
|
|
83
|
+
console.debug(`The most likely you can safely ignore this error. Module not found: ${id}. Fake process object is returned instead.`);
|
|
84
|
+
return process;
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
const module = originalRequire?.(id);
|
|
88
|
+
if (module) {
|
|
89
|
+
return extractDefault(module);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
console.debug(`The most likely you can safely ignore this error. Module not found: ${id}. Empty object is returned instead.`);
|
|
93
|
+
return {};
|
|
94
|
+
}
|
|
95
|
+
})();
|
|
96
|
+
|
|
97
|
+
"use strict";
|
|
98
|
+
var __defProp = Object.defineProperty;
|
|
99
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
100
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
101
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
102
|
+
var __export = (target, all) => {
|
|
103
|
+
for (var name in all)
|
|
104
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
105
|
+
};
|
|
106
|
+
var __copyProps = (to, from, except, desc) => {
|
|
107
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
108
|
+
for (let key of __getOwnPropNames(from))
|
|
109
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
110
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
111
|
+
}
|
|
112
|
+
return to;
|
|
113
|
+
};
|
|
114
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
115
|
+
var data_store_transport_exports = {};
|
|
116
|
+
__export(data_store_transport_exports, {
|
|
117
|
+
DataStoreTransport: () => DataStoreTransport
|
|
118
|
+
});
|
|
119
|
+
module.exports = __toCommonJS(data_store_transport_exports);
|
|
120
|
+
var import_type_guards = require('@mnaoumov/typescript-lib/type-guards');
|
|
121
|
+
var import_obsidian = require('obsidian');
|
|
122
|
+
var import_debug = require('../debug.cjs');
|
|
123
|
+
const DEFAULT_MAX_FILE_BYTES = 1e6;
|
|
124
|
+
const MIN_FILE_BYTES = 256;
|
|
125
|
+
const MAX_FILE_BYTES = 4e6;
|
|
126
|
+
const DOCUMENT_PREFIX = "```json\n";
|
|
127
|
+
const DOCUMENT_SUFFIX = "\n```\n";
|
|
128
|
+
const BISECTION_DIVISOR = 2;
|
|
129
|
+
const HEX_RADIX = 16;
|
|
130
|
+
const HEX_BYTE_LENGTH = 2;
|
|
131
|
+
class DataStoreTransport {
|
|
132
|
+
/**
|
|
133
|
+
* Creates a bounded vault transport.
|
|
134
|
+
*
|
|
135
|
+
* @param params - Adapter, identity and file bounds.
|
|
136
|
+
*/
|
|
137
|
+
constructor(params) {
|
|
138
|
+
this.params = params;
|
|
139
|
+
this.adapter = params.adapter;
|
|
140
|
+
const folder = params.folder ?? "Plugin data";
|
|
141
|
+
if (!folder || folder.split("/").some((part) => !part || part.startsWith(".") || part.includes("\\")) || !/^[\w-]+$/.test(params.pluginId) || !/^[\w-]+$/.test(params.storeId)) {
|
|
142
|
+
throw new Error("Data store requires a non-dot vault folder and safe plugin/store ids");
|
|
143
|
+
}
|
|
144
|
+
this.root = `${folder}/${params.pluginId}/${params.storeId}`;
|
|
145
|
+
this.maxFileBytes = params.maxFileBytes ?? DEFAULT_MAX_FILE_BYTES;
|
|
146
|
+
if (!Number.isSafeInteger(this.maxFileBytes) || this.maxFileBytes < MIN_FILE_BYTES || this.maxFileBytes > MAX_FILE_BYTES) {
|
|
147
|
+
throw new Error("Data-store file limit must be between 256 and 4000000 bytes");
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
params;
|
|
151
|
+
/**
|
|
152
|
+
* Maximum encoded bytes per document.
|
|
153
|
+
*/
|
|
154
|
+
maxFileBytes;
|
|
155
|
+
/**
|
|
156
|
+
* Full vault-relative store root.
|
|
157
|
+
*/
|
|
158
|
+
root;
|
|
159
|
+
/**
|
|
160
|
+
* Vault adapter used by this transport.
|
|
161
|
+
*/
|
|
162
|
+
adapter;
|
|
163
|
+
/**
|
|
164
|
+
* Tests the complete encoded size without writing.
|
|
165
|
+
*
|
|
166
|
+
* @param value - JSON payload.
|
|
167
|
+
* @returns Whether its document fits the configured cap.
|
|
168
|
+
*/
|
|
169
|
+
fits(value) {
|
|
170
|
+
return new TextEncoder().encode(this.encode(value)).length <= this.maxFileBytes;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* Lists the store's files recursively, never leaving its folder.
|
|
174
|
+
*
|
|
175
|
+
* @returns Vault-relative paths.
|
|
176
|
+
*/
|
|
177
|
+
async list() {
|
|
178
|
+
if (!await this.adapter.exists(this.root)) {
|
|
179
|
+
return [];
|
|
180
|
+
}
|
|
181
|
+
const pending = [this.root];
|
|
182
|
+
const files = [];
|
|
183
|
+
while (pending.length > 0) {
|
|
184
|
+
const folder = (0, import_type_guards.ensureNonNullable)(pending.shift());
|
|
185
|
+
const children = await this.adapter.list(folder);
|
|
186
|
+
files.push(...children.files.filter((path) => path.startsWith(`${folder}/`)));
|
|
187
|
+
pending.push(...children.folders.filter((path) => path.startsWith(`${folder}/`)));
|
|
188
|
+
}
|
|
189
|
+
return files.sort();
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Reads a complete fenced JSON document. Partial or not-yet-delivered documents are ignored.
|
|
193
|
+
*
|
|
194
|
+
* @param path - Vault-relative file within this store.
|
|
195
|
+
* @returns Parsed JSON, or `undefined` for an incomplete document.
|
|
196
|
+
*/
|
|
197
|
+
async read(path) {
|
|
198
|
+
this.checkPath(path);
|
|
199
|
+
if (!await this.adapter.exists(path)) {
|
|
200
|
+
return void 0;
|
|
201
|
+
}
|
|
202
|
+
let text;
|
|
203
|
+
try {
|
|
204
|
+
text = await this.adapter.read(path);
|
|
205
|
+
} catch (error) {
|
|
206
|
+
if (!await this.adapter.exists(path)) {
|
|
207
|
+
return void 0;
|
|
208
|
+
}
|
|
209
|
+
throw error;
|
|
210
|
+
}
|
|
211
|
+
this.checkSize(text);
|
|
212
|
+
if (!text.startsWith(DOCUMENT_PREFIX) || !text.endsWith(DOCUMENT_SUFFIX)) {
|
|
213
|
+
return void 0;
|
|
214
|
+
}
|
|
215
|
+
const parsed = JSON.parse(text.slice(DOCUMENT_PREFIX.length, -DOCUMENT_SUFFIX.length));
|
|
216
|
+
return parsed;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Reads a snapshot only after every numbered chunk arrives and passes its SHA-256 check.
|
|
220
|
+
*
|
|
221
|
+
* @param path - Vault-relative manifest path.
|
|
222
|
+
* @returns Its payload, or `undefined` while delivery is incomplete.
|
|
223
|
+
*/
|
|
224
|
+
async readSnapshot(path) {
|
|
225
|
+
const manifest = await this.read(path);
|
|
226
|
+
if (typeof manifest !== "object" || manifest === null || !("format" in manifest) || manifest.format !== 1 || !("hashes" in manifest) || !Array.isArray(manifest.hashes) || manifest.hashes.length === 0 || manifest.hashes.some((hash) => typeof hash !== "string")) {
|
|
227
|
+
return void 0;
|
|
228
|
+
}
|
|
229
|
+
const chunks = [];
|
|
230
|
+
for (const [index, hash] of manifest.hashes.entries()) {
|
|
231
|
+
const chunk = await this.read(`${path.slice(0, -".md".length)}-${String(index)}.md`);
|
|
232
|
+
if (typeof chunk !== "string" || await digest(chunk) !== hash) {
|
|
233
|
+
return void 0;
|
|
234
|
+
}
|
|
235
|
+
chunks.push(chunk);
|
|
236
|
+
}
|
|
237
|
+
const parsed = JSON.parse(chunks.join(""));
|
|
238
|
+
return parsed;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Removes a document owned by the caller's device.
|
|
242
|
+
*
|
|
243
|
+
* @param path - Vault-relative file within this store.
|
|
244
|
+
* @returns A promise that resolves after removal.
|
|
245
|
+
*/
|
|
246
|
+
async remove(path) {
|
|
247
|
+
this.checkPath(path);
|
|
248
|
+
await this.adapter.remove(path);
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Writes a complete document, refusing an oversized write before adapter mutation.
|
|
252
|
+
*
|
|
253
|
+
* @param path - Vault-relative file within this store.
|
|
254
|
+
* @param value - JSON-compatible payload.
|
|
255
|
+
* @returns A promise that resolves once the adapter finishes writing.
|
|
256
|
+
*/
|
|
257
|
+
async write(path, value) {
|
|
258
|
+
this.checkPath(path);
|
|
259
|
+
const text = this.encode(value);
|
|
260
|
+
this.checkSize(text);
|
|
261
|
+
const parts = path.split("/");
|
|
262
|
+
parts.pop();
|
|
263
|
+
let folder = "";
|
|
264
|
+
for (const part of parts) {
|
|
265
|
+
folder = folder ? `${folder}/${part}` : part;
|
|
266
|
+
if (!await this.adapter.exists(folder)) {
|
|
267
|
+
try {
|
|
268
|
+
await this.adapter.mkdir(folder);
|
|
269
|
+
} catch (error) {
|
|
270
|
+
if (!await this.adapter.exists(folder)) {
|
|
271
|
+
throw error;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
await this.adapter.write(path, text);
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Publishes bounded immutable chunks before their manifest. Readers verify delivery completeness.
|
|
280
|
+
*
|
|
281
|
+
* @param params - Unique base path and snapshot payload.
|
|
282
|
+
* @returns A promise that resolves when the manifest has been published.
|
|
283
|
+
*/
|
|
284
|
+
async writeSnapshot(params) {
|
|
285
|
+
const payload = JSON.stringify(params.payload);
|
|
286
|
+
if (payload === void 0) {
|
|
287
|
+
throw new Error("Snapshot must be JSON-compatible");
|
|
288
|
+
}
|
|
289
|
+
const chunks = [];
|
|
290
|
+
let offset = 0;
|
|
291
|
+
while (offset < payload.length) {
|
|
292
|
+
let low = 1;
|
|
293
|
+
let high = Math.min(payload.length - offset, this.maxFileBytes);
|
|
294
|
+
while (low < high) {
|
|
295
|
+
const middle = Math.ceil((low + high) / BISECTION_DIVISOR);
|
|
296
|
+
if (this.fits(payload.slice(offset, offset + middle))) {
|
|
297
|
+
low = middle;
|
|
298
|
+
} else {
|
|
299
|
+
high = middle - 1;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
chunks.push(payload.slice(offset, offset + low));
|
|
303
|
+
offset += low;
|
|
304
|
+
}
|
|
305
|
+
const hashes = [];
|
|
306
|
+
for (const chunk of chunks) {
|
|
307
|
+
hashes.push(await digest(chunk));
|
|
308
|
+
}
|
|
309
|
+
const manifest = { format: 1, hashes };
|
|
310
|
+
this.checkSize(this.encode(manifest));
|
|
311
|
+
for (const [index, chunk] of chunks.entries()) {
|
|
312
|
+
await this.write(`${params.base}-${String(index)}.md`, chunk);
|
|
313
|
+
}
|
|
314
|
+
await this.write(`${params.base}.md`, manifest);
|
|
315
|
+
}
|
|
316
|
+
checkPath(path) {
|
|
317
|
+
if (!path.startsWith(`${this.root}/`) || path.split("/").some((part) => !part || part.startsWith(".") || part.includes("\\"))) {
|
|
318
|
+
throw new Error("Data-store path leaves its configured folder");
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
checkSize(text) {
|
|
322
|
+
const size = new TextEncoder().encode(text).length;
|
|
323
|
+
if (size <= this.maxFileBytes) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
const message = `Plugin data store: ${String(size)} bytes exceeds the ${String(this.maxFileBytes)} byte file limit.`;
|
|
327
|
+
new import_obsidian.Notice(message);
|
|
328
|
+
(0, import_debug.getObsidianLibDebugger)("DataStore")(message);
|
|
329
|
+
throw new Error(message);
|
|
330
|
+
}
|
|
331
|
+
encode(value) {
|
|
332
|
+
const json = JSON.stringify(value);
|
|
333
|
+
if (json === void 0) {
|
|
334
|
+
throw new Error("Data-store payload must be JSON-compatible");
|
|
335
|
+
}
|
|
336
|
+
return `${DOCUMENT_PREFIX}${json}${DOCUMENT_SUFFIX}`;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
async function digest(text) {
|
|
340
|
+
const hash = await window.crypto.subtle.digest("SHA-256", new TextEncoder().encode(text));
|
|
341
|
+
return [...new Uint8Array(hash)].map((byte) => byte.toString(HEX_RADIX).padStart(HEX_BYTE_LENGTH, "0")).join("");
|
|
342
|
+
}
|
|
343
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
344
|
+
0 && (module.exports = {
|
|
345
|
+
DataStoreTransport
|
|
346
|
+
});
|
|
347
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9kYXRhLXN0b3Jlcy9kYXRhLXN0b3JlLXRyYW5zcG9ydC50cyJdLCJzb3VyY2VzQ29udGVudCI6WyIvKiBlc2xpbnQtZGlzYWJsZSBuL25vLXVuc3VwcG9ydGVkLWZlYXR1cmVzL25vZGUtYnVpbHRpbnMgLS0gVGhlc2UgYXJlIE9ic2lkaWFuIHJlbmRlcmVyIFdlYiBBUElzLCBub3QgTm9kZSBidWlsdGlucy4gKi9cbi8qKlxuICogQGZpbGVcbiAqXG4gKiBTaXplLWJvdW5kZWQgTWFya2Rvd24gdHJhbnNwb3J0IGNhcnJpZWQgYnkgdmF1bHQgc3luY2hyb25pemF0aW9uIHdpdGggZGVmYXVsdCBmaWxlIHR5cGVzLlxuICovXG5cbmltcG9ydCB0eXBlIHsgRGF0YUFkYXB0ZXIgfSBmcm9tICdvYnNpZGlhbic7XG5cbmltcG9ydCB7IGVuc3VyZU5vbk51bGxhYmxlIH0gZnJvbSAnQG1uYW91bW92L3R5cGVzY3JpcHQtbGliL3R5cGUtZ3VhcmRzJztcbmltcG9ydCB7IE5vdGljZSB9IGZyb20gJ29ic2lkaWFuJztcblxuaW1wb3J0IHsgZ2V0T2JzaWRpYW5MaWJEZWJ1Z2dlciB9IGZyb20gJy4uL2RlYnVnLnRzJztcblxuY29uc3QgREVGQVVMVF9NQVhfRklMRV9CWVRFUyA9IDFfMDAwXzAwMDtcbmNvbnN0IE1JTl9GSUxFX0JZVEVTID0gMjU2O1xuY29uc3QgTUFYX0ZJTEVfQllURVMgPSA0XzAwMF8wMDA7XG5jb25zdCBET0NVTUVOVF9QUkVGSVggPSAnYGBganNvblxcbic7XG5jb25zdCBET0NVTUVOVF9TVUZGSVggPSAnXFxuYGBgXFxuJztcbmNvbnN0IEJJU0VDVElPTl9ESVZJU09SID0gMjtcbmNvbnN0IEhFWF9SQURJWCA9IDE2O1xuY29uc3QgSEVYX0JZVEVfTEVOR1RIID0gMjtcblxuLyoqXG4gKiBQYXJhbWV0ZXJzIGZvciB0aGUgc2hhcmVkLXN0b3JlIHZhdWx0IHRyYW5zcG9ydC5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBEYXRhU3RvcmVUcmFuc3BvcnRDb25zdHJ1Y3RvclBhcmFtcyB7XG4gIC8qKlxuICAgKiBWYXVsdCBhZGFwdGVyOyBubyBkZXNrdG9wIGZpbGVzeXN0ZW0gQVBJcyBhcmUgdXNlZC5cbiAgICovXG4gIHJlYWRvbmx5IGFkYXB0ZXI6IERhdGFBZGFwdGVyO1xuICAvKipcbiAgICogQ29uZmlndXJhYmxlIG5vbi1kb3QgdmF1bHQgZm9sZGVyLlxuICAgKlxuICAgKiBAZGVmYXVsdCBgJ1BsdWdpbiBkYXRhJ2BcbiAgICovXG4gIHJlYWRvbmx5IGZvbGRlcj86IHN0cmluZztcbiAgLyoqXG4gICAqIE1heGltdW0gVVRGLTggYnl0ZXMgaW4gYW55IGZpbGUsIGluY2x1ZGluZyBpdHMgTWFya2Rvd24gd3JhcHBlci5cbiAgICpcbiAgICogQGRlZmF1bHQgYDEwMDAwMDBgXG4gICAqL1xuICByZWFkb25seSBtYXhGaWxlQnl0ZXM/OiBudW1iZXI7XG4gIC8qKlxuICAgKiBQbHVnaW4gbWFuaWZlc3QgaWQuXG4gICAqL1xuICByZWFkb25seSBwbHVnaW5JZDogc3RyaW5nO1xuICAvKipcbiAgICogU3RvcmUgbmFtZSBpbmNsdWRpbmcgaXRzIGNvbnN1bWVyIHNjaGVtYSB2ZXJzaW9uLlxuICAgKi9cbiAgcmVhZG9ubHkgc3RvcmVJZDogc3RyaW5nO1xufVxuXG4vKipcbiAqIFBhcmFtZXRlcnMgZm9yIHB1Ymxpc2hpbmcgYSBjaHVua2VkIHNuYXBzaG90LlxuICovXG5leHBvcnQgaW50ZXJmYWNlIERhdGFTdG9yZVRyYW5zcG9ydFdyaXRlU25hcHNob3RQYXJhbXMge1xuICAvKipcbiAgICogVmF1bHQtcmVsYXRpdmUgYmFzZSBwYXRoIHdpdGhvdXQgdGhlIGAubWRgIGV4dGVuc2lvbi4gVXNlIGEgZnJlc2ggdW5pcXVlIG5hbWUgZm9yIGV2ZXJ5IHB1YmxpY2F0aW9uLlxuICAgKi9cbiAgcmVhZG9ubHkgYmFzZTogc3RyaW5nO1xuICAvKipcbiAgICogSlNPTi1jb21wYXRpYmxlIHNuYXBzaG90LCBpbmNsdWRpbmcgaXRzIGNvbnN1bWVyIHNjaGVtYSBhbmQgY292ZXJhZ2UgbWV0YWRhdGEuXG4gICAqL1xuICByZWFkb25seSBwYXlsb2FkOiB1bmtub3duO1xufVxuXG4vKipcbiAqIEEgc3RvcmUtc2NvcGVkIHRyYW5zcG9ydC4gRXZlcnkgcHVibGlzaGVkIGRvY3VtZW50IGlzIGltbXV0YWJsZSBleGNlcHQgZGV2aWNlIGFja25vd2xlZGdlbWVudHMuXG4gKi9cbmV4cG9ydCBjbGFzcyBEYXRhU3RvcmVUcmFuc3BvcnQge1xuICAvKipcbiAgICogTWF4aW11bSBlbmNvZGVkIGJ5dGVzIHBlciBkb2N1bWVudC5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSBtYXhGaWxlQnl0ZXM6IG51bWJlcjtcbiAgLyoqXG4gICAqIEZ1bGwgdmF1bHQtcmVsYXRpdmUgc3RvcmUgcm9vdC5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSByb290OiBzdHJpbmc7XG4gIC8qKlxuICAgKiBWYXVsdCBhZGFwdGVyIHVzZWQgYnkgdGhpcyB0cmFuc3BvcnQuXG4gICAqL1xuICBwcm90ZWN0ZWQgcmVhZG9ubHkgYWRhcHRlcjogRGF0YUFkYXB0ZXI7XG5cbiAgLyoqXG4gICAqIENyZWF0ZXMgYSBib3VuZGVkIHZhdWx0IHRyYW5zcG9ydC5cbiAgICpcbiAgICogQHBhcmFtIHBhcmFtcyAtIEFkYXB0ZXIsIGlkZW50aXR5IGFuZCBmaWxlIGJvdW5kcy5cbiAgICovXG4gIHB1YmxpYyBjb25zdHJ1Y3Rvcihwcm90ZWN0ZWQgcmVhZG9ubHkgcGFyYW1zOiBEYXRhU3RvcmVUcmFuc3BvcnRDb25zdHJ1Y3RvclBhcmFtcykge1xuICAgIHRoaXMuYWRhcHRlciA9IHBhcmFtcy5hZGFwdGVyO1xuICAgIGNvbnN0IGZvbGRlciA9IHBhcmFtcy5mb2xkZXIgPz8gJ1BsdWdpbiBkYXRhJztcbiAgICBpZiAoXG4gICAgICAhZm9sZGVyIHx8IGZvbGRlci5zcGxpdCgnLycpLnNvbWUoKHBhcnQpID0+ICFwYXJ0IHx8IHBhcnQuc3RhcnRzV2l0aCgnLicpIHx8IHBhcnQuaW5jbHVkZXMoJ1xcXFwnKSlcbiAgICAgIHx8ICEvXltcXHctXSskLy50ZXN0KHBhcmFtcy5wbHVnaW5JZCkgfHwgIS9eW1xcdy1dKyQvLnRlc3QocGFyYW1zLnN0b3JlSWQpXG4gICAgKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ0RhdGEgc3RvcmUgcmVxdWlyZXMgYSBub24tZG90IHZhdWx0IGZvbGRlciBhbmQgc2FmZSBwbHVnaW4vc3RvcmUgaWRzJyk7XG4gICAgfVxuICAgIHRoaXMucm9vdCA9IGAke2ZvbGRlcn0vJHtwYXJhbXMucGx1Z2luSWR9LyR7cGFyYW1zLnN0b3JlSWR9YDtcbiAgICB0aGlzLm1heEZpbGVCeXRlcyA9IHBhcmFtcy5tYXhGaWxlQnl0ZXMgPz8gREVGQVVMVF9NQVhfRklMRV9CWVRFUztcbiAgICBpZiAoIU51bWJlci5pc1NhZmVJbnRlZ2VyKHRoaXMubWF4RmlsZUJ5dGVzKSB8fCB0aGlzLm1heEZpbGVCeXRlcyA8IE1JTl9GSUxFX0JZVEVTIHx8IHRoaXMubWF4RmlsZUJ5dGVzID4gTUFYX0ZJTEVfQllURVMpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignRGF0YS1zdG9yZSBmaWxlIGxpbWl0IG11c3QgYmUgYmV0d2VlbiAyNTYgYW5kIDQwMDAwMDAgYnl0ZXMnKTtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogVGVzdHMgdGhlIGNvbXBsZXRlIGVuY29kZWQgc2l6ZSB3aXRob3V0IHdyaXRpbmcuXG4gICAqXG4gICAqIEBwYXJhbSB2YWx1ZSAtIEpTT04gcGF5bG9hZC5cbiAgICogQHJldHVybnMgV2hldGhlciBpdHMgZG9jdW1lbnQgZml0cyB0aGUgY29uZmlndXJlZCBjYXAuXG4gICAqL1xuICBwdWJsaWMgZml0cyh2YWx1ZTogdW5rbm93bik6IGJvb2xlYW4ge1xuICAgIHJldHVybiBuZXcgVGV4dEVuY29kZXIoKS5lbmNvZGUodGhpcy5lbmNvZGUodmFsdWUpKS5sZW5ndGggPD0gdGhpcy5tYXhGaWxlQnl0ZXM7XG4gIH1cblxuICAvKipcbiAgICogTGlzdHMgdGhlIHN0b3JlJ3MgZmlsZXMgcmVjdXJzaXZlbHksIG5ldmVyIGxlYXZpbmcgaXRzIGZvbGRlci5cbiAgICpcbiAgICogQHJldHVybnMgVmF1bHQtcmVsYXRpdmUgcGF0aHMuXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgbGlzdCgpOiBQcm9taXNlPHN0cmluZ1tdPiB7XG4gICAgaWYgKCFhd2FpdCB0aGlzLmFkYXB0ZXIuZXhpc3RzKHRoaXMucm9vdCkpIHtcbiAgICAgIHJldHVybiBbXTtcbiAgICB9XG4gICAgY29uc3QgcGVuZGluZyA9IFt0aGlzLnJvb3RdO1xuICAgIGNvbnN0IGZpbGVzOiBzdHJpbmdbXSA9IFtdO1xuICAgIHdoaWxlIChwZW5kaW5nLmxlbmd0aCA+IDApIHtcbiAgICAgIGNvbnN0IGZvbGRlciA9IGVuc3VyZU5vbk51bGxhYmxlKHBlbmRpbmcuc2hpZnQoKSk7XG4gICAgICBjb25zdCBjaGlsZHJlbiA9IGF3YWl0IHRoaXMuYWRhcHRlci5saXN0KGZvbGRlcik7XG4gICAgICBmaWxlcy5wdXNoKC4uLmNoaWxkcmVuLmZpbGVzLmZpbHRlcigocGF0aCkgPT4gcGF0aC5zdGFydHNXaXRoKGAke2ZvbGRlcn0vYCkpKTtcbiAgICAgIHBlbmRpbmcucHVzaCguLi5jaGlsZHJlbi5mb2xkZXJzLmZpbHRlcigocGF0aCkgPT4gcGF0aC5zdGFydHNXaXRoKGAke2ZvbGRlcn0vYCkpKTtcbiAgICB9XG4gICAgcmV0dXJuIGZpbGVzLnNvcnQoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBSZWFkcyBhIGNvbXBsZXRlIGZlbmNlZCBKU09OIGRvY3VtZW50LiBQYXJ0aWFsIG9yIG5vdC15ZXQtZGVsaXZlcmVkIGRvY3VtZW50cyBhcmUgaWdub3JlZC5cbiAgICpcbiAgICogQHBhcmFtIHBhdGggLSBWYXVsdC1yZWxhdGl2ZSBmaWxlIHdpdGhpbiB0aGlzIHN0b3JlLlxuICAgKiBAcmV0dXJucyBQYXJzZWQgSlNPTiwgb3IgYHVuZGVmaW5lZGAgZm9yIGFuIGluY29tcGxldGUgZG9jdW1lbnQuXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgcmVhZChwYXRoOiBzdHJpbmcpOiBQcm9taXNlPHVua25vd24+IHtcbiAgICB0aGlzLmNoZWNrUGF0aChwYXRoKTtcbiAgICBpZiAoIWF3YWl0IHRoaXMuYWRhcHRlci5leGlzdHMocGF0aCkpIHtcbiAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgfVxuICAgIGxldCB0ZXh0OiBzdHJpbmc7XG4gICAgdHJ5IHtcbiAgICAgIHRleHQgPSBhd2FpdCB0aGlzLmFkYXB0ZXIucmVhZChwYXRoKTtcbiAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgaWYgKCFhd2FpdCB0aGlzLmFkYXB0ZXIuZXhpc3RzKHBhdGgpKSB7XG4gICAgICAgIHJldHVybiB1bmRlZmluZWQ7XG4gICAgICB9XG4gICAgICB0aHJvdyBlcnJvcjtcbiAgICB9XG4gICAgdGhpcy5jaGVja1NpemUodGV4dCk7XG4gICAgaWYgKCF0ZXh0LnN0YXJ0c1dpdGgoRE9DVU1FTlRfUFJFRklYKSB8fCAhdGV4dC5lbmRzV2l0aChET0NVTUVOVF9TVUZGSVgpKSB7XG4gICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgIH1cbiAgICBjb25zdCBwYXJzZWQ6IHVua25vd24gPSBKU09OLnBhcnNlKHRleHQuc2xpY2UoRE9DVU1FTlRfUFJFRklYLmxlbmd0aCwgLURPQ1VNRU5UX1NVRkZJWC5sZW5ndGgpKTtcbiAgICByZXR1cm4gcGFyc2VkO1xuICB9XG5cbiAgLyoqXG4gICAqIFJlYWRzIGEgc25hcHNob3Qgb25seSBhZnRlciBldmVyeSBudW1iZXJlZCBjaHVuayBhcnJpdmVzIGFuZCBwYXNzZXMgaXRzIFNIQS0yNTYgY2hlY2suXG4gICAqXG4gICAqIEBwYXJhbSBwYXRoIC0gVmF1bHQtcmVsYXRpdmUgbWFuaWZlc3QgcGF0aC5cbiAgICogQHJldHVybnMgSXRzIHBheWxvYWQsIG9yIGB1bmRlZmluZWRgIHdoaWxlIGRlbGl2ZXJ5IGlzIGluY29tcGxldGUuXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgcmVhZFNuYXBzaG90KHBhdGg6IHN0cmluZyk6IFByb21pc2U8dW5rbm93bj4ge1xuICAgIGNvbnN0IG1hbmlmZXN0ID0gYXdhaXQgdGhpcy5yZWFkKHBhdGgpO1xuICAgIGlmIChcbiAgICAgIHR5cGVvZiBtYW5pZmVzdCAhPT0gJ29iamVjdCcgfHwgbWFuaWZlc3QgPT09IG51bGwgfHwgISgnZm9ybWF0JyBpbiBtYW5pZmVzdCkgfHwgbWFuaWZlc3QuZm9ybWF0ICE9PSAxXG4gICAgICB8fCAhKCdoYXNoZXMnIGluIG1hbmlmZXN0KSB8fCAhQXJyYXkuaXNBcnJheShtYW5pZmVzdC5oYXNoZXMpXG4gICAgICB8fCBtYW5pZmVzdC5oYXNoZXMubGVuZ3RoID09PSAwIHx8IG1hbmlmZXN0Lmhhc2hlcy5zb21lKChoYXNoOiB1bmtub3duKSA9PiB0eXBlb2YgaGFzaCAhPT0gJ3N0cmluZycpXG4gICAgKSB7XG4gICAgICByZXR1cm4gdW5kZWZpbmVkO1xuICAgIH1cbiAgICBjb25zdCBjaHVua3M6IHN0cmluZ1tdID0gW107XG4gICAgZm9yIChjb25zdCBbaW5kZXgsIGhhc2hdIG9mIG1hbmlmZXN0Lmhhc2hlcy5lbnRyaWVzKCkpIHtcbiAgICAgIGNvbnN0IGNodW5rID0gYXdhaXQgdGhpcy5yZWFkKGAke3BhdGguc2xpY2UoMCwgLScubWQnLmxlbmd0aCl9LSR7U3RyaW5nKGluZGV4KX0ubWRgKTtcbiAgICAgIGlmICh0eXBlb2YgY2h1bmsgIT09ICdzdHJpbmcnIHx8IGF3YWl0IGRpZ2VzdChjaHVuaykgIT09IGhhc2gpIHtcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgICAgIH1cbiAgICAgIGNodW5rcy5wdXNoKGNodW5rKTtcbiAgICB9XG4gICAgY29uc3QgcGFyc2VkOiB1bmtub3duID0gSlNPTi5wYXJzZShjaHVua3Muam9pbignJykpO1xuICAgIHJldHVybiBwYXJzZWQ7XG4gIH1cblxuICAvKipcbiAgICogUmVtb3ZlcyBhIGRvY3VtZW50IG93bmVkIGJ5IHRoZSBjYWxsZXIncyBkZXZpY2UuXG4gICAqXG4gICAqIEBwYXJhbSBwYXRoIC0gVmF1bHQtcmVsYXRpdmUgZmlsZSB3aXRoaW4gdGhpcyBzdG9yZS5cbiAgICogQHJldHVybnMgQSBwcm9taXNlIHRoYXQgcmVzb2x2ZXMgYWZ0ZXIgcmVtb3ZhbC5cbiAgICovXG4gIHB1YmxpYyBhc3luYyByZW1vdmUocGF0aDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgdGhpcy5jaGVja1BhdGgocGF0aCk7XG4gICAgYXdhaXQgdGhpcy5hZGFwdGVyLnJlbW92ZShwYXRoKTtcbiAgfVxuXG4gIC8qKlxuICAgKiBXcml0ZXMgYSBjb21wbGV0ZSBkb2N1bWVudCwgcmVmdXNpbmcgYW4gb3ZlcnNpemVkIHdyaXRlIGJlZm9yZSBhZGFwdGVyIG11dGF0aW9uLlxuICAgKlxuICAgKiBAcGFyYW0gcGF0aCAtIFZhdWx0LXJlbGF0aXZlIGZpbGUgd2l0aGluIHRoaXMgc3RvcmUuXG4gICAqIEBwYXJhbSB2YWx1ZSAtIEpTT04tY29tcGF0aWJsZSBwYXlsb2FkLlxuICAgKiBAcmV0dXJucyBBIHByb21pc2UgdGhhdCByZXNvbHZlcyBvbmNlIHRoZSBhZGFwdGVyIGZpbmlzaGVzIHdyaXRpbmcuXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgd3JpdGUocGF0aDogc3RyaW5nLCB2YWx1ZTogdW5rbm93bik6IFByb21pc2U8dm9pZD4ge1xuICAgIHRoaXMuY2hlY2tQYXRoKHBhdGgpO1xuICAgIGNvbnN0IHRleHQgPSB0aGlzLmVuY29kZSh2YWx1ZSk7XG4gICAgdGhpcy5jaGVja1NpemUodGV4dCk7XG4gICAgY29uc3QgcGFydHMgPSBwYXRoLnNwbGl0KCcvJyk7XG4gICAgcGFydHMucG9wKCk7XG4gICAgbGV0IGZvbGRlciA9ICcnO1xuICAgIGZvciAoY29uc3QgcGFydCBvZiBwYXJ0cykge1xuICAgICAgZm9sZGVyID0gZm9sZGVyID8gYCR7Zm9sZGVyfS8ke3BhcnR9YCA6IHBhcnQ7XG4gICAgICBpZiAoIWF3YWl0IHRoaXMuYWRhcHRlci5leGlzdHMoZm9sZGVyKSkge1xuICAgICAgICB0cnkge1xuICAgICAgICAgIGF3YWl0IHRoaXMuYWRhcHRlci5ta2Rpcihmb2xkZXIpO1xuICAgICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICAgIGlmICghYXdhaXQgdGhpcy5hZGFwdGVyLmV4aXN0cyhmb2xkZXIpKSB7XG4gICAgICAgICAgICB0aHJvdyBlcnJvcjtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gICAgYXdhaXQgdGhpcy5hZGFwdGVyLndyaXRlKHBhdGgsIHRleHQpO1xuICB9XG5cbiAgLyoqXG4gICAqIFB1Ymxpc2hlcyBib3VuZGVkIGltbXV0YWJsZSBjaHVua3MgYmVmb3JlIHRoZWlyIG1hbmlmZXN0LiBSZWFkZXJzIHZlcmlmeSBkZWxpdmVyeSBjb21wbGV0ZW5lc3MuXG4gICAqXG4gICAqIEBwYXJhbSBwYXJhbXMgLSBVbmlxdWUgYmFzZSBwYXRoIGFuZCBzbmFwc2hvdCBwYXlsb2FkLlxuICAgKiBAcmV0dXJucyBBIHByb21pc2UgdGhhdCByZXNvbHZlcyB3aGVuIHRoZSBtYW5pZmVzdCBoYXMgYmVlbiBwdWJsaXNoZWQuXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgd3JpdGVTbmFwc2hvdChwYXJhbXM6IERhdGFTdG9yZVRyYW5zcG9ydFdyaXRlU25hcHNob3RQYXJhbXMpOiBQcm9taXNlPHZvaWQ+IHtcbiAgICBjb25zdCBwYXlsb2FkID0gSlNPTi5zdHJpbmdpZnkocGFyYW1zLnBheWxvYWQpO1xuICAgIGlmIChwYXlsb2FkID09PSB1bmRlZmluZWQpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcignU25hcHNob3QgbXVzdCBiZSBKU09OLWNvbXBhdGlibGUnKTtcbiAgICB9XG4gICAgY29uc3QgY2h1bmtzOiBzdHJpbmdbXSA9IFtdO1xuICAgIGxldCBvZmZzZXQgPSAwO1xuICAgIHdoaWxlIChvZmZzZXQgPCBwYXlsb2FkLmxlbmd0aCkge1xuICAgICAgbGV0IGxvdyA9IDE7XG4gICAgICBsZXQgaGlnaCA9IE1hdGgubWluKHBheWxvYWQubGVuZ3RoIC0gb2Zmc2V0LCB0aGlzLm1heEZpbGVCeXRlcyk7XG4gICAgICB3aGlsZSAobG93IDwgaGlnaCkge1xuICAgICAgICBjb25zdCBtaWRkbGUgPSBNYXRoLmNlaWwoKGxvdyArIGhpZ2gpIC8gQklTRUNUSU9OX0RJVklTT1IpO1xuICAgICAgICBpZiAodGhpcy5maXRzKHBheWxvYWQuc2xpY2Uob2Zmc2V0LCBvZmZzZXQgKyBtaWRkbGUpKSkge1xuICAgICAgICAgIGxvdyA9IG1pZGRsZTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBoaWdoID0gbWlkZGxlIC0gMTtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgY2h1bmtzLnB1c2gocGF5bG9hZC5zbGljZShvZmZzZXQsIG9mZnNldCArIGxvdykpO1xuICAgICAgb2Zmc2V0ICs9IGxvdztcbiAgICB9XG4gICAgY29uc3QgaGFzaGVzOiBzdHJpbmdbXSA9IFtdO1xuICAgIGZvciAoY29uc3QgY2h1bmsgb2YgY2h1bmtzKSB7XG4gICAgICBoYXNoZXMucHVzaChhd2FpdCBkaWdlc3QoY2h1bmspKTtcbiAgICB9XG4gICAgY29uc3QgbWFuaWZlc3QgPSB7IGZvcm1hdDogMSwgaGFzaGVzIH07XG4gICAgdGhpcy5jaGVja1NpemUodGhpcy5lbmNvZGUobWFuaWZlc3QpKTtcbiAgICBmb3IgKGNvbnN0IFtpbmRleCwgY2h1bmtdIG9mIGNodW5rcy5lbnRyaWVzKCkpIHtcbiAgICAgIGF3YWl0IHRoaXMud3JpdGUoYCR7cGFyYW1zLmJhc2V9LSR7U3RyaW5nKGluZGV4KX0ubWRgLCBjaHVuayk7XG4gICAgfVxuICAgIGF3YWl0IHRoaXMud3JpdGUoYCR7cGFyYW1zLmJhc2V9Lm1kYCwgbWFuaWZlc3QpO1xuICB9XG5cbiAgcHJpdmF0ZSBjaGVja1BhdGgocGF0aDogc3RyaW5nKTogdm9pZCB7XG4gICAgaWYgKCFwYXRoLnN0YXJ0c1dpdGgoYCR7dGhpcy5yb290fS9gKSB8fCBwYXRoLnNwbGl0KCcvJykuc29tZSgocGFydCkgPT4gIXBhcnQgfHwgcGFydC5zdGFydHNXaXRoKCcuJykgfHwgcGFydC5pbmNsdWRlcygnXFxcXCcpKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdEYXRhLXN0b3JlIHBhdGggbGVhdmVzIGl0cyBjb25maWd1cmVkIGZvbGRlcicpO1xuICAgIH1cbiAgfVxuXG4gIHByaXZhdGUgY2hlY2tTaXplKHRleHQ6IHN0cmluZyk6IHZvaWQge1xuICAgIGNvbnN0IHNpemUgPSBuZXcgVGV4dEVuY29kZXIoKS5lbmNvZGUodGV4dCkubGVuZ3RoO1xuICAgIGlmIChzaXplIDw9IHRoaXMubWF4RmlsZUJ5dGVzKSB7XG4gICAgICByZXR1cm47XG4gICAgfVxuICAgIGNvbnN0IG1lc3NhZ2UgPSBgUGx1Z2luIGRhdGEgc3RvcmU6ICR7U3RyaW5nKHNpemUpfSBieXRlcyBleGNlZWRzIHRoZSAke1N0cmluZyh0aGlzLm1heEZpbGVCeXRlcyl9IGJ5dGUgZmlsZSBsaW1pdC5gO1xuICAgIG5ldyBOb3RpY2UobWVzc2FnZSk7XG4gICAgZ2V0T2JzaWRpYW5MaWJEZWJ1Z2dlcignRGF0YVN0b3JlJykobWVzc2FnZSk7XG4gICAgdGhyb3cgbmV3IEVycm9yKG1lc3NhZ2UpO1xuICB9XG5cbiAgcHJpdmF0ZSBlbmNvZGUodmFsdWU6IHVua25vd24pOiBzdHJpbmcge1xuICAgIGNvbnN0IGpzb24gPSBKU09OLnN0cmluZ2lmeSh2YWx1ZSk7XG4gICAgaWYgKGpzb24gPT09IHVuZGVmaW5lZCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdEYXRhLXN0b3JlIHBheWxvYWQgbXVzdCBiZSBKU09OLWNvbXBhdGlibGUnKTtcbiAgICB9XG4gICAgcmV0dXJuIGAke0RPQ1VNRU5UX1BSRUZJWH0ke2pzb259JHtET0NVTUVOVF9TVUZGSVh9YDtcbiAgfVxufVxuXG5hc3luYyBmdW5jdGlvbiBkaWdlc3QodGV4dDogc3RyaW5nKTogUHJvbWlzZTxzdHJpbmc+IHtcbiAgY29uc3QgaGFzaCA9IGF3YWl0IHdpbmRvdy5jcnlwdG8uc3VidGxlLmRpZ2VzdCgnU0hBLTI1NicsIG5ldyBUZXh0RW5jb2RlcigpLmVuY29kZSh0ZXh0KSk7XG4gIHJldHVybiBbLi4ubmV3IFVpbnQ4QXJyYXkoaGFzaCldLm1hcCgoYnl0ZSkgPT4gYnl0ZS50b1N0cmluZyhIRVhfUkFESVgpLnBhZFN0YXJ0KEhFWF9CWVRFX0xFTkdUSCwgJzAnKSkuam9pbignJyk7XG59XG5cbi8qIGVzbGludC1lbmFibGUgbi9uby11bnN1cHBvcnRlZC1mZWF0dXJlcy9ub2RlLWJ1aWx0aW5zIC0tIFRoZXNlIGFyZSBPYnNpZGlhbiByZW5kZXJlciBXZWIgQVBJcywgbm90IE5vZGUgYnVpbHRpbnMuICovXG4iXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7OztBQUFBO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFTQSx5QkFBa0M7QUFDbEMsc0JBQXVCO0FBRXZCLG1CQUF1QztBQUV2QyxNQUFNLHlCQUF5QjtBQUMvQixNQUFNLGlCQUFpQjtBQUN2QixNQUFNLGlCQUFpQjtBQUN2QixNQUFNLGtCQUFrQjtBQUN4QixNQUFNLGtCQUFrQjtBQUN4QixNQUFNLG9CQUFvQjtBQUMxQixNQUFNLFlBQVk7QUFDbEIsTUFBTSxrQkFBa0I7QUFpRGpCLE1BQU0sbUJBQW1CO0FBQUE7QUFBQTtBQUFBO0FBQUE7QUFBQTtBQUFBLEVBbUJ2QixZQUErQixRQUE2QztBQUE3QztBQUNwQyxTQUFLLFVBQVUsT0FBTztBQUN0QixVQUFNLFNBQVMsT0FBTyxVQUFVO0FBQ2hDLFFBQ0UsQ0FBQyxVQUFVLE9BQU8sTUFBTSxHQUFHLEVBQUUsS0FBSyxDQUFDLFNBQVMsQ0FBQyxRQUFRLEtBQUssV0FBVyxHQUFHLEtBQUssS0FBSyxTQUFTLElB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
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/// <reference path="../library-references.d.cts" />
|
|
2
|
+
/**
|
|
3
|
+
* @file
|
|
4
|
+
*
|
|
5
|
+
* Size-bounded Markdown transport carried by vault synchronization with default file types.
|
|
6
|
+
*/
|
|
7
|
+
import type { DataAdapter } from 'obsidian' with { 'resolution-mode': 'import' };
|
|
8
|
+
/**
|
|
9
|
+
* Parameters for the shared-store vault transport.
|
|
10
|
+
*/
|
|
11
|
+
export interface DataStoreTransportConstructorParams {
|
|
12
|
+
/**
|
|
13
|
+
* Vault adapter; no desktop filesystem APIs are used.
|
|
14
|
+
*/
|
|
15
|
+
readonly adapter: DataAdapter;
|
|
16
|
+
/**
|
|
17
|
+
* Configurable non-dot vault folder.
|
|
18
|
+
*
|
|
19
|
+
* @default `'Plugin data'`
|
|
20
|
+
*/
|
|
21
|
+
readonly folder?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Maximum UTF-8 bytes in any file, including its Markdown wrapper.
|
|
24
|
+
*
|
|
25
|
+
* @default `1000000`
|
|
26
|
+
*/
|
|
27
|
+
readonly maxFileBytes?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Plugin manifest id.
|
|
30
|
+
*/
|
|
31
|
+
readonly pluginId: string;
|
|
32
|
+
/**
|
|
33
|
+
* Store name including its consumer schema version.
|
|
34
|
+
*/
|
|
35
|
+
readonly storeId: string;
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Parameters for publishing a chunked snapshot.
|
|
39
|
+
*/
|
|
40
|
+
export interface DataStoreTransportWriteSnapshotParams {
|
|
41
|
+
/**
|
|
42
|
+
* Vault-relative base path without the `.md` extension. Use a fresh unique name for every publication.
|
|
43
|
+
*/
|
|
44
|
+
readonly base: string;
|
|
45
|
+
/**
|
|
46
|
+
* JSON-compatible snapshot, including its consumer schema and coverage metadata.
|
|
47
|
+
*/
|
|
48
|
+
readonly payload: unknown;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* A store-scoped transport. Every published document is immutable except device acknowledgements.
|
|
52
|
+
*/
|
|
53
|
+
export declare class DataStoreTransport {
|
|
54
|
+
protected readonly params: DataStoreTransportConstructorParams;
|
|
55
|
+
/**
|
|
56
|
+
* Maximum encoded bytes per document.
|
|
57
|
+
*/
|
|
58
|
+
readonly maxFileBytes: number;
|
|
59
|
+
/**
|
|
60
|
+
* Full vault-relative store root.
|
|
61
|
+
*/
|
|
62
|
+
readonly root: string;
|
|
63
|
+
/**
|
|
64
|
+
* Vault adapter used by this transport.
|
|
65
|
+
*/
|
|
66
|
+
protected readonly adapter: DataAdapter;
|
|
67
|
+
/**
|
|
68
|
+
* Creates a bounded vault transport.
|
|
69
|
+
*
|
|
70
|
+
* @param params - Adapter, identity and file bounds.
|
|
71
|
+
*/
|
|
72
|
+
constructor(params: DataStoreTransportConstructorParams);
|
|
73
|
+
/**
|
|
74
|
+
* Tests the complete encoded size without writing.
|
|
75
|
+
*
|
|
76
|
+
* @param value - JSON payload.
|
|
77
|
+
* @returns Whether its document fits the configured cap.
|
|
78
|
+
*/
|
|
79
|
+
fits(value: unknown): boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Lists the store's files recursively, never leaving its folder.
|
|
82
|
+
*
|
|
83
|
+
* @returns Vault-relative paths.
|
|
84
|
+
*/
|
|
85
|
+
list(): Promise<string[]>;
|
|
86
|
+
/**
|
|
87
|
+
* Reads a complete fenced JSON document. Partial or not-yet-delivered documents are ignored.
|
|
88
|
+
*
|
|
89
|
+
* @param path - Vault-relative file within this store.
|
|
90
|
+
* @returns Parsed JSON, or `undefined` for an incomplete document.
|
|
91
|
+
*/
|
|
92
|
+
read(path: string): Promise<unknown>;
|
|
93
|
+
/**
|
|
94
|
+
* Reads a snapshot only after every numbered chunk arrives and passes its SHA-256 check.
|
|
95
|
+
*
|
|
96
|
+
* @param path - Vault-relative manifest path.
|
|
97
|
+
* @returns Its payload, or `undefined` while delivery is incomplete.
|
|
98
|
+
*/
|
|
99
|
+
readSnapshot(path: string): Promise<unknown>;
|
|
100
|
+
/**
|
|
101
|
+
* Removes a document owned by the caller's device.
|
|
102
|
+
*
|
|
103
|
+
* @param path - Vault-relative file within this store.
|
|
104
|
+
* @returns A promise that resolves after removal.
|
|
105
|
+
*/
|
|
106
|
+
remove(path: string): Promise<void>;
|
|
107
|
+
/**
|
|
108
|
+
* Writes a complete document, refusing an oversized write before adapter mutation.
|
|
109
|
+
*
|
|
110
|
+
* @param path - Vault-relative file within this store.
|
|
111
|
+
* @param value - JSON-compatible payload.
|
|
112
|
+
* @returns A promise that resolves once the adapter finishes writing.
|
|
113
|
+
*/
|
|
114
|
+
write(path: string, value: unknown): Promise<void>;
|
|
115
|
+
/**
|
|
116
|
+
* Publishes bounded immutable chunks before their manifest. Readers verify delivery completeness.
|
|
117
|
+
*
|
|
118
|
+
* @param params - Unique base path and snapshot payload.
|
|
119
|
+
* @returns A promise that resolves when the manifest has been published.
|
|
120
|
+
*/
|
|
121
|
+
writeSnapshot(params: DataStoreTransportWriteSnapshotParams): Promise<void>;
|
|
122
|
+
private checkPath;
|
|
123
|
+
private checkSize;
|
|
124
|
+
private encode;
|
|
125
|
+
}
|