@mdbase-dev/connect 0.1.0-beta.21 → 0.1.0-beta.23
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 +108 -38
- package/dist/authorization-url.d.ts +2 -1
- package/dist/authorization-url.js +5 -7
- package/dist/browser/integrity.json +1 -1
- package/dist/browser/mdbase-connect.min.js +3 -3
- package/dist/browser/mdbase-connect.min.js.sha384 +1 -1
- package/dist/collection-client.d.ts +34 -31
- package/dist/collection-client.js +107 -61
- package/dist/connect.d.ts +4 -3
- package/dist/connect.js +53 -38
- package/dist/connection-file-transport.d.ts +31 -0
- package/dist/connection-file-transport.js +42 -0
- package/dist/connection-notifications.js +21 -21
- package/dist/connection-transport.d.ts +2 -0
- package/dist/connection-transport.js +95 -37
- package/dist/connection-types.d.ts +2 -1
- package/dist/connection.d.ts +49 -45
- package/dist/connection.js +73 -33
- package/dist/crypto.d.ts +5 -6
- package/dist/crypto.js +13 -13
- package/dist/errors.d.ts +38 -24
- package/dist/errors.js +130 -71
- package/dist/file-crypto.d.ts +25 -0
- package/dist/file-crypto.js +132 -0
- package/dist/file-sha256.d.ts +12 -0
- package/dist/file-sha256.js +143 -0
- package/dist/file-stream-source.d.ts +17 -0
- package/dist/file-stream-source.js +157 -0
- package/dist/files.d.ts +91 -0
- package/dist/files.js +644 -0
- package/dist/hosted-file-request.d.ts +6 -0
- package/dist/hosted-file-request.js +63 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +3 -0
- package/dist/internal-types.d.ts +3 -1
- package/dist/local-file-transport.d.ts +30 -0
- package/dist/local-file-transport.js +367 -0
- package/dist/notifications.js +5 -5
- package/dist/operation-helpers.d.ts +2 -2
- package/dist/operation-helpers.js +22 -18
- package/dist/operation-types.d.ts +3 -4
- package/dist/outcomes.d.ts +51 -0
- package/dist/outcomes.js +168 -0
- package/dist/runtime-utils.d.ts +6 -1
- package/dist/runtime-utils.js +90 -11
- package/dist/selection.js +3 -3
- package/dist/session.d.ts +9 -8
- package/dist/session.js +31 -20
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -27,49 +27,49 @@ const session = mdbase.createSession({
|
|
|
27
27
|
selection: new MdbaseBrowserSelection()
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
await session.start();
|
|
30
|
+
const started = await session.start();
|
|
31
|
+
if (!started.ok) {
|
|
32
|
+
renderProblem(started.problem);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
31
35
|
if (session.getSnapshot().status === "unselected") {
|
|
32
|
-
await session.authorize("choose");
|
|
36
|
+
const authorized = await session.authorize("choose");
|
|
37
|
+
if (!authorized.ok) {
|
|
38
|
+
renderProblem(authorized.problem);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
33
41
|
}
|
|
34
42
|
|
|
35
43
|
const snapshot = session.getSnapshot();
|
|
36
44
|
if (snapshot.status !== "ready") throw new Error("Choose an authorized collection.");
|
|
37
45
|
const connection = snapshot.connection;
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
46
|
+
const queried = await connection.query({ types: ["workout"] });
|
|
47
|
+
if (!queried.ok) {
|
|
48
|
+
renderProblem(queried.problem);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
const workouts = queried.value;
|
|
52
|
+
const updated = await connection.update({
|
|
41
53
|
path: "workouts/monday.md",
|
|
42
54
|
patch: { completed: true },
|
|
43
|
-
if_revision: workouts.
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
const preview = await connection.preflightRename({
|
|
47
|
-
from: "workouts/monday.md",
|
|
48
|
-
to: "archive/monday.md",
|
|
49
|
-
update_refs: true,
|
|
50
|
-
if_revision: workouts.result.results[0].revision
|
|
51
|
-
});
|
|
52
|
-
console.log(preview.result.references_affected);
|
|
53
|
-
|
|
54
|
-
const controller = new AbortController();
|
|
55
|
-
await connection.renameWithProgress({
|
|
56
|
-
from: "workouts/monday.md",
|
|
57
|
-
to: "archive/monday.md",
|
|
58
|
-
update_refs: true,
|
|
59
|
-
if_revision: workouts.result.results[0].revision
|
|
60
|
-
}, {
|
|
61
|
-
preflight: preview.result,
|
|
62
|
-
signal: controller.signal,
|
|
63
|
-
onProgress: ({ state, estimate, cancellable }) => {
|
|
64
|
-
console.log(state, estimate?.affectedRecords, cancellable);
|
|
65
|
-
}
|
|
55
|
+
if_revision: workouts.results[0].revision
|
|
66
56
|
});
|
|
57
|
+
if (!updated.ok) renderProblem(updated.problem);
|
|
67
58
|
|
|
68
59
|
for await (const change of connection.watch()) {
|
|
69
|
-
|
|
60
|
+
if (!change.ok) {
|
|
61
|
+
renderProblem(change.problem);
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
console.log(change.value.type, change.value.payload.path);
|
|
70
65
|
}
|
|
71
66
|
```
|
|
72
67
|
|
|
68
|
+
Expected failures are returned as typed `ConnectOutcome` values. Exceptions are
|
|
69
|
+
reserved for programming errors and broken SDK invariants. See
|
|
70
|
+
[typed outcomes and recovery](../../docs/sdk-outcomes.md) for the complete
|
|
71
|
+
problem model, setup failures, mutation uncertainty, and UI guidance.
|
|
72
|
+
|
|
73
73
|
`MdbaseConnect` is the application-level authorization registry.
|
|
74
74
|
`MdbaseSession` is the normal application boundary: it owns active collection
|
|
75
75
|
selection, authorization callback completion, access capabilities, and one
|
|
@@ -174,8 +174,8 @@ if (!capabilities.sufficient) {
|
|
|
174
174
|
`requestOperations()` is a no-op when the current grant is sufficient. When a
|
|
175
175
|
replacement grant is needed, it requests the least-privilege union of the
|
|
176
176
|
already granted operations and the missing requirements. An
|
|
177
|
-
`insufficient_access`
|
|
178
|
-
`
|
|
177
|
+
An `insufficient_access` problem carries the same `granted_operations`,
|
|
178
|
+
`missing_operations`, and `required_operations` metadata with a `reauthorize`
|
|
179
179
|
recovery action.
|
|
180
180
|
|
|
181
181
|
Applications that declare an `mdbase.runtime.timer.fired` notification criterion can keep
|
|
@@ -222,12 +222,15 @@ schema:
|
|
|
222
222
|
`
|
|
223
223
|
});
|
|
224
224
|
|
|
225
|
+
if (!created.ok) return renderProblem(created.problem);
|
|
225
226
|
const current = await connection.readType({ name: "workout" });
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
227
|
+
if (!current.ok) return renderProblem(current.problem);
|
|
228
|
+
const updatedType = await connection.updateType({
|
|
229
|
+
path: current.value.path,
|
|
230
|
+
document: current.value.document.replace("version: 1", "version: 2"),
|
|
231
|
+
if_revision: current.value.revision
|
|
230
232
|
});
|
|
233
|
+
if (!updatedType.ok) renderProblem(updatedType.problem);
|
|
231
234
|
```
|
|
232
235
|
|
|
233
236
|
Request `read_type`, `create_type`, and `update_type` during authorization.
|
|
@@ -250,7 +253,8 @@ For a local collection, ask for same-computer access from a user gesture:
|
|
|
250
253
|
|
|
251
254
|
```ts
|
|
252
255
|
const status = await connection.checkDirectAccess();
|
|
253
|
-
if (status
|
|
256
|
+
if (!status.ok) return renderProblem(status.problem);
|
|
257
|
+
if (status.value === "permission_required") {
|
|
254
258
|
directButton.onclick = () => connection.requestDirectAccess();
|
|
255
259
|
}
|
|
256
260
|
|
|
@@ -353,8 +357,9 @@ installs each missing type pack transactionally, verifies its exact contracts
|
|
|
353
357
|
and implementations, and creates the scoped grant afterward. The application
|
|
354
358
|
is not granted collection-wide type-management access.
|
|
355
359
|
|
|
356
|
-
The SDK returns
|
|
357
|
-
|
|
360
|
+
The SDK returns typed outcomes, carries successful mdbase diagnostics alongside
|
|
361
|
+
the value, carries revision tokens in typed record results, and accepts
|
|
362
|
+
`if_revision` on mutations. `describe()` exposes
|
|
358
363
|
JSON Schemas, portable type definitions, canonical collection settings, and
|
|
359
364
|
first-class data contracts. `watch()` resumes from a local collection cursor;
|
|
360
365
|
the Connect server does not store the change feed.
|
|
@@ -399,6 +404,71 @@ local and hosted authorities.
|
|
|
399
404
|
provider credential. It refreshes the grant-bound capability as needed and can
|
|
400
405
|
be passed directly to `@mdbase-dev/connect-sync` for an offline application cache.
|
|
401
406
|
|
|
407
|
+
Applications request non-Markdown files separately from record contracts:
|
|
408
|
+
|
|
409
|
+
```ts
|
|
410
|
+
requirements: {
|
|
411
|
+
contracts: [],
|
|
412
|
+
files: {
|
|
413
|
+
actions: ["list", "read", "add", "replace", "move", "delete"],
|
|
414
|
+
scope: { kind: "selected_folders", folders: ["Photos"] }
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
The granted connection exposes one storage-neutral facade. Hosted uploads go
|
|
420
|
+
directly to private R2 objects through short-lived prepared requests. Downloads
|
|
421
|
+
use authenticated bounded ranges so the authority can recheck access between
|
|
422
|
+
parts. Object keys, multipart ETags, provider credentials, retries, and
|
|
423
|
+
integrity checks stay inside the SDK.
|
|
424
|
+
|
|
425
|
+
```ts
|
|
426
|
+
const saved = await connection.files.upload("Photos/image.jpg", browserFile, {
|
|
427
|
+
onProgress: ({ phase, transferredBytes, totalBytes }) => {
|
|
428
|
+
console.log(phase, transferredBytes, totalBytes);
|
|
429
|
+
}
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
const large = await connection.files.uploadStream("Media/video.mp4", {
|
|
433
|
+
size: manifest.size,
|
|
434
|
+
contentDigest: manifest.sha256,
|
|
435
|
+
stream: response.body!
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
for await (const file of connection.files.list({ folder: "Photos" })) {
|
|
439
|
+
const stream = await connection.files.downloadStream(file);
|
|
440
|
+
await stream.pipeTo(destination);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
const moved = await connection.files.move(saved, "Archive/image.jpg");
|
|
444
|
+
await connection.files.delete(moved);
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
`download()` and `downloadBytes()` are convenient for values up to 64 MiB.
|
|
448
|
+
Use `downloadStream()` for larger files; it verifies the pinned revision while
|
|
449
|
+
forwarding response chunks with native stream backpressure rather than
|
|
450
|
+
buffering a complete hosted range.
|
|
451
|
+
The live stream retries unopened chunks and can switch a local download between
|
|
452
|
+
direct and relay. It does not persist progress after consumption stops or
|
|
453
|
+
resume a hosted range after part of that range has reached the consumer;
|
|
454
|
+
restart `downloadStream()` to reopen the pinned revision in those cases.
|
|
455
|
+
|
|
456
|
+
`upload()` accepts `Blob`, `ArrayBuffer`, and typed-array values and hashes them
|
|
457
|
+
before opening the transfer. `uploadStream()` accepts a `ReadableStream` or
|
|
458
|
+
async iterable plus its exact size and `sha256:…` commitment. It verifies the
|
|
459
|
+
stream while uploading sequentially without accumulating multiple file parts.
|
|
460
|
+
SDK memory is bounded by one assembled negotiated part plus at most one source
|
|
461
|
+
chunk; a single-part upload may therefore hold the complete file. Yielded
|
|
462
|
+
source chunks must fit within the authority's negotiated upload part; ordinary
|
|
463
|
+
browser and Node streams already produce much smaller chunks. After an
|
|
464
|
+
ambiguous failure, call it again with a newly opened source and the same
|
|
465
|
+
`transferId` to resume safely.
|
|
466
|
+
|
|
467
|
+
Both lifecycle methods are optimistic and use the descriptor revision by
|
|
468
|
+
default. Pass a stable `mutationId` when retrying after an ambiguous network
|
|
469
|
+
failure; the authority returns the original durable receipt instead of applying
|
|
470
|
+
the change twice.
|
|
471
|
+
|
|
402
472
|
Record-facing code can depend on `MdbaseCollectionClient` instead of the OAuth
|
|
403
473
|
client. It accepts a small `MdbaseCollectionTransport`, which is the stable seam
|
|
404
474
|
used by Connect, the developer sandbox, and future hosted providers. This keeps
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ConnectProblem } from "@mdbase-dev/connect-protocol";
|
|
1
2
|
export declare function cleanAuthorizationParameters(url: URL): URL;
|
|
2
3
|
export declare function isAuthorizationCallbackUrl(value: string): boolean;
|
|
3
|
-
export declare function
|
|
4
|
+
export declare function authorizationReturnToFromProblem(problem: ConnectProblem): string | undefined;
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { MdbaseConnectError } from "./errors.js";
|
|
2
1
|
export function cleanAuthorizationParameters(url) {
|
|
3
2
|
for (const parameter of ["code", "state", "error", "error_description"]) {
|
|
4
3
|
url.searchParams.delete(parameter);
|
|
@@ -16,12 +15,11 @@ export function isAuthorizationCallbackUrl(value) {
|
|
|
16
15
|
return url.searchParams.has("state")
|
|
17
16
|
&& (url.searchParams.has("code") || url.searchParams.has("error"));
|
|
18
17
|
}
|
|
19
|
-
export function
|
|
20
|
-
if (!
|
|
21
|
-
||
|
|
22
|
-
||
|
|
23
|
-
|| Array.isArray(error.details))
|
|
18
|
+
export function authorizationReturnToFromProblem(problem) {
|
|
19
|
+
if (!problem.details
|
|
20
|
+
|| typeof problem.details !== "object"
|
|
21
|
+
|| Array.isArray(problem.details))
|
|
24
22
|
return undefined;
|
|
25
|
-
const returnTo =
|
|
23
|
+
const returnTo = problem.details.return_to;
|
|
26
24
|
return typeof returnTo === "string" ? returnTo : undefined;
|
|
27
25
|
}
|