@mdbase-dev/connect 0.1.0-beta.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +405 -0
  3. package/dist/async.d.ts +1 -0
  4. package/dist/async.js +13 -0
  5. package/dist/authorization-types.d.ts +7 -0
  6. package/dist/authorization-types.js +1 -0
  7. package/dist/authorization-url.d.ts +3 -0
  8. package/dist/authorization-url.js +27 -0
  9. package/dist/base64.d.ts +3 -0
  10. package/dist/base64.js +21 -0
  11. package/dist/browser/integrity.json +4 -0
  12. package/dist/browser/mdbase-connect.min.js +3 -0
  13. package/dist/browser/mdbase-connect.min.js.sha384 +1 -0
  14. package/dist/collection-client.d.ts +57 -0
  15. package/dist/collection-client.js +214 -0
  16. package/dist/connect.d.ts +39 -0
  17. package/dist/connect.js +620 -0
  18. package/dist/connection-notifications.d.ts +25 -0
  19. package/dist/connection-notifications.js +179 -0
  20. package/dist/connection-transport.d.ts +80 -0
  21. package/dist/connection-transport.js +670 -0
  22. package/dist/connection-types.d.ts +29 -0
  23. package/dist/connection-types.js +1 -0
  24. package/dist/connection.d.ts +154 -0
  25. package/dist/connection.js +371 -0
  26. package/dist/crypto.d.ts +64 -0
  27. package/dist/crypto.js +391 -0
  28. package/dist/errors.d.ts +31 -0
  29. package/dist/errors.js +83 -0
  30. package/dist/index.d.ts +13 -0
  31. package/dist/index.js +12 -0
  32. package/dist/internal-types.d.ts +70 -0
  33. package/dist/internal-types.js +1 -0
  34. package/dist/notifications.d.ts +47 -0
  35. package/dist/notifications.js +52 -0
  36. package/dist/operation-helpers.d.ts +24 -0
  37. package/dist/operation-helpers.js +129 -0
  38. package/dist/operation-types.d.ts +254 -0
  39. package/dist/operation-types.js +1 -0
  40. package/dist/runtime-utils.d.ts +33 -0
  41. package/dist/runtime-utils.js +224 -0
  42. package/dist/selection.d.ts +48 -0
  43. package/dist/selection.js +112 -0
  44. package/dist/session.d.ts +76 -0
  45. package/dist/session.js +271 -0
  46. package/package.json +41 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 mdbase connect contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,405 @@
1
+ # `@mdbase-dev/connect`
2
+
3
+ Browser SDK for dynamically discovered mdbase connect applications.
4
+
5
+ It also ships a dependency-free browser bundle for downloaded HTML
6
+ applications. See the [portable application guide](../../docs/portable-apps.md)
7
+ for the v1 manifest, device-code flow, version-pinned CDN URL, SRI metadata,
8
+ and `file://` storage boundary.
9
+
10
+ The complete developer guide covers
11
+ [setup](https://mdbase.dev/sdk/quickstart/),
12
+ [manifests and contracts](https://mdbase.dev/sdk/manifest/),
13
+ [authorization](https://mdbase.dev/sdk/authorization/),
14
+ [operations](https://mdbase.dev/sdk/operations/),
15
+ [routing](https://mdbase.dev/sdk/routing/),
16
+ [offline sync](https://mdbase.dev/sdk/offline-sync/), and
17
+ [testing](https://mdbase.dev/sdk/testing/).
18
+
19
+ ```ts
20
+ const mdbase = new MdbaseConnect({
21
+ serverUrl: "https://connect.mdbase.dev",
22
+ manifest: new URL(".well-known/mdbase-app.json", location.href).href,
23
+ redirectUri: "https://workouts.example/auth/mdbase/callback"
24
+ });
25
+ const session = mdbase.createSession({
26
+ operations: ["describe", "changes", "read", "query", "update"],
27
+ selection: new MdbaseBrowserSelection()
28
+ });
29
+
30
+ await session.start();
31
+ if (session.getSnapshot().status === "unselected") {
32
+ await session.authorize("choose");
33
+ }
34
+
35
+ const snapshot = session.getSnapshot();
36
+ if (snapshot.status !== "ready") throw new Error("Choose an authorized collection.");
37
+ const connection = snapshot.connection;
38
+ const description = await connection.describe();
39
+ const workouts = await connection.query({ types: ["workout"] });
40
+ await connection.update({
41
+ path: "workouts/monday.md",
42
+ patch: { completed: true },
43
+ if_revision: workouts.result.results[0].revision
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
+ }
66
+ });
67
+
68
+ for await (const change of connection.watch()) {
69
+ console.log(change.type, change.payload.path);
70
+ }
71
+ ```
72
+
73
+ `MdbaseConnect` is the application-level authorization registry.
74
+ `MdbaseSession` is the normal application boundary: it owns active collection
75
+ selection, authorization callback completion, access capabilities, and one
76
+ reactive snapshot. Use `getSnapshot()` and `subscribe()` directly or through
77
+ your framework's external-store integration. A snapshot is `unselected`,
78
+ `ready`, or `unavailable`; an unavailable bookmark includes the explicit
79
+ collection ID and reason instead of collapsing every recovery state to `null`.
80
+
81
+ `MdbaseBrowserSelection` keeps the stable collection ID in
82
+ `?collection=<id>`, preserves unrelated path/query/hash and router state,
83
+ restores authorization callbacks safely, and reports browser back/forward
84
+ changes to the session. The session auto-selects only when exactly one saved
85
+ connection exists. Switching is state-driven and must not reload the page:
86
+
87
+ ```ts
88
+ session.select(collectionId, { history: "replace" });
89
+ session.clearSelection();
90
+ session.forget(collectionId);
91
+ ```
92
+
93
+ Selection validates the saved authorization before changing the URL. Explicit
94
+ unavailable IDs remain authoritative so applications can offer exact
95
+ reauthorization or another collection:
96
+
97
+ ```ts
98
+ await session.authorize("selected"); // exact bookmarked collection
99
+ await session.authorize("choose"); // any compatible collection
100
+ await session.authorize({ collectionId }); // exact adoption/migration target
101
+ ```
102
+
103
+ Collection IDs are opaque non-secret locators that may appear in browser
104
+ history and logs. Grants remain the authorization boundary, and names are
105
+ display text that may change.
106
+
107
+ Bundled v1 application manifests can declare runtime notification criteria.
108
+ Register a service worker from a user gesture to receive standards-based Web
109
+ Push while the app is closed:
110
+
111
+ ```ts
112
+ const worker = await navigator.serviceWorker.register("/service-worker.js");
113
+ await connection.registerNotifications({
114
+ serviceWorker: worker,
115
+ criteria: ["workout.reminder"]
116
+ });
117
+ ```
118
+
119
+ In the service worker:
120
+
121
+ ```ts
122
+ import { showMdbasePushNotification } from "@mdbase-dev/connect";
123
+
124
+ self.addEventListener("push", (event) => {
125
+ event.waitUntil(
126
+ showMdbasePushNotification(self.registration, event.data?.json())
127
+ );
128
+ });
129
+ ```
130
+
131
+ Pushes contain an opaque signal and cursor plus static declaration presentation,
132
+ never record paths or content. Treat them as a wake-up hint and read current
133
+ authorized state after opening. Registration atomically replaces the selected
134
+ criteria for that installation. See the
135
+ [runtime notifications guide](../../docs/notifications.md) for declaration and
136
+ deployment examples.
137
+
138
+ Native shells can use one FCM token for Android and iOS when the declaration
139
+ declares `notifications.native_delivery.mode` as `managed_fcm`:
140
+
141
+ ```ts
142
+ await connection.registerNativeNotifications({
143
+ token: await nativeMessaging.getToken(),
144
+ criteria: ["workout.reminder"]
145
+ });
146
+ ```
147
+
148
+ Re-register when Firebase rotates the token. Parse the string-valued
149
+ notification data with `parseMdbaseNativeNotificationData()`, refresh current
150
+ collection state, and call `unregisterNativeNotifications()` before deleting
151
+ the token on opt-out. The public Firebase project ID is read from the
152
+ application declaration; service credentials are never embedded in the app.
153
+
154
+ Connect-managed FCM makes Connect a trusted sender for the application's
155
+ Firebase project. It suits single-owner deployments; applications with a
156
+ broader audience should normally declare signed webhook delivery and keep
157
+ their Apple/Firebase credentials in their own backend. The notifications guide
158
+ documents both threat models. New or changed declaration criteria never silently
159
+ broaden an existing grant: handle `notification_reauthorization_required` by
160
+ running authorization again before retrying registration.
161
+
162
+ Before opening a feature, inspect its exact authorization gap instead of
163
+ waiting for an operation to fail:
164
+
165
+ ```ts
166
+ const required = ["read", "query", "update"] as const;
167
+ const capabilities = connection.authorizationCapabilities([...required]);
168
+ if (!capabilities.sufficient) {
169
+ console.log("Needs", capabilities.missingOperations);
170
+ await connection.requestOperations([...required]);
171
+ }
172
+ ```
173
+
174
+ `requestOperations()` is a no-op when the current grant is sufficient. When a
175
+ replacement grant is needed, it requests the least-privilege union of the
176
+ already granted operations and the missing requirements. An
177
+ `insufficient_access` error carries the same `grantedOperations`,
178
+ `missingOperations`, and `requiredOperations` metadata with a `reauthorize`
179
+ recovery action.
180
+
181
+ Applications that declare an `mdbase.runtime.timer.fired` notification criterion can keep
182
+ one-shot reminders at the collection authority:
183
+
184
+ ```ts
185
+ await connection.reconcileTimers({
186
+ namespace: "workout-reminders",
187
+ criterion_id: "workout.reminder",
188
+ timers: [{
189
+ id: "workout-42",
190
+ fire_at: new Date("2026-07-25T10:00:00Z").toISOString()
191
+ }]
192
+ });
193
+ ```
194
+
195
+ The SDK also exposes `listTimers()`, `putTimer()`, and `cancelTimer()`.
196
+ Reconciliation is atomic and is normally the safest way to project a complete
197
+ desired timer set. IDs and optional timer data stay at the local connector or
198
+ hosted provider and are not included in notification signals.
199
+
200
+ When the local connector definitively rejects an encrypted grant, the SDK does
201
+ not bypass that decision through the relay. It classifies the error as requiring
202
+ authorization, removes only the matching stale credential, and emits a `null`
203
+ connection through `onConnectionChange()`. A subsequent
204
+ `requestOperations()` call therefore starts authorization instead of trusting
205
+ the stale cached capability list.
206
+
207
+ Applications with full collection access can also register and maintain type
208
+ definitions. Type source is returned with a revision token so updates cannot
209
+ silently overwrite a definition changed by another application:
210
+
211
+ ```ts
212
+ const created = await connection.createType({
213
+ document: `---
214
+ kind: mdbase.type
215
+ name: workout
216
+ version: 1
217
+ schema:
218
+ dialect: json-schema-2020-12
219
+ value:
220
+ type: object
221
+ ---
222
+ `
223
+ });
224
+
225
+ const current = await connection.readType({ name: "workout" });
226
+ await connection.updateType({
227
+ path: current.result.path,
228
+ document: current.result.document.replace("version: 1", "version: 2"),
229
+ if_revision: current.result.revision
230
+ });
231
+ ```
232
+
233
+ Request `read_type`, `create_type`, and `update_type` during authorization.
234
+ Contract-scoped applications cannot manage collection-wide type definitions.
235
+
236
+ To install a complete catalog pack without exposing its individual collection
237
+ paths, request `install_type_pack`, fetch and verify the published provision,
238
+ then pass it to the atomic collection operation:
239
+
240
+ ```ts
241
+ const response = await fetch(pack.provisionUrl);
242
+ const provision = await response.json();
243
+ await connection.installTypePack(provision);
244
+ ```
245
+
246
+ The caller remains responsible for verifying the catalog digest before sending
247
+ the provision. Existing targets with different content are rejected.
248
+
249
+ For a local collection, ask for same-computer access from a user gesture:
250
+
251
+ ```ts
252
+ const status = await connection.checkDirectAccess();
253
+ if (status === "permission_required") {
254
+ directButton.onclick = () => connection.requestDirectAccess();
255
+ }
256
+
257
+ connection.onConnectionChange((info) => {
258
+ console.log(info?.route); // "direct", "relay", or "remote"
259
+ });
260
+ ```
261
+
262
+ After permission is granted, all grantable operations prefer the fixed
263
+ loopback connector automatically. If it is absent or unavailable, the SDK
264
+ retries the exact encrypted envelope through the relay. Durable connector
265
+ receipts prevent an ambiguous direct write from running twice. The SDK retains
266
+ one unresolved encrypted mutation locally and requires that exact write to be
267
+ retried before a later mutation can overtake it. Set
268
+ `directAccess: "disabled"` only when an embedding environment cannot use
269
+ loopback requests.
270
+
271
+ The SDK loads the v1 manifest from the application's own bundle and
272
+ posts it inline to Connect. Connect identifies the exact canonical declaration
273
+ by its digest. The declared reverse-domain ID and name are presentation
274
+ metadata, not proof of a publisher; grants remain bound to the authorization
275
+ completed by that installation. No developer account, public manifest host, or
276
+ manually issued client secret is required.
277
+
278
+ Applications declare exact data-contract versions in their bundled manifest.
279
+ Connect offers collections that already provide those contracts or can install
280
+ the declared type pack safely. The developer helper computes every resource
281
+ digest:
282
+
283
+ ```ts
284
+ import { defineTypePack } from "@mdbase-dev/connect-dev";
285
+
286
+ const contract = `---
287
+ kind: mdbase.contract
288
+ contract_type: record
289
+ id: example.work-item
290
+ version: 1.0.0
291
+ record_schema:
292
+ dialect: json-schema-2020-12
293
+ value:
294
+ type: object
295
+ required: [title]
296
+ properties:
297
+ title: { type: string }
298
+ ---
299
+ `;
300
+ const type = `---
301
+ kind: mdbase.type
302
+ name: work-item
303
+ version: 1
304
+ schema:
305
+ dialect: json-schema-2020-12
306
+ value:
307
+ type: object
308
+ implements:
309
+ - contract: example.work-item
310
+ version: 1.0.0
311
+ fields: { title: title }
312
+ ---
313
+ `;
314
+
315
+ export const manifest = {
316
+ manifest_version: 1,
317
+ id: "dev.mdbase.worklog",
318
+ name: "Worklog",
319
+ homepage: "https://worklog.example",
320
+ redirect_uris: ["https://worklog.example/auth/mdbase/callback"],
321
+ requirements: {
322
+ contracts: [{ id: "example.work-item", version: "1.0.0" }]
323
+ },
324
+ provisions: {
325
+ type_packs: [defineTypePack({
326
+ id: "example.work-items",
327
+ version: "1.0.0",
328
+ provides: [{ id: "example.work-item", version: "1.0.0" }],
329
+ resources: [
330
+ { kind: "contract", source: "_contracts/example.work-item.md", document: contract },
331
+ { kind: "type", source: "_types/work-item.md", document: type }
332
+ ]
333
+ })]
334
+ }
335
+ };
336
+ ```
337
+
338
+ Set `collection_kind` to `hosted` when the application needs a durable
339
+ provider-backed collection and the offline sync transport returned by
340
+ `sync()`. Connect then offers and accepts collections with replication capability.
341
+
342
+ Access defaults to the record types supplied by `requirements.contracts`.
343
+ Set `access` to `full_collection` when the application needs collection-level
344
+ features such as saved views. Required contracts still determine compatibility
345
+ and are provisioned during approval.
346
+
347
+ `listViews()` returns each named view's selected result properties in display
348
+ order. Property descriptors retain source labels for projected and computed
349
+ values. `executeView()` returns their values on each result row.
350
+
351
+ Provisioning is part of the approval flow. The connector validates and
352
+ installs each missing type pack transactionally, verifies its exact contracts
353
+ and implementations, and creates the scoped grant afterward. The application
354
+ is not granted collection-wide type-management access.
355
+
356
+ The SDK returns the mdbase operation envelope, carries revision tokens in typed
357
+ record results, and accepts `if_revision` on mutations. `describe()` exposes
358
+ JSON Schemas, portable type definitions, canonical collection settings, and
359
+ first-class data contracts. `watch()` resumes from a local collection cursor;
360
+ the Connect server does not store the change feed.
361
+ `preflightRename()` and `preflightDelete()` run the canonical collection
362
+ operation without changing records or advancing the change cursor, so an app
363
+ can show authoritative reference impact before asking for confirmation.
364
+ `renameWithProgress()` and `deleteWithProgress()` expose preflight, ready,
365
+ applying, completed, and cancelled phases with an impact estimate. Cancellation
366
+ remains available during an encrypted local/relay mutation because the SDK
367
+ persists its exact encrypted request before dispatch. If waiting is cancelled
368
+ after dispatch, `pendingMutation()` reports the interruption and
369
+ `resumePendingMutation()` safely recovers the connector's durable receipt using
370
+ the exact same input. Other providers are cancellable until apply begins and
371
+ then run to a definitive response.
372
+ Authorization is retained in `localStorage` by default. Access tokens are
373
+ renewed with rotating refresh tokens; passing a custom `Storage` implementation
374
+ allows a host to choose another persistence boundary.
375
+
376
+ Native shells can pass `navigate` to open the authorization URL in the system
377
+ browser and list a reverse-domain callback such as
378
+ `dev.mdbase.worklog://auth/mdbase/callback` in the bundled declaration. Its
379
+ scheme must match the declaration ID. PKCE remains mandatory. Call
380
+ `completeAuthorization(callbackUrl)` when the application receives the deep
381
+ link.
382
+
383
+ New authorizations require encrypted relay protocol 1 by default. The SDK keeps
384
+ independent non-extractable P-256 ECDH agreement and ECDSA signing keys plus an
385
+ atomic message counter in IndexedDB. It encrypts operation inputs for the
386
+ connector, decrypts connector results locally, and signs hosted authority
387
+ requests with the independent signing key. Set `relayEncryption: "disabled"`
388
+ only for development where end-to-end relay encryption is intentionally
389
+ unavailable; an encrypted grant never falls back to plaintext.
390
+
391
+ For a hosted collection, the same authorization exchange returns a short-lived,
392
+ grant-bound provider capability. The SDK routes operations directly to the
393
+ hosted Rust data plane, binds browser requests to the approved callback origin, and does
394
+ not send record payloads through the Connect control plane. Refresh rotation,
395
+ permission narrowing, and revocation keep the same public SDK behavior across
396
+ local and hosted authorities.
397
+
398
+ `sync()` exposes a provider-neutral sync transport without exposing the
399
+ provider credential. It refreshes the grant-bound capability as needed and can
400
+ be passed directly to `@mdbase-dev/connect-sync` for an offline application cache.
401
+
402
+ Record-facing code can depend on `MdbaseCollectionClient` instead of the OAuth
403
+ client. It accepts a small `MdbaseCollectionTransport`, which is the stable seam
404
+ used by Connect, the developer sandbox, and future hosted providers. This keeps
405
+ application logic independent of authorization and deployment topology.
@@ -0,0 +1 @@
1
+ export declare function abortableDelay(milliseconds: number, signal?: AbortSignal): Promise<void>;
package/dist/async.js ADDED
@@ -0,0 +1,13 @@
1
+ export function abortableDelay(milliseconds, signal) {
2
+ return new Promise((resolve, reject) => {
3
+ if (signal?.aborted) {
4
+ reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
5
+ return;
6
+ }
7
+ const timeout = setTimeout(resolve, milliseconds);
8
+ signal?.addEventListener("abort", () => {
9
+ clearTimeout(timeout);
10
+ reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
11
+ }, { once: true });
12
+ });
13
+ }
@@ -0,0 +1,7 @@
1
+ export interface MdbaseDeviceAuthorization {
2
+ userCode: string;
3
+ verificationUri: string;
4
+ verificationUriComplete: string;
5
+ expiresAt: number;
6
+ intervalSeconds: number;
7
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ export declare function cleanAuthorizationParameters(url: URL): URL;
2
+ export declare function isAuthorizationCallbackUrl(value: string): boolean;
3
+ export declare function authorizationReturnToFromError(error: unknown): string | undefined;
@@ -0,0 +1,27 @@
1
+ import { MdbaseConnectError } from "./errors.js";
2
+ export function cleanAuthorizationParameters(url) {
3
+ for (const parameter of ["code", "state", "error", "error_description"]) {
4
+ url.searchParams.delete(parameter);
5
+ }
6
+ return url;
7
+ }
8
+ export function isAuthorizationCallbackUrl(value) {
9
+ let url;
10
+ try {
11
+ url = new URL(value);
12
+ }
13
+ catch {
14
+ return false;
15
+ }
16
+ return url.searchParams.has("state")
17
+ && (url.searchParams.has("code") || url.searchParams.has("error"));
18
+ }
19
+ export function authorizationReturnToFromError(error) {
20
+ if (!(error instanceof MdbaseConnectError)
21
+ || !error.details
22
+ || typeof error.details !== "object"
23
+ || Array.isArray(error.details))
24
+ return undefined;
25
+ const returnTo = error.details.returnTo;
26
+ return typeof returnTo === "string" ? returnTo : undefined;
27
+ }
@@ -0,0 +1,3 @@
1
+ export declare function randomBase64Url(size: number): string;
2
+ export declare function bytesToBase64Url(bytes: Uint8Array): string;
3
+ export declare function base64UrlBytes(value: string): Uint8Array<ArrayBuffer>;
package/dist/base64.js ADDED
@@ -0,0 +1,21 @@
1
+ export function randomBase64Url(size) {
2
+ const bytes = new Uint8Array(size);
3
+ crypto.getRandomValues(bytes);
4
+ return bytesToBase64Url(bytes);
5
+ }
6
+ export function bytesToBase64Url(bytes) {
7
+ let binary = "";
8
+ for (const byte of bytes)
9
+ binary += String.fromCharCode(byte);
10
+ return btoa(binary).replaceAll("+", "-").replaceAll("/", "_").replace(/=+$/, "");
11
+ }
12
+ export function base64UrlBytes(value) {
13
+ const padded = value.replaceAll("-", "+").replaceAll("_", "/")
14
+ .padEnd(Math.ceil(value.length / 4) * 4, "=");
15
+ const binary = atob(padded);
16
+ const bytes = new Uint8Array(new ArrayBuffer(binary.length));
17
+ for (let index = 0; index < binary.length; index += 1) {
18
+ bytes[index] = binary.charCodeAt(index);
19
+ }
20
+ return bytes;
21
+ }
@@ -0,0 +1,4 @@
1
+ {
2
+ "file": "mdbase-connect.min.js",
3
+ "integrity": "sha384-u+z1WOpeMWPwuYV9kDMHHAqQEZKGn+NwX9tjAYtNztyMD7WJbsdj+Be++i43R9nY"
4
+ }