@mdbase-dev/connect 0.1.0-beta.123 → 0.1.0-beta.124
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +77 -2
- package/dist/application-session.js +9 -3
- package/dist/authority-features.d.ts +29 -0
- package/dist/authority-features.js +84 -0
- package/dist/browser/integrity.json +1 -1
- package/dist/browser/mdbase-connect.min.js +4 -4
- package/dist/browser/mdbase-connect.min.js.sha384 +1 -1
- package/dist/change-events.js +2 -9
- package/dist/collection-client.d.ts +7 -2
- package/dist/collection-client.js +73 -96
- package/dist/connection.d.ts +8 -1
- package/dist/connection.js +55 -116
- package/dist/description-cache.js +1 -28
- package/dist/file-descriptor.d.ts +2 -0
- package/dist/file-descriptor.js +17 -0
- package/dist/file-metadata.d.ts +21 -0
- package/dist/file-metadata.js +112 -0
- package/dist/files.d.ts +7 -8
- package/dist/files.js +13 -45
- package/dist/index.d.ts +2 -2
- package/dist/local-file-transport.js +18 -16
- package/dist/operation-types.d.ts +35 -15
- package/dist/query-pagination.d.ts +7 -3
- package/dist/query-pagination.js +3 -1
- package/dist/query-wire.d.ts +61 -0
- package/dist/query-wire.js +110 -0
- package/dist/read-many.d.ts +4 -3
- package/dist/read-many.js +160 -55
- package/dist/watch-policy.d.ts +22 -1
- package/dist/watch-policy.js +104 -0
- package/package.json +2 -2
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { CollectionFileDescriptor as WireFile } from "@mdbase-dev/connect-protocol";
|
|
2
|
+
export declare const FILE_ID: RegExp;
|
|
2
3
|
export interface CollectionFileDescriptor {
|
|
3
4
|
fileId: string;
|
|
4
5
|
path: string;
|
|
@@ -9,4 +10,5 @@ export interface CollectionFileDescriptor {
|
|
|
9
10
|
mediaClass: import("@mdbase-dev/connect-protocol").FileMediaClass;
|
|
10
11
|
modifiedAt: string;
|
|
11
12
|
}
|
|
13
|
+
export declare function validFileDescriptor(value: unknown): value is WireFile;
|
|
12
14
|
export declare function clientFileDescriptor(file: WireFile): CollectionFileDescriptor;
|
package/dist/file-descriptor.js
CHANGED
|
@@ -1,4 +1,21 @@
|
|
|
1
|
+
import { connectError } from "./errors.js";
|
|
2
|
+
import { SHA256_DIGEST } from "./file-transfer-internals.js";
|
|
3
|
+
// Match files.v1's UUID format (including nil), not crypto transfer-version rules.
|
|
4
|
+
export const FILE_ID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/iu;
|
|
5
|
+
export function validFileDescriptor(value) {
|
|
6
|
+
if (!value || typeof value !== "object")
|
|
7
|
+
return false;
|
|
8
|
+
const file = value;
|
|
9
|
+
return typeof file.file_id === "string" && FILE_ID.test(file.file_id)
|
|
10
|
+
&& typeof file.path === "string" && typeof file.revision === "string"
|
|
11
|
+
&& typeof file.content_digest === "string" && SHA256_DIGEST.test(file.content_digest)
|
|
12
|
+
&& Number.isSafeInteger(file.size) && file.size >= 0
|
|
13
|
+
&& ["image", "audio", "video", "pdf", "other"].includes(file.media_class)
|
|
14
|
+
&& typeof file.modified_at === "string" && (file.media_type === undefined || typeof file.media_type === "string");
|
|
15
|
+
}
|
|
1
16
|
export function clientFileDescriptor(file) {
|
|
17
|
+
if (!validFileDescriptor(file))
|
|
18
|
+
throw connectError("invalid_operation_response", "The authority returned an invalid file descriptor.");
|
|
2
19
|
return {
|
|
3
20
|
fileId: file.file_id,
|
|
4
21
|
path: file.path,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { FileCapability, ListFilesPage } from "@mdbase-dev/connect-protocol";
|
|
2
|
+
import type { ConnectRequestOptions } from "./operation-types.js";
|
|
3
|
+
import { type ConnectOutcome } from "./outcomes.js";
|
|
4
|
+
import { type CollectionFileDescriptor } from "./file-descriptor.js";
|
|
5
|
+
export interface MdbaseFileListOptions extends ConnectRequestOptions {
|
|
6
|
+
folder?: string;
|
|
7
|
+
pageSize?: number;
|
|
8
|
+
/** Called while a cold or changed collection builds its verified binary index. */
|
|
9
|
+
onIndexing?: () => void;
|
|
10
|
+
}
|
|
11
|
+
export type MdbaseFileStatTarget = {
|
|
12
|
+
path: string;
|
|
13
|
+
fileId?: never;
|
|
14
|
+
} | {
|
|
15
|
+
path?: never;
|
|
16
|
+
fileId: string;
|
|
17
|
+
};
|
|
18
|
+
export type FileControlRequest = <Result>(method: "GET" | "POST" | "DELETE", path?: string, input?: unknown, signal?: AbortSignal) => Promise<Result>;
|
|
19
|
+
export declare function filePage(request: FileControlRequest, query: URLSearchParams, signal?: AbortSignal, onIndexing?: () => void): Promise<ListFilesPage>;
|
|
20
|
+
export declare function listFiles(capability: () => FileCapability | null, request: FileControlRequest, options: MdbaseFileListOptions, timeoutMs: number | null): AsyncGenerator<CollectionFileDescriptor>;
|
|
21
|
+
export declare function statFile(capability: () => FileCapability | null, request: FileControlRequest, supports: (id: string, options?: ConnectRequestOptions) => Promise<ConnectOutcome<boolean>>, list: (options: MdbaseFileListOptions) => AsyncIterable<CollectionFileDescriptor>, target: MdbaseFileStatTarget, options: ConnectRequestOptions, timeoutMs: number | null): Promise<ConnectOutcome<CollectionFileDescriptor | null>>;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { abortableDelay } from "./async.js";
|
|
2
|
+
import { authorityCapabilities } from "./authority-features.js";
|
|
3
|
+
import { connectError, MdbaseConnectError } from "./errors.js";
|
|
4
|
+
import { normalizeFileError, throwIfAborted, validPageSize } from "./file-transfer-internals.js";
|
|
5
|
+
import { ALL_CONNECT_PROBLEM_CODES, captureConnectOutcome } from "./outcomes.js";
|
|
6
|
+
import { createRequestBudget, withRequestBudget } from "./request-budget.js";
|
|
7
|
+
import { clientFileDescriptor, FILE_ID } from "./file-descriptor.js";
|
|
8
|
+
export async function filePage(request, query, signal, onIndexing) {
|
|
9
|
+
while (true) {
|
|
10
|
+
throwIfAborted(signal);
|
|
11
|
+
try {
|
|
12
|
+
const page = await request("GET", `?${query}`, undefined, signal);
|
|
13
|
+
if (page?.protocol_version !== 1 || page.type !== "files_page" || !Array.isArray(page.files)
|
|
14
|
+
|| (page.next !== undefined && (typeof page.next !== "string" || !page.next))) {
|
|
15
|
+
throw connectError("invalid_operation_response", "The authority returned an invalid file page.");
|
|
16
|
+
}
|
|
17
|
+
authorityCapabilities(page.authority_capabilities);
|
|
18
|
+
return page;
|
|
19
|
+
}
|
|
20
|
+
catch (error) {
|
|
21
|
+
const normalized = normalizeFileError(error);
|
|
22
|
+
if (normalized.code !== "file_index_warming")
|
|
23
|
+
throw normalized;
|
|
24
|
+
onIndexing?.();
|
|
25
|
+
await abortableDelay(500, signal);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export async function* listFiles(capability, request, options, timeoutMs) {
|
|
30
|
+
const budget = createRequestBudget(options, timeoutMs);
|
|
31
|
+
try {
|
|
32
|
+
requireList(capability());
|
|
33
|
+
let after;
|
|
34
|
+
do {
|
|
35
|
+
const query = new URLSearchParams({
|
|
36
|
+
protocol_version: "1",
|
|
37
|
+
...(options.folder ? { folder: options.folder } : {}),
|
|
38
|
+
...(after ? { after } : {}),
|
|
39
|
+
...(options.pageSize ? { limit: String(validPageSize(options.pageSize)) } : {})
|
|
40
|
+
});
|
|
41
|
+
const page = await filePage(request, query, budget.signal, options.onIndexing);
|
|
42
|
+
for (const file of page.files) {
|
|
43
|
+
throwIfAborted(budget.signal);
|
|
44
|
+
yield clientFileDescriptor(file);
|
|
45
|
+
}
|
|
46
|
+
after = page.next;
|
|
47
|
+
} while (after);
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
budget.dispose();
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export function statFile(capability, request, supports, list, target, options, timeoutMs) {
|
|
54
|
+
return captureConnectOutcome(() => withRequestBudget(options, timeoutMs, async (budget) => {
|
|
55
|
+
requireList(capability());
|
|
56
|
+
if (!target || typeof target !== "object" || Object.keys(target).length !== 1
|
|
57
|
+
|| !(Object.hasOwn(target, "path") && typeof target.path === "string"
|
|
58
|
+
|| Object.hasOwn(target, "fileId") && typeof target.fileId === "string" && FILE_ID.test(target.fileId))) {
|
|
59
|
+
throw connectError("invalid_request", "File stat requires exactly one path or UUID fileId.");
|
|
60
|
+
}
|
|
61
|
+
// Collection-relative wire syntax only. Eligibility, exclusion rules and
|
|
62
|
+
// filesystem access remain authority-owned, including during legacy listing.
|
|
63
|
+
if (target.path !== undefined && (target.path.length > 1024 || target.path.includes("\\")
|
|
64
|
+
|| target.path.split("/").some(part => !part || part.startsWith(".")))) {
|
|
65
|
+
throw connectError("invalid_request", "File stat requires a collection-relative file path.");
|
|
66
|
+
}
|
|
67
|
+
const supported = await supports("files-stat-v1", { signal: budget.signal, timeoutMs: null });
|
|
68
|
+
if (!supported.ok)
|
|
69
|
+
throw new MdbaseConnectError(supported.problem);
|
|
70
|
+
throwIfAborted(budget.signal);
|
|
71
|
+
if (supported.value) {
|
|
72
|
+
const result = await request("POST", "stat", {
|
|
73
|
+
protocol_version: 1, type: "stat_file",
|
|
74
|
+
...(target.path !== undefined ? { path: target.path } : { file_id: target.fileId })
|
|
75
|
+
}, budget.signal);
|
|
76
|
+
if (result?.protocol_version !== 1 || result.type !== "file_stat" || result.file === undefined) {
|
|
77
|
+
throw connectError("invalid_operation_response", "The authority returned an invalid file stat.");
|
|
78
|
+
}
|
|
79
|
+
if (result.file === null)
|
|
80
|
+
return null;
|
|
81
|
+
const file = clientFileDescriptor(result.file);
|
|
82
|
+
if (target.path !== undefined ? portableKey(file.path) !== portableKey(target.path)
|
|
83
|
+
: file.fileId.toLowerCase() !== target.fileId.toLowerCase()) {
|
|
84
|
+
throw connectError("invalid_operation_response", "The authority returned a stat for a different file.");
|
|
85
|
+
}
|
|
86
|
+
return file;
|
|
87
|
+
}
|
|
88
|
+
// Consumers: Writer/Reader/editor/TaskNotes against late-updated authorities.
|
|
89
|
+
// Remove once the minimum files-stat authority, all consumer pins, and N-1
|
|
90
|
+
// rollback/connection-cache windows close. Listing is not point-cost or atomic.
|
|
91
|
+
const folder = target.path?.includes("/") ? target.path.slice(0, target.path.lastIndexOf("/")) : undefined;
|
|
92
|
+
for await (const file of list({ folder, signal: budget.signal, timeoutMs: null })) {
|
|
93
|
+
throwIfAborted(budget.signal);
|
|
94
|
+
if (target.path !== undefined
|
|
95
|
+
? portableKey(file.path) === portableKey(target.path)
|
|
96
|
+
: file.fileId.toLowerCase() === target.fileId.toLowerCase())
|
|
97
|
+
return file;
|
|
98
|
+
}
|
|
99
|
+
throwIfAborted(budget.signal);
|
|
100
|
+
return null;
|
|
101
|
+
}), ALL_CONNECT_PROBLEM_CODES);
|
|
102
|
+
}
|
|
103
|
+
function requireList(capability) {
|
|
104
|
+
if (!capability?.actions.includes("list"))
|
|
105
|
+
throw connectError("not_authorized", "This connection is not authorized to list files.");
|
|
106
|
+
}
|
|
107
|
+
// File protocol portable identity comparison, not collection eligibility validation.
|
|
108
|
+
function portableKey(path) {
|
|
109
|
+
// Match Connect's NFC + per-Unicode-scalar lowercase + NFC key, not JS's
|
|
110
|
+
// context-sensitive whole-string lowercase (e.g. Greek final sigma).
|
|
111
|
+
return Array.from(path.normalize("NFC"), scalar => scalar.toLowerCase()).join("").normalize("NFC");
|
|
112
|
+
}
|
package/dist/files.d.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { type CollectionFileDescriptor } from "./file-descriptor.js";
|
|
2
2
|
export type { CollectionFileDescriptor } from "./file-descriptor.js";
|
|
3
3
|
import type { FileCapability, FileTransferSession } from "@mdbase-dev/connect-protocol";
|
|
4
|
+
import { type FileControlRequest, type MdbaseFileListOptions, type MdbaseFileStatTarget } from "./file-metadata.js";
|
|
5
|
+
export type { MdbaseFileListOptions, MdbaseFileStatTarget } from "./file-metadata.js";
|
|
6
|
+
import { type ConnectOutcome } from "./outcomes.js";
|
|
4
7
|
import { type FileSource } from "./file-transfer-internals.js";
|
|
5
8
|
import type { ConnectRequestOptions } from "./operation-types.js";
|
|
6
9
|
import { type ResolvedConnectTimeouts } from "./request-budget.js";
|
|
@@ -30,12 +33,6 @@ export interface MdbaseFileProgress {
|
|
|
30
33
|
transferredBytes: number;
|
|
31
34
|
totalBytes: number;
|
|
32
35
|
}
|
|
33
|
-
export interface MdbaseFileListOptions extends ConnectRequestOptions {
|
|
34
|
-
folder?: string;
|
|
35
|
-
pageSize?: number;
|
|
36
|
-
/** Called while a cold or changed collection builds its verified binary index. */
|
|
37
|
-
onIndexing?: () => void;
|
|
38
|
-
}
|
|
39
36
|
export interface MdbaseFileUploadOptions extends ConnectRequestOptions {
|
|
40
37
|
mediaType?: string;
|
|
41
38
|
ifRevision?: string;
|
|
@@ -68,7 +65,6 @@ export interface MdbaseFramedFileTransport {
|
|
|
68
65
|
export interface MdbaseHostedFileTransport {
|
|
69
66
|
downloadPart(session: FileTransferSession, partIndex: number, expectedLength: number, signal?: AbortSignal): Promise<ReadableStream<Uint8Array>>;
|
|
70
67
|
}
|
|
71
|
-
type ControlRequest = <Result>(method: "GET" | "POST" | "DELETE", path?: string, input?: unknown, signal?: AbortSignal) => Promise<Result>;
|
|
72
68
|
/** Ergonomic facade over resumable, authority-specific file delivery. */
|
|
73
69
|
export declare class MdbaseFileClient {
|
|
74
70
|
private readonly capability;
|
|
@@ -76,8 +72,11 @@ export declare class MdbaseFileClient {
|
|
|
76
72
|
private readonly framed?;
|
|
77
73
|
private readonly hosted?;
|
|
78
74
|
private readonly timeouts;
|
|
79
|
-
|
|
75
|
+
private readonly supportsAuthorityFeature;
|
|
76
|
+
constructor(capability: () => FileCapability | null, request: FileControlRequest, framed?: MdbaseFramedFileTransport | undefined, hosted?: MdbaseHostedFileTransport | undefined, timeouts?: ResolvedConnectTimeouts, supportsAuthorityFeature?: (id: string, options?: ConnectRequestOptions) => Promise<ConnectOutcome<boolean>>);
|
|
80
77
|
list(options?: MdbaseFileListOptions): AsyncGenerator<CollectionFileDescriptor>;
|
|
78
|
+
/** Current metadata lookup; requires file list approval, not file byte access. */
|
|
79
|
+
stat(target: MdbaseFileStatTarget, options?: ConnectRequestOptions): Promise<ConnectOutcome<CollectionFileDescriptor | null>>;
|
|
81
80
|
upload(path: string, source: MdbaseFileSource, options?: MdbaseFileUploadOptions): Promise<CollectionFileDescriptor>;
|
|
82
81
|
private uploadWithinBudget;
|
|
83
82
|
/**
|
package/dist/files.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { clientFileDescriptor } from "./file-descriptor.js";
|
|
2
2
|
import { FILE_PROTOCOL_VERSION } from "@mdbase-dev/connect-protocol";
|
|
3
|
-
import {
|
|
3
|
+
import { listFiles, statFile } from "./file-metadata.js";
|
|
4
|
+
import { connectSuccess } from "./outcomes.js";
|
|
4
5
|
import { MdbaseConnectError, connectError } from "./errors.js";
|
|
5
6
|
import { IncrementalSha256 } from "./file-sha256.js";
|
|
6
7
|
import { BinaryPartReader } from "./file-stream-source.js";
|
|
7
|
-
import { MAX_BUFFERED_DOWNLOAD_BYTES, MAX_OBJECT_ATTEMPTS, SHA256_DIGEST, UUID, browserObjectHeaders, chunkLength, hashBlob, mapConcurrent, normalizeFileError, requirePreparedPart, requireTransferSession, requireTransferStatus, retryChunk, sourceBlob, throwIfAborted, uploadBodyBytes, uploadBodyLength, validConcurrency
|
|
8
|
-
import {
|
|
8
|
+
import { MAX_BUFFERED_DOWNLOAD_BYTES, MAX_OBJECT_ATTEMPTS, SHA256_DIGEST, UUID, browserObjectHeaders, chunkLength, hashBlob, mapConcurrent, normalizeFileError, requirePreparedPart, requireTransferSession, requireTransferStatus, retryChunk, sourceBlob, throwIfAborted, uploadBodyBytes, uploadBodyLength, validConcurrency } from "./file-transfer-internals.js";
|
|
9
|
+
import { resolveConnectTimeouts, withCooperativeRequestBudget } from "./request-budget.js";
|
|
9
10
|
/** Ergonomic facade over resumable, authority-specific file delivery. */
|
|
10
11
|
export class MdbaseFileClient {
|
|
11
12
|
capability;
|
|
@@ -13,54 +14,21 @@ export class MdbaseFileClient {
|
|
|
13
14
|
framed;
|
|
14
15
|
hosted;
|
|
15
16
|
timeouts;
|
|
16
|
-
|
|
17
|
+
supportsAuthorityFeature;
|
|
18
|
+
constructor(capability, request, framed, hosted, timeouts = resolveConnectTimeouts(), supportsAuthorityFeature = async () => connectSuccess(false)) {
|
|
17
19
|
this.capability = capability;
|
|
18
20
|
this.request = request;
|
|
19
21
|
this.framed = framed;
|
|
20
22
|
this.hosted = hosted;
|
|
21
23
|
this.timeouts = timeouts;
|
|
24
|
+
this.supportsAuthorityFeature = supportsAuthorityFeature;
|
|
22
25
|
}
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
do {
|
|
30
|
-
throwIfAborted(signal);
|
|
31
|
-
const query = new URLSearchParams({
|
|
32
|
-
protocol_version: String(FILE_PROTOCOL_VERSION),
|
|
33
|
-
...(options.folder ? { folder: options.folder } : {}),
|
|
34
|
-
...(after ? { after } : {}),
|
|
35
|
-
...(options.pageSize ? { limit: String(validPageSize(options.pageSize)) } : {})
|
|
36
|
-
});
|
|
37
|
-
let page;
|
|
38
|
-
while (true) {
|
|
39
|
-
try {
|
|
40
|
-
page = await this.request("GET", `?${query.toString()}`, undefined, signal);
|
|
41
|
-
break;
|
|
42
|
-
}
|
|
43
|
-
catch (error) {
|
|
44
|
-
const normalized = normalizeFileError(error);
|
|
45
|
-
if (normalized.code !== "file_index_warming")
|
|
46
|
-
throw normalized;
|
|
47
|
-
options.onIndexing?.();
|
|
48
|
-
await abortableDelay(500, signal);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
if (page.protocol_version !== 1 || page.type !== "files_page" || !Array.isArray(page.files)) {
|
|
52
|
-
throw connectError("invalid_operation_response", "The authority returned an invalid file page.");
|
|
53
|
-
}
|
|
54
|
-
for (const file of page.files) {
|
|
55
|
-
throwIfAborted(signal);
|
|
56
|
-
yield clientFileDescriptor(file);
|
|
57
|
-
}
|
|
58
|
-
after = page.next;
|
|
59
|
-
} while (after);
|
|
60
|
-
}
|
|
61
|
-
finally {
|
|
62
|
-
budget.dispose();
|
|
63
|
-
}
|
|
26
|
+
list(options = {}) {
|
|
27
|
+
return listFiles(this.capability, this.request, options, this.timeouts.fileIndexMs);
|
|
28
|
+
}
|
|
29
|
+
/** Current metadata lookup; requires file list approval, not file byte access. */
|
|
30
|
+
stat(target, options = {}) {
|
|
31
|
+
return statFile(this.capability, this.request, this.supportsAuthorityFeature, options => this.list(options), target, options, this.timeouts.fileIndexMs);
|
|
64
32
|
}
|
|
65
33
|
async upload(path, source, options = {}) {
|
|
66
34
|
return withCooperativeRequestBudget(options, this.timeouts.uploadMs, (budget) => this.uploadWithinBudget(path, source, { ...options, signal: budget.signal, timeoutMs: null }));
|
package/dist/index.d.ts
CHANGED
|
@@ -19,11 +19,11 @@ export type { MdbaseRecordResolution, MdbaseRecordSessionOptions, MdbaseRecordSe
|
|
|
19
19
|
export { MdbasePeopleClient, PERSON_CONTRACT, sameIdentity } from "./people-client.js";
|
|
20
20
|
export type { CurrentAccount, InvalidPersonRecord, PeopleDirectory, PeopleDirectoryOptions, PersonRecord, PersonResolution } from "./people-client.js";
|
|
21
21
|
export type { AccountIdentity, AccountProfile, CollectionMemberProfile, ApplicationPeopleRequirement, PeoplePermission } from "@mdbase-dev/connect-protocol";
|
|
22
|
-
export type { MdbaseFileSource, CollectionFileDescriptor, MdbaseFileDeleteReceipt, MdbaseFileStreamSource, MdbaseFileProgress, MdbaseFileListOptions, MdbaseFileUploadOptions, MdbaseFileStreamUploadOptions, MdbaseFileDownloadOptions, MdbaseFileMoveOptions, MdbaseFileDeleteOptions } from "./files.js";
|
|
22
|
+
export type { MdbaseFileSource, CollectionFileDescriptor, MdbaseFileDeleteReceipt, MdbaseFileStreamSource, MdbaseFileProgress, MdbaseFileListOptions, MdbaseFileStatTarget, MdbaseFileUploadOptions, MdbaseFileStreamUploadOptions, MdbaseFileDownloadOptions, MdbaseFileMoveOptions, MdbaseFileDeleteOptions } from "./files.js";
|
|
23
23
|
export { parseMdbasePushPayload, parseMdbaseNativeNotificationData, showMdbasePushNotification } from "./notifications.js";
|
|
24
24
|
export type { MdbaseNotificationRegistrationOptions, MdbaseNotificationRegistration, MdbaseNativeNotificationRegistrationOptions, MdbaseNativeNotificationRegistration, MdbaseNativeNotificationData, MdbasePushPayload } from "./notifications.js";
|
|
25
25
|
export type { ConnectSuccess, ConnectFailure, ConnectOutcome, CommonOperationProblemCode, RegistrationProblemCode, AuthorizationProblemCode, NotificationProblemCode, DirectAccessProblemCode, SessionProblemCode, CollectionSetupProblemCode, CollectionDescriptionProblemCode, CollectionReadProblemCode, CollectionQueryProblemCode, CollectionMutationProblemCode, CollectionChangesProblemCode, CollectionTypeProblemCode } from "./outcomes.js";
|
|
26
|
-
export type { MdbaseDesiredTimer, MdbaseTimer, MdbaseTimerList, MdbaseTimerReconciliation, ReadInput, ReadManyOptions, ReadManyRecord, ReadManyEntry, ReadManyBatchError, ReadManyResult, DataContractSelector, CollectionFileMetadata, DataContractViewIdentity, QueryRecord, RecordDocument, QueryInput, QueryProjection, QuerySelectionExpression, QueryOrder, QuerySummary, QueryResult, QueryPagesOptions, QueryAllOptions, QueryPage, ConnectRequestOptions, MutationEstimate, MutationProgressState, MutationProgress, MutationProgressOptions, RequestCoordinationOptions, RenameProgressOptions, DeleteProgressOptions, PendingMutationSummary, PendingMutation, CreateInput, UpdateInput, DeleteInput, DeleteResult, DeletePreflightResult, RenameInput, RenameResult, RenamePreflightResult, ReadTypeInput, CreateTypeInput, UpdateTypeInput, ReadViewSourceInput, CreateViewSourceInput, UpdateViewSourceInput, DeleteViewSourceInput, DeleteViewSourceResult, ExecuteViewInput, SavedViewPresentation, SavedViewSource, SavedViewProperty, SavedNamedView, SavedViewDocument, SavedViewList, SavedViewSourceDocument, SavedViewExecution, SavedViewPage, SavedViewPagesOptions, ContractSetupChoice, TypePackResourceDiff, TypePackReceipt, TypePackAssessment, TypePackApplyResult, ConfigurationContributionValue, ConfigurationRequirement, ConfigurationProvision, ApplicationCollectionSetupRequirements, ApplicationCollectionSetupProvisions, ConfigurationSetupConflict, ConfigurationSetupAssessment, CollectionSetupAssessment, CollectionSetupReceipt, CollectionSetupApplyResult, AssessTypePackInput, ApplyTypePackInput, AssessCollectionSetupInput, ApplyCollectionSetupInput, ChangesInput, WatchOptions, WatchRetryOptions, WatchStatus, WatchInput, MdbaseWatchSubscription, CollectionChange, RecordChangeMetadata, DescribeOptions, CollectionChangesPage, CollectionDescription, CollectionContractDescriptor, CollectionContractImplementationDescriptor, CollectionTypeDescriptor } from "./operation-types.js";
|
|
26
|
+
export type { MdbaseDesiredTimer, MdbaseTimer, MdbaseTimerList, MdbaseTimerReconciliation, ReadInput, ReadManyOptions, ReadManyRecord, ReadManyEntry, ReadManyBatchError, ReadManyResult, DataContractSelector, CollectionFileMetadata, DataContractViewIdentity, QueryRecord, RecordDocument, QueryInput, QueryMetadataInput, QueryMetadataRecord, QueryMetadataResult, QueryMetadataPage, QueryProjection, QuerySelectionExpression, QueryOrder, QuerySummary, QueryResult, QueryPagesOptions, QueryAllOptions, QueryPage, ConnectRequestOptions, MutationEstimate, MutationProgressState, MutationProgress, MutationProgressOptions, RequestCoordinationOptions, RenameProgressOptions, DeleteProgressOptions, PendingMutationSummary, PendingMutation, CreateInput, UpdateInput, DeleteInput, DeleteResult, DeletePreflightResult, RenameInput, RenameResult, RenamePreflightResult, ReadTypeInput, CreateTypeInput, UpdateTypeInput, ReadViewSourceInput, CreateViewSourceInput, UpdateViewSourceInput, DeleteViewSourceInput, DeleteViewSourceResult, ExecuteViewInput, SavedViewPresentation, SavedViewSource, SavedViewProperty, SavedNamedView, SavedViewDocument, SavedViewList, SavedViewSourceDocument, SavedViewExecution, SavedViewPage, SavedViewPagesOptions, ContractSetupChoice, TypePackResourceDiff, TypePackReceipt, TypePackAssessment, TypePackApplyResult, ConfigurationContributionValue, ConfigurationRequirement, ConfigurationProvision, ApplicationCollectionSetupRequirements, ApplicationCollectionSetupProvisions, ConfigurationSetupConflict, ConfigurationSetupAssessment, CollectionSetupAssessment, CollectionSetupReceipt, CollectionSetupApplyResult, AssessTypePackInput, ApplyTypePackInput, AssessCollectionSetupInput, ApplyCollectionSetupInput, ChangesInput, WatchOptions, WatchRetryOptions, WatchStatus, WatchInput, MdbaseWatchSubscription, CollectionChange, RecordChangeMetadata, DescribeOptions, CollectionChangesPage, CollectionDescription, CollectionContractDescriptor, CollectionContractImplementationDescriptor, CollectionTypeDescriptor } from "./operation-types.js";
|
|
27
27
|
export { linksTo } from "./query-predicates.js";
|
|
28
28
|
export type { LinksToOptions } from "./query-predicates.js";
|
|
29
29
|
export { externalStore } from "./external-store.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { decodeFileFrame, FILE_PROTOCOL_VERSION, FILE_TRANSFER_PROTOCOL_VERSION } from "@mdbase-dev/connect-protocol";
|
|
1
|
+
import { decodeFileFrame, isMutatingOperation, FILE_PROTOCOL_VERSION, FILE_TRANSFER_PROTOCOL_VERSION } from "@mdbase-dev/connect-protocol";
|
|
2
2
|
import { decryptRelayResponse, encryptRelayRequest } from "./crypto.js";
|
|
3
3
|
import { MdbaseConnectError, connectError, serverConnectError } from "./errors.js";
|
|
4
4
|
import { GrantFileTransferCipher } from "./file-crypto.js";
|
|
@@ -51,7 +51,7 @@ export class LocalFileTransport {
|
|
|
51
51
|
if (response)
|
|
52
52
|
this.options.onDirectUnavailable();
|
|
53
53
|
}
|
|
54
|
-
if (triedDirect &&
|
|
54
|
+
if (triedDirect && !isMutatingOperation("file_control", controlInput)) {
|
|
55
55
|
token = token.expiresAt > Date.now() + 30_000
|
|
56
56
|
? token
|
|
57
57
|
: await this.options.refreshAuthorization(signal);
|
|
@@ -81,7 +81,7 @@ export class LocalFileTransport {
|
|
|
81
81
|
return this.controlAttempt(token, method, path, input, signal, freshReadRetried, connectorBusyRetries + 1, leases);
|
|
82
82
|
}
|
|
83
83
|
if (error.code === "fresh_request_required"
|
|
84
|
-
&& method
|
|
84
|
+
&& !isMutatingOperation("file_control", localFileControlInput(method, path, input))
|
|
85
85
|
&& !freshReadRetried) {
|
|
86
86
|
return this.controlAttempt(token, method, path, input, signal, true, connectorBusyRetries, leases);
|
|
87
87
|
}
|
|
@@ -94,7 +94,7 @@ export class LocalFileTransport {
|
|
|
94
94
|
const decrypted = await decryptRelayResponse(this.options.keyStore, token.keyHandle, relayBinding(token), encryptedRequest, encryptedResponse);
|
|
95
95
|
if (!decrypted.ok
|
|
96
96
|
&& decrypted.problem.code === "fresh_request_required"
|
|
97
|
-
&& method
|
|
97
|
+
&& !isMutatingOperation("file_control", localFileControlInput(method, path, input))
|
|
98
98
|
&& !freshReadRetried) {
|
|
99
99
|
return this.controlAttempt(token, method, path, input, signal, true, connectorBusyRetries, leases);
|
|
100
100
|
}
|
|
@@ -407,19 +407,21 @@ function localFileControlInput(method, path, input) {
|
|
|
407
407
|
return transferControl("abort_file_transfer", segments[1]);
|
|
408
408
|
}
|
|
409
409
|
if (method === "POST" && isRecord(input)) {
|
|
410
|
-
const expectedType = path === "
|
|
411
|
-
? "
|
|
412
|
-
: path === "
|
|
413
|
-
? "
|
|
414
|
-
:
|
|
415
|
-
? "
|
|
416
|
-
: segments.length ===
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
: segments.length === 2 && segments[1] === "delete"
|
|
410
|
+
const expectedType = path === "stat"
|
|
411
|
+
? "stat_file"
|
|
412
|
+
: path === "uploads"
|
|
413
|
+
? "open_file_upload"
|
|
414
|
+
: path === "downloads"
|
|
415
|
+
? "open_file_download"
|
|
416
|
+
: segments.length === 3 && segments[0] === "uploads" && segments[2] === "commit"
|
|
417
|
+
? "commit_file_upload"
|
|
418
|
+
: segments.length === 2 && segments[1] === "move"
|
|
420
419
|
&& input.file_id === decodeURIComponent(segments[0])
|
|
421
|
-
? "
|
|
422
|
-
:
|
|
420
|
+
? "move_file"
|
|
421
|
+
: segments.length === 2 && segments[1] === "delete"
|
|
422
|
+
&& input.file_id === decodeURIComponent(segments[0])
|
|
423
|
+
? "delete_file"
|
|
424
|
+
: null;
|
|
423
425
|
if (expectedType && input.type === expectedType)
|
|
424
426
|
return { ...input };
|
|
425
427
|
}
|
|
@@ -53,6 +53,8 @@ export interface DataContractViewIdentity {
|
|
|
53
53
|
}
|
|
54
54
|
export interface QueryRecord<Frontmatter extends JsonObject = JsonObject> {
|
|
55
55
|
path: string;
|
|
56
|
+
/** Exact-source token when the authority supports query-record-revisions-v1. */
|
|
57
|
+
revision?: string;
|
|
56
58
|
frontmatter?: Frontmatter;
|
|
57
59
|
effectiveFrontmatter?: Frontmatter;
|
|
58
60
|
body?: string;
|
|
@@ -64,17 +66,14 @@ export interface QueryRecord<Frontmatter extends JsonObject = JsonObject> {
|
|
|
64
66
|
values?: JsonObject;
|
|
65
67
|
contract?: DataContractViewIdentity;
|
|
66
68
|
}
|
|
67
|
-
/** Query-shaped
|
|
68
|
-
export
|
|
69
|
-
/** Absent on current query authorities. Never synthesized from timestamps or content. */
|
|
70
|
-
revision?: string;
|
|
71
|
-
}
|
|
69
|
+
/** Query-shaped projection; qualified document batches supply exact-source revisions. */
|
|
70
|
+
export type ReadManyRecord<Frontmatter extends JsonObject = JsonObject> = QueryRecord<Frontmatter>;
|
|
72
71
|
export interface ReadManyOptions extends ConnectRequestOptions {
|
|
73
72
|
includeBody?: boolean;
|
|
74
73
|
frontmatterMode?: QueryInput["frontmatterMode"];
|
|
75
74
|
/** Restrict matching records to these raw collection types, not a contract view. */
|
|
76
75
|
types?: string[];
|
|
77
|
-
/** Unique paths per
|
|
76
|
+
/** Unique paths per batch, default 100; range 1..1,000. Document authorities cap at 100. */
|
|
78
77
|
batchSize?: number;
|
|
79
78
|
/** Independent batches in flight, default 4; range 1..4. Cursor pages stay serial. */
|
|
80
79
|
concurrency?: number;
|
|
@@ -99,7 +98,7 @@ export interface ReadManyBatchError {
|
|
|
99
98
|
export interface ReadManyResult<Frontmatter extends JsonObject = JsonObject> {
|
|
100
99
|
/** One entry per input path, in input order, including duplicates. */
|
|
101
100
|
results: ReadManyEntry<Frontmatter>[];
|
|
102
|
-
/**
|
|
101
|
+
/** Batch failures cover the entire batch; semantic item failures cover only their paths. */
|
|
103
102
|
errors: ReadManyBatchError[];
|
|
104
103
|
}
|
|
105
104
|
export interface RecordDocument<Frontmatter extends JsonObject = JsonObject> {
|
|
@@ -145,6 +144,8 @@ export interface CollectionContractDescriptor {
|
|
|
145
144
|
implementations: CollectionContractImplementationDescriptor[];
|
|
146
145
|
}
|
|
147
146
|
export interface CollectionDescription {
|
|
147
|
+
/** Immutable authority implementation features; never a permission claim. */
|
|
148
|
+
authorityCapabilities?: readonly string[];
|
|
148
149
|
protocolVersion: 1;
|
|
149
150
|
collectionId: string;
|
|
150
151
|
displayName: string;
|
|
@@ -177,6 +178,7 @@ export interface QuerySummary {
|
|
|
177
178
|
}
|
|
178
179
|
/** Application-facing form of the canonical mdbase v0.3 query schema. */
|
|
179
180
|
export interface QueryInput {
|
|
181
|
+
output?: never;
|
|
180
182
|
/**
|
|
181
183
|
* Queries using a semantic contract view accept only `types`, `timezone`,
|
|
182
184
|
* pagination, `frontmatterMode`, and `contract`; filter normalized fields in
|
|
@@ -210,8 +212,26 @@ export interface QueryInput {
|
|
|
210
212
|
/** Select one exact semantic contract/provider view for this query. */
|
|
211
213
|
contract?: DataContractSelector;
|
|
212
214
|
}
|
|
213
|
-
export
|
|
214
|
-
|
|
215
|
+
export type QueryMetadataInput = Omit<QueryInput, "output" | "includeBody"> & {
|
|
216
|
+
output: "metadata";
|
|
217
|
+
includeBody?: false;
|
|
218
|
+
};
|
|
219
|
+
/** Partial metadata is not a record document; revision and selected values are required. */
|
|
220
|
+
export interface QueryMetadataRecord {
|
|
221
|
+
path: string;
|
|
222
|
+
types: string[];
|
|
223
|
+
revision: string;
|
|
224
|
+
values: JsonObject;
|
|
225
|
+
contract?: DataContractViewIdentity;
|
|
226
|
+
}
|
|
227
|
+
export type QueryMetadataResult = QueryResult<JsonObject, QueryMetadataRecord> & {
|
|
228
|
+
output: "metadata";
|
|
229
|
+
};
|
|
230
|
+
export type QueryMetadataPage = QueryPage<JsonObject, QueryMetadataRecord> & {
|
|
231
|
+
output: "metadata";
|
|
232
|
+
};
|
|
233
|
+
export interface QueryResult<Record extends JsonObject = JsonObject, Row = QueryRecord<Record>> {
|
|
234
|
+
results: Row[];
|
|
215
235
|
meta?: {
|
|
216
236
|
/** Exact total when it is available within the page summary budget. */
|
|
217
237
|
totalCount?: number;
|
|
@@ -226,7 +246,7 @@ export interface QueryResult<Record extends JsonObject = JsonObject> {
|
|
|
226
246
|
snapshot?: string;
|
|
227
247
|
};
|
|
228
248
|
}
|
|
229
|
-
export interface QueryPagesOptions<Record extends JsonObject = JsonObject
|
|
249
|
+
export interface QueryPagesOptions<Record extends JsonObject = JsonObject, Row = QueryRecord<Record>> {
|
|
230
250
|
/** Initial page size. Cursor continuations use this pinned size. */
|
|
231
251
|
firstPageSize?: number;
|
|
232
252
|
/** Initial cursor size (default 200), unless firstPageSize is set. Offset continuations use this size (default 1,000). Authorities may cap it. */
|
|
@@ -237,19 +257,19 @@ export interface QueryPagesOptions<Record extends JsonObject = JsonObject> {
|
|
|
237
257
|
/** Independent budget for each page requested by this caller-driven iterator. */
|
|
238
258
|
pageTimeoutMs?: number | null;
|
|
239
259
|
coordination?: RequestCoordinationOptions;
|
|
240
|
-
onProgress?: (page: QueryPage<Record>) => void;
|
|
260
|
+
onProgress?: (page: QueryPage<Record, Row>) => void;
|
|
241
261
|
}
|
|
242
|
-
export interface QueryAllOptions<Record extends JsonObject = JsonObject
|
|
262
|
+
export interface QueryAllOptions<Record extends JsonObject = JsonObject, Row = QueryRecord<Record>> extends ConnectRequestOptions {
|
|
243
263
|
/** Initial page size. Cursor continuations use this pinned size. */
|
|
244
264
|
firstPageSize?: number;
|
|
245
265
|
/** Initial cursor size (default 200), unless firstPageSize is set. Offset continuations use this size (default 1,000). Authorities may cap it. */
|
|
246
266
|
pageSize?: number;
|
|
247
267
|
/** Total rows to return, independent of page size. */
|
|
248
268
|
maxResults?: number;
|
|
249
|
-
onProgress?: (page: QueryPage<Record>) => void;
|
|
269
|
+
onProgress?: (page: QueryPage<Record, Row>) => void;
|
|
250
270
|
}
|
|
251
|
-
export interface QueryPage<Record extends JsonObject = JsonObject
|
|
252
|
-
results: QueryResult<Record>["results"];
|
|
271
|
+
export interface QueryPage<Record extends JsonObject = JsonObject, Row = QueryRecord<Record>> {
|
|
272
|
+
results: QueryResult<Record, Row>["results"];
|
|
253
273
|
meta?: QueryResult<Record>["meta"];
|
|
254
274
|
page: number;
|
|
255
275
|
offset: number;
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import type { JsonObject } from "@mdbase-dev/connect-protocol";
|
|
2
2
|
import { type CollectionQueryProblemCode, type ConnectOutcome } from "./outcomes.js";
|
|
3
|
-
import type { ConnectRequestOptions, QueryInput, QueryPage, QueryPagesOptions, QueryResult } from "./operation-types.js";
|
|
4
|
-
type QueryOperation<Frontmatter extends JsonObject> = (input: QueryInput, options?: ConnectRequestOptions) => Promise<ConnectOutcome<QueryResult<Frontmatter
|
|
5
|
-
|
|
3
|
+
import type { ConnectRequestOptions, QueryInput, QueryMetadataInput, QueryRecord, QueryPage, QueryPagesOptions, QueryResult } from "./operation-types.js";
|
|
4
|
+
type QueryOperation<Frontmatter extends JsonObject, Row> = (input: QueryInput | QueryMetadataInput, options?: ConnectRequestOptions) => Promise<ConnectOutcome<QueryResult<Frontmatter, Row> & {
|
|
5
|
+
output?: "metadata";
|
|
6
|
+
}, CollectionQueryProblemCode>>;
|
|
7
|
+
export declare function coordinatedQueryPages<Frontmatter extends JsonObject, Row = QueryRecord<Frontmatter>>(query: QueryOperation<Frontmatter, Row>, releaseQueryCursor: (cursor: string) => Promise<void>, input?: QueryInput | QueryMetadataInput, options?: QueryPagesOptions<Frontmatter, Row>): AsyncGenerator<ConnectOutcome<QueryPage<Frontmatter, Row> & {
|
|
8
|
+
output?: "metadata";
|
|
9
|
+
}, CollectionQueryProblemCode>>;
|
|
6
10
|
export {};
|
package/dist/query-pagination.js
CHANGED
|
@@ -25,7 +25,8 @@ export async function* coordinatedQueryPages(query, releaseQueryCursor, input =
|
|
|
25
25
|
const automaticCursorProbe = pageNumber === 0
|
|
26
26
|
&& requestedCursor === undefined
|
|
27
27
|
&& requestedPagination === undefined
|
|
28
|
-
&& requestedSnapshot === undefined
|
|
28
|
+
&& requestedSnapshot === undefined
|
|
29
|
+
&& input.output !== "metadata";
|
|
29
30
|
let queried = await query({
|
|
30
31
|
...criteria,
|
|
31
32
|
// Initial size is portable across authorities; continuations use the
|
|
@@ -96,6 +97,7 @@ export async function* coordinatedQueryPages(query, releaseQueryCursor, input =
|
|
|
96
97
|
loaded += results.length;
|
|
97
98
|
const complete = loaded >= maxResults || !result.meta?.hasMore || result.results.length === 0;
|
|
98
99
|
const page = {
|
|
100
|
+
...(result.output ? { output: result.output } : {}),
|
|
99
101
|
results,
|
|
100
102
|
...(result.meta ? { meta: result.meta } : {}),
|
|
101
103
|
page: pageNumber,
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { CollectionDescription as WireDescription, DataContractViewIdentity as WireIdentity, JsonObject, QueryRecord as WireQueryRecord } from "@mdbase-dev/connect-protocol";
|
|
2
|
+
import type { CollectionDescription, DataContractViewIdentity, QueryInput, QueryMetadataInput, QueryMetadataResult, QueryRecord, QueryResult } from "./operation-types.js";
|
|
3
|
+
export interface WireQueryResult<Frontmatter extends JsonObject> {
|
|
4
|
+
output?: "metadata";
|
|
5
|
+
results: Array<WireQueryRecord<Frontmatter> | import("@mdbase-dev/connect-protocol").QueryMetadataRecord>;
|
|
6
|
+
meta?: {
|
|
7
|
+
total_count?: number | null;
|
|
8
|
+
total_count_outcome?: {
|
|
9
|
+
status: "deferred";
|
|
10
|
+
budget: "eager_summary_rows";
|
|
11
|
+
limit: number;
|
|
12
|
+
};
|
|
13
|
+
has_more: boolean;
|
|
14
|
+
cursor?: string;
|
|
15
|
+
snapshot?: string;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export declare function wireQueryInput(input: QueryInput | QueryMetadataInput): {
|
|
19
|
+
output?: "metadata" | undefined;
|
|
20
|
+
types?: string[] | undefined;
|
|
21
|
+
timezone?: string | undefined;
|
|
22
|
+
context?: {
|
|
23
|
+
this: {
|
|
24
|
+
path: string;
|
|
25
|
+
};
|
|
26
|
+
} | undefined;
|
|
27
|
+
projections?: {
|
|
28
|
+
[k: string]: {
|
|
29
|
+
expr: string;
|
|
30
|
+
description?: string | undefined;
|
|
31
|
+
};
|
|
32
|
+
} | undefined;
|
|
33
|
+
where?: string | undefined;
|
|
34
|
+
select?: (string | {
|
|
35
|
+
name: string;
|
|
36
|
+
expr: string;
|
|
37
|
+
label?: string | undefined;
|
|
38
|
+
description?: string | undefined;
|
|
39
|
+
})[] | undefined;
|
|
40
|
+
order_by?: import("./operation-types.js").QueryOrder[] | undefined;
|
|
41
|
+
group_by?: import("./operation-types.js").QueryOrder[] | undefined;
|
|
42
|
+
summary_functions?: {
|
|
43
|
+
[k: string]: {
|
|
44
|
+
expr: string;
|
|
45
|
+
description?: string | undefined;
|
|
46
|
+
};
|
|
47
|
+
} | undefined;
|
|
48
|
+
summaries?: import("./operation-types.js").QuerySummary[] | undefined;
|
|
49
|
+
limit?: number | undefined;
|
|
50
|
+
offset?: number | undefined;
|
|
51
|
+
pagination?: "cursor" | undefined;
|
|
52
|
+
cursor?: string | undefined;
|
|
53
|
+
snapshot?: string | undefined;
|
|
54
|
+
include_body?: boolean | undefined;
|
|
55
|
+
frontmatter_mode?: "both" | "effective" | "persisted" | undefined;
|
|
56
|
+
contract?: import("./operation-types.js").DataContractSelector | undefined;
|
|
57
|
+
};
|
|
58
|
+
export declare function wireDataContractIdentity(value: WireIdentity): DataContractViewIdentity;
|
|
59
|
+
export declare function wireQueryRecord<Frontmatter extends JsonObject>(value: WireQueryRecord<Frontmatter>): QueryRecord<Frontmatter>;
|
|
60
|
+
export declare function wireQueryResult<Frontmatter extends JsonObject>(value: WireQueryResult<Frontmatter>, output: "metadata" | undefined): QueryResult<Frontmatter> | QueryMetadataResult;
|
|
61
|
+
export declare function wireCollectionDescription(value: WireDescription): CollectionDescription;
|