@weaveprotocol/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1106 -0
- package/dist/elements/auth-styles.d.ts +18 -0
- package/dist/elements/auth-styles.d.ts.map +1 -0
- package/dist/elements/auth-styles.js +150 -0
- package/dist/elements/auth-styles.js.map +1 -0
- package/dist/elements/dom.d.ts +28 -0
- package/dist/elements/dom.d.ts.map +1 -0
- package/dist/elements/dom.js +74 -0
- package/dist/elements/dom.js.map +1 -0
- package/dist/elements/index.d.ts +13 -0
- package/dist/elements/index.d.ts.map +1 -0
- package/dist/elements/index.js +11 -0
- package/dist/elements/index.js.map +1 -0
- package/dist/elements/weave-auth.d.ts +52 -0
- package/dist/elements/weave-auth.d.ts.map +1 -0
- package/dist/elements/weave-auth.js +427 -0
- package/dist/elements/weave-auth.js.map +1 -0
- package/dist/identity/account-store.d.ts +113 -0
- package/dist/identity/account-store.d.ts.map +1 -0
- package/dist/identity/account-store.js +305 -0
- package/dist/identity/account-store.js.map +1 -0
- package/dist/identity/account-vault.d.ts +199 -0
- package/dist/identity/account-vault.d.ts.map +1 -0
- package/dist/identity/account-vault.js +251 -0
- package/dist/identity/account-vault.js.map +1 -0
- package/dist/identity/agent-note.d.ts +19 -0
- package/dist/identity/agent-note.d.ts.map +1 -0
- package/dist/identity/agent-note.js +29 -0
- package/dist/identity/agent-note.js.map +1 -0
- package/dist/identity/crypto-p256.d.ts +7 -0
- package/dist/identity/crypto-p256.d.ts.map +1 -0
- package/dist/identity/crypto-p256.js +84 -0
- package/dist/identity/crypto-p256.js.map +1 -0
- package/dist/identity/device-key.d.ts +60 -0
- package/dist/identity/device-key.d.ts.map +1 -0
- package/dist/identity/device-key.js +103 -0
- package/dist/identity/device-key.js.map +1 -0
- package/dist/identity/did.d.ts +22 -0
- package/dist/identity/did.d.ts.map +1 -0
- package/dist/identity/did.js +37 -0
- package/dist/identity/did.js.map +1 -0
- package/dist/identity/folder-account.d.ts +65 -0
- package/dist/identity/folder-account.d.ts.map +1 -0
- package/dist/identity/folder-account.js +115 -0
- package/dist/identity/folder-account.js.map +1 -0
- package/dist/identity/identity-manager.d.ts +36 -0
- package/dist/identity/identity-manager.d.ts.map +1 -0
- package/dist/identity/identity-manager.js +115 -0
- package/dist/identity/identity-manager.js.map +1 -0
- package/dist/identity/index.d.ts +10 -0
- package/dist/identity/index.d.ts.map +1 -0
- package/dist/identity/index.js +10 -0
- package/dist/identity/index.js.map +1 -0
- package/dist/identity/keys.d.ts +24 -0
- package/dist/identity/keys.d.ts.map +1 -0
- package/dist/identity/keys.js +38 -0
- package/dist/identity/keys.js.map +1 -0
- package/dist/identity/pairing.d.ts +83 -0
- package/dist/identity/pairing.d.ts.map +1 -0
- package/dist/identity/pairing.js +120 -0
- package/dist/identity/pairing.js.map +1 -0
- package/dist/identity/passkey-diagnostics.d.ts +54 -0
- package/dist/identity/passkey-diagnostics.d.ts.map +1 -0
- package/dist/identity/passkey-diagnostics.js +178 -0
- package/dist/identity/passkey-diagnostics.js.map +1 -0
- package/dist/identity/recovery-code.d.ts +50 -0
- package/dist/identity/recovery-code.d.ts.map +1 -0
- package/dist/identity/recovery-code.js +116 -0
- package/dist/identity/recovery-code.js.map +1 -0
- package/dist/identity/root-signer.d.ts +59 -0
- package/dist/identity/root-signer.d.ts.map +1 -0
- package/dist/identity/root-signer.js +44 -0
- package/dist/identity/root-signer.js.map +1 -0
- package/dist/identity/ucan.d.ts +164 -0
- package/dist/identity/ucan.d.ts.map +1 -0
- package/dist/identity/ucan.js +273 -0
- package/dist/identity/ucan.js.map +1 -0
- package/dist/identity/webauthn.d.ts +87 -0
- package/dist/identity/webauthn.d.ts.map +1 -0
- package/dist/identity/webauthn.js +155 -0
- package/dist/identity/webauthn.js.map +1 -0
- package/dist/index.d.ts +116 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +85 -0
- package/dist/index.js.map +1 -0
- package/dist/network/index.d.ts +17 -0
- package/dist/network/index.d.ts.map +1 -0
- package/dist/network/index.js +10 -0
- package/dist/network/index.js.map +1 -0
- package/dist/network/introductions.d.ts +85 -0
- package/dist/network/introductions.d.ts.map +1 -0
- package/dist/network/introductions.js +94 -0
- package/dist/network/introductions.js.map +1 -0
- package/dist/network/local-transport.d.ts +22 -0
- package/dist/network/local-transport.d.ts.map +1 -0
- package/dist/network/local-transport.js +55 -0
- package/dist/network/local-transport.js.map +1 -0
- package/dist/network/mesh.d.ts +31 -0
- package/dist/network/mesh.d.ts.map +1 -0
- package/dist/network/mesh.js +332 -0
- package/dist/network/mesh.js.map +1 -0
- package/dist/network/multi-signaling.d.ts +29 -0
- package/dist/network/multi-signaling.d.ts.map +1 -0
- package/dist/network/multi-signaling.js +125 -0
- package/dist/network/multi-signaling.js.map +1 -0
- package/dist/network/network-manager.d.ts +37 -0
- package/dist/network/network-manager.d.ts.map +1 -0
- package/dist/network/network-manager.js +64 -0
- package/dist/network/network-manager.js.map +1 -0
- package/dist/network/peer-auth.d.ts +98 -0
- package/dist/network/peer-auth.d.ts.map +1 -0
- package/dist/network/peer-auth.js +86 -0
- package/dist/network/peer-auth.js.map +1 -0
- package/dist/network/rtc-transport.d.ts +18 -0
- package/dist/network/rtc-transport.d.ts.map +1 -0
- package/dist/network/rtc-transport.js +149 -0
- package/dist/network/rtc-transport.js.map +1 -0
- package/dist/network/signaling.d.ts +40 -0
- package/dist/network/signaling.d.ts.map +1 -0
- package/dist/network/signaling.js +124 -0
- package/dist/network/signaling.js.map +1 -0
- package/dist/network/transport.d.ts +50 -0
- package/dist/network/transport.d.ts.map +1 -0
- package/dist/network/transport.js +13 -0
- package/dist/network/transport.js.map +1 -0
- package/dist/network/ws-transport.d.ts +42 -0
- package/dist/network/ws-transport.d.ts.map +1 -0
- package/dist/network/ws-transport.js +194 -0
- package/dist/network/ws-transport.js.map +1 -0
- package/dist/node/actions.d.ts +62 -0
- package/dist/node/actions.d.ts.map +1 -0
- package/dist/node/actions.js +500 -0
- package/dist/node/actions.js.map +1 -0
- package/dist/node/carrier.d.ts +85 -0
- package/dist/node/carrier.d.ts.map +1 -0
- package/dist/node/carrier.js +192 -0
- package/dist/node/carrier.js.map +1 -0
- package/dist/node/copy.d.ts +38 -0
- package/dist/node/copy.d.ts.map +1 -0
- package/dist/node/copy.js +53 -0
- package/dist/node/copy.js.map +1 -0
- package/dist/node/index.d.ts +16 -0
- package/dist/node/index.d.ts.map +1 -0
- package/dist/node/index.js +11 -0
- package/dist/node/index.js.map +1 -0
- package/dist/node/node.d.ts +12 -0
- package/dist/node/node.d.ts.map +1 -0
- package/dist/node/node.js +872 -0
- package/dist/node/node.js.map +1 -0
- package/dist/node/space-runtime.d.ts +144 -0
- package/dist/node/space-runtime.d.ts.map +1 -0
- package/dist/node/space-runtime.js +1221 -0
- package/dist/node/space-runtime.js.map +1 -0
- package/dist/node/stores.d.ts +45 -0
- package/dist/node/stores.d.ts.map +1 -0
- package/dist/node/stores.js +31 -0
- package/dist/node/stores.js.map +1 -0
- package/dist/node/types.d.ts +508 -0
- package/dist/node/types.d.ts.map +1 -0
- package/dist/node/types.js +2 -0
- package/dist/node/types.js.map +1 -0
- package/dist/privacy/index.d.ts +4 -0
- package/dist/privacy/index.d.ts.map +1 -0
- package/dist/privacy/index.js +4 -0
- package/dist/privacy/index.js.map +1 -0
- package/dist/privacy/key-distribution.d.ts +42 -0
- package/dist/privacy/key-distribution.d.ts.map +1 -0
- package/dist/privacy/key-distribution.js +50 -0
- package/dist/privacy/key-distribution.js.map +1 -0
- package/dist/privacy/privacy-guard.d.ts +24 -0
- package/dist/privacy/privacy-guard.d.ts.map +1 -0
- package/dist/privacy/privacy-guard.js +87 -0
- package/dist/privacy/privacy-guard.js.map +1 -0
- package/dist/privacy/space-encryption.d.ts +48 -0
- package/dist/privacy/space-encryption.d.ts.map +1 -0
- package/dist/privacy/space-encryption.js +61 -0
- package/dist/privacy/space-encryption.js.map +1 -0
- package/dist/query/engine.d.ts +27 -0
- package/dist/query/engine.d.ts.map +1 -0
- package/dist/query/engine.js +84 -0
- package/dist/query/engine.js.map +1 -0
- package/dist/query/filter.d.ts +15 -0
- package/dist/query/filter.d.ts.map +1 -0
- package/dist/query/filter.js +207 -0
- package/dist/query/filter.js.map +1 -0
- package/dist/query/types.d.ts +144 -0
- package/dist/query/types.d.ts.map +1 -0
- package/dist/query/types.js +22 -0
- package/dist/query/types.js.map +1 -0
- package/dist/react/context.d.ts +81 -0
- package/dist/react/context.d.ts.map +1 -0
- package/dist/react/context.js +95 -0
- package/dist/react/context.js.map +1 -0
- package/dist/react/index.d.ts +40 -0
- package/dist/react/index.d.ts.map +1 -0
- package/dist/react/index.js +36 -0
- package/dist/react/index.js.map +1 -0
- package/dist/react/use-live.d.ts +16 -0
- package/dist/react/use-live.d.ts.map +1 -0
- package/dist/react/use-live.js +55 -0
- package/dist/react/use-live.js.map +1 -0
- package/dist/react/use-query.d.ts +17 -0
- package/dist/react/use-query.d.ts.map +1 -0
- package/dist/react/use-query.js +23 -0
- package/dist/react/use-query.js.map +1 -0
- package/dist/react/use-space.d.ts +29 -0
- package/dist/react/use-space.d.ts.map +1 -0
- package/dist/react/use-space.js +48 -0
- package/dist/react/use-space.js.map +1 -0
- package/dist/react/use-spaces.d.ts +19 -0
- package/dist/react/use-spaces.d.ts.map +1 -0
- package/dist/react/use-spaces.js +55 -0
- package/dist/react/use-spaces.js.map +1 -0
- package/dist/react/use-weave-auth.d.ts +10 -0
- package/dist/react/use-weave-auth.d.ts.map +1 -0
- package/dist/react/use-weave-auth.js +15 -0
- package/dist/react/use-weave-auth.js.map +1 -0
- package/dist/react/weave-auth.d.ts +17 -0
- package/dist/react/weave-auth.d.ts.map +1 -0
- package/dist/react/weave-auth.js +38 -0
- package/dist/react/weave-auth.js.map +1 -0
- package/dist/records/describe.d.ts +38 -0
- package/dist/records/describe.d.ts.map +1 -0
- package/dist/records/describe.js +101 -0
- package/dist/records/describe.js.map +1 -0
- package/dist/records/links.d.ts +14 -0
- package/dist/records/links.d.ts.map +1 -0
- package/dist/records/links.js +25 -0
- package/dist/records/links.js.map +1 -0
- package/dist/records/rules.d.ts +59 -0
- package/dist/records/rules.d.ts.map +1 -0
- package/dist/records/rules.js +118 -0
- package/dist/records/rules.js.map +1 -0
- package/dist/records/version.d.ts +54 -0
- package/dist/records/version.d.ts.map +1 -0
- package/dist/records/version.js +70 -0
- package/dist/records/version.js.map +1 -0
- package/dist/schema/collection-def.d.ts +96 -0
- package/dist/schema/collection-def.d.ts.map +1 -0
- package/dist/schema/collection-def.js +272 -0
- package/dist/schema/collection-def.js.map +1 -0
- package/dist/schema/expression.d.ts +69 -0
- package/dist/schema/expression.d.ts.map +1 -0
- package/dist/schema/expression.js +92 -0
- package/dist/schema/expression.js.map +1 -0
- package/dist/schema/index.d.ts +4 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +4 -0
- package/dist/schema/index.js.map +1 -0
- package/dist/schema/schema-engine.d.ts +42 -0
- package/dist/schema/schema-engine.d.ts.map +1 -0
- package/dist/schema/schema-engine.js +36 -0
- package/dist/schema/schema-engine.js.map +1 -0
- package/dist/schema/signer.d.ts +27 -0
- package/dist/schema/signer.d.ts.map +1 -0
- package/dist/schema/signer.js +36 -0
- package/dist/schema/signer.js.map +1 -0
- package/dist/schemas/apps.d.ts +88 -0
- package/dist/schemas/apps.d.ts.map +1 -0
- package/dist/schemas/apps.js +168 -0
- package/dist/schemas/apps.js.map +1 -0
- package/dist/schemas/index.d.ts +410 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +208 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/screens.d.ts +67 -0
- package/dist/schemas/screens.d.ts.map +1 -0
- package/dist/schemas/screens.js +242 -0
- package/dist/schemas/screens.js.map +1 -0
- package/dist/session/agent-link.d.ts +79 -0
- package/dist/session/agent-link.d.ts.map +1 -0
- package/dist/session/agent-link.js +251 -0
- package/dist/session/agent-link.js.map +1 -0
- package/dist/session/auth.d.ts +233 -0
- package/dist/session/auth.d.ts.map +1 -0
- package/dist/session/auth.js +783 -0
- package/dist/session/auth.js.map +1 -0
- package/dist/session/connect.d.ts +211 -0
- package/dist/session/connect.d.ts.map +1 -0
- package/dist/session/connect.js +355 -0
- package/dist/session/connect.js.map +1 -0
- package/dist/session/connection.d.ts +70 -0
- package/dist/session/connection.d.ts.map +1 -0
- package/dist/session/connection.js +120 -0
- package/dist/session/connection.js.map +1 -0
- package/dist/session/credentials.d.ts +44 -0
- package/dist/session/credentials.d.ts.map +1 -0
- package/dist/session/credentials.js +60 -0
- package/dist/session/credentials.js.map +1 -0
- package/dist/session/index.d.ts +25 -0
- package/dist/session/index.d.ts.map +1 -0
- package/dist/session/index.js +18 -0
- package/dist/session/index.js.map +1 -0
- package/dist/session/pairing.d.ts +59 -0
- package/dist/session/pairing.d.ts.map +1 -0
- package/dist/session/pairing.js +144 -0
- package/dist/session/pairing.js.map +1 -0
- package/dist/session/places.d.ts +62 -0
- package/dist/session/places.d.ts.map +1 -0
- package/dist/session/places.js +101 -0
- package/dist/session/places.js.map +1 -0
- package/dist/session/stay-signed-in.d.ts +37 -0
- package/dist/session/stay-signed-in.d.ts.map +1 -0
- package/dist/session/stay-signed-in.js +124 -0
- package/dist/session/stay-signed-in.js.map +1 -0
- package/dist/space/account-registry.d.ts +64 -0
- package/dist/space/account-registry.d.ts.map +1 -0
- package/dist/space/account-registry.js +66 -0
- package/dist/space/account-registry.js.map +1 -0
- package/dist/space/index.d.ts +3 -0
- package/dist/space/index.d.ts.map +1 -0
- package/dist/space/index.js +3 -0
- package/dist/space/index.js.map +1 -0
- package/dist/space/pass.d.ts +63 -0
- package/dist/space/pass.d.ts.map +1 -0
- package/dist/space/pass.js +54 -0
- package/dist/space/pass.js.map +1 -0
- package/dist/space/presets.d.ts +31 -0
- package/dist/space/presets.d.ts.map +1 -0
- package/dist/space/presets.js +24 -0
- package/dist/space/presets.js.map +1 -0
- package/dist/space/roles.d.ts +186 -0
- package/dist/space/roles.d.ts.map +1 -0
- package/dist/space/roles.js +500 -0
- package/dist/space/roles.js.map +1 -0
- package/dist/space/space-access.d.ts +80 -0
- package/dist/space/space-access.d.ts.map +1 -0
- package/dist/space/space-access.js +135 -0
- package/dist/space/space-access.js.map +1 -0
- package/dist/space/space-manager.d.ts +87 -0
- package/dist/space/space-manager.d.ts.map +1 -0
- package/dist/space/space-manager.js +187 -0
- package/dist/space/space-manager.js.map +1 -0
- package/dist/storage/directory-access.d.ts +78 -0
- package/dist/storage/directory-access.d.ts.map +1 -0
- package/dist/storage/directory-access.js +164 -0
- package/dist/storage/directory-access.js.map +1 -0
- package/dist/storage/encrypted-adapter.d.ts +49 -0
- package/dist/storage/encrypted-adapter.d.ts.map +1 -0
- package/dist/storage/encrypted-adapter.js +106 -0
- package/dist/storage/encrypted-adapter.js.map +1 -0
- package/dist/storage/folder-adapter.d.ts +105 -0
- package/dist/storage/folder-adapter.d.ts.map +1 -0
- package/dist/storage/folder-adapter.js +280 -0
- package/dist/storage/folder-adapter.js.map +1 -0
- package/dist/storage/folder-reconcile.d.ts +53 -0
- package/dist/storage/folder-reconcile.d.ts.map +1 -0
- package/dist/storage/folder-reconcile.js +65 -0
- package/dist/storage/folder-reconcile.js.map +1 -0
- package/dist/storage/index.d.ts +10 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +9 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/indexeddb-adapter.d.ts +12 -0
- package/dist/storage/indexeddb-adapter.d.ts.map +1 -0
- package/dist/storage/indexeddb-adapter.js +190 -0
- package/dist/storage/indexeddb-adapter.js.map +1 -0
- package/dist/storage/mst.d.ts +121 -0
- package/dist/storage/mst.d.ts.map +1 -0
- package/dist/storage/mst.js +402 -0
- package/dist/storage/mst.js.map +1 -0
- package/dist/storage/storage-provider.d.ts +85 -0
- package/dist/storage/storage-provider.d.ts.map +1 -0
- package/dist/storage/storage-provider.js +220 -0
- package/dist/storage/storage-provider.js.map +1 -0
- package/dist/sync/anti-entropy.d.ts +49 -0
- package/dist/sync/anti-entropy.d.ts.map +1 -0
- package/dist/sync/anti-entropy.js +62 -0
- package/dist/sync/anti-entropy.js.map +1 -0
- package/dist/sync/index.d.ts +4 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +4 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/sync-engine.d.ts +71 -0
- package/dist/sync/sync-engine.d.ts.map +1 -0
- package/dist/sync/sync-engine.js +309 -0
- package/dist/sync/sync-engine.js.map +1 -0
- package/dist/sync/sync-messages.d.ts +75 -0
- package/dist/sync/sync-messages.d.ts.map +1 -0
- package/dist/sync/sync-messages.js +12 -0
- package/dist/sync/sync-messages.js.map +1 -0
- package/dist/types.d.ts +243 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/encoding.d.ts +63 -0
- package/dist/utils/encoding.d.ts.map +1 -0
- package/dist/utils/encoding.js +130 -0
- package/dist/utils/encoding.js.map +1 -0
- package/dist/utils/errors.d.ts +43 -0
- package/dist/utils/errors.d.ts.map +1 -0
- package/dist/utils/errors.js +28 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/events.d.ts +19 -0
- package/dist/utils/events.d.ts.map +1 -0
- package/dist/utils/events.js +27 -0
- package/dist/utils/events.js.map +1 -0
- package/dist/utils/hash.d.ts +23 -0
- package/dist/utils/hash.d.ts.map +1 -0
- package/dist/utils/hash.js +49 -0
- package/dist/utils/hash.js.map +1 -0
- package/dist/utils/index.d.ts +9 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +9 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/validation/capability-gate.d.ts +50 -0
- package/dist/validation/capability-gate.d.ts.map +1 -0
- package/dist/validation/capability-gate.js +71 -0
- package/dist/validation/capability-gate.js.map +1 -0
- package/dist/validation/crypto-gate.d.ts +16 -0
- package/dist/validation/crypto-gate.d.ts.map +1 -0
- package/dist/validation/crypto-gate.js +37 -0
- package/dist/validation/crypto-gate.js.map +1 -0
- package/dist/validation/index.d.ts +6 -0
- package/dist/validation/index.d.ts.map +1 -0
- package/dist/validation/index.js +6 -0
- package/dist/validation/index.js.map +1 -0
- package/dist/validation/stateful-gate.d.ts +15 -0
- package/dist/validation/stateful-gate.d.ts.map +1 -0
- package/dist/validation/stateful-gate.js +50 -0
- package/dist/validation/stateful-gate.js.map +1 -0
- package/dist/validation/structural-gate.d.ts +18 -0
- package/dist/validation/structural-gate.d.ts.map +1 -0
- package/dist/validation/structural-gate.js +54 -0
- package/dist/validation/structural-gate.js.map +1 -0
- package/dist/validation/validation-engine.d.ts +28 -0
- package/dist/validation/validation-engine.d.ts.map +1 -0
- package/dist/validation/validation-engine.js +38 -0
- package/dist/validation/validation-engine.js.map +1 -0
- package/package.json +107 -0
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module directory-access
|
|
3
|
+
* Getting hold of the user's data folder, and getting it back on the next visit.
|
|
4
|
+
*
|
|
5
|
+
* A directory handle is the one capability a web page can hold that outlives the
|
|
6
|
+
* origin sandbox, so the browser guards it carefully: it can only be obtained
|
|
7
|
+
* from a user gesture, and permission has to be re-confirmed when a new session
|
|
8
|
+
* starts. The handle itself is structured-cloneable, which means it can be kept
|
|
9
|
+
* in IndexedDB — origin-local storage of a *pointer* to origin-independent data.
|
|
10
|
+
*/
|
|
11
|
+
import { protocolError } from '../utils/errors.js';
|
|
12
|
+
const HANDLE_DB = 'weave-folder';
|
|
13
|
+
const HANDLE_STORE = 'handles';
|
|
14
|
+
const HANDLE_KEY = 'data-folder';
|
|
15
|
+
/**
|
|
16
|
+
* Whether this browser can hand out a directory at all.
|
|
17
|
+
*
|
|
18
|
+
* Chrome, Edge and Opera on the desktop can. Firefox and Safari cannot, and
|
|
19
|
+
* neither can any mobile browser, so a caller has to have something else to
|
|
20
|
+
* offer — this returning false is a normal state, not a broken one.
|
|
21
|
+
*
|
|
22
|
+
* @returns Whether {@link pickDataFolder} will work here
|
|
23
|
+
*/
|
|
24
|
+
export function isFolderStorageAvailable() {
|
|
25
|
+
return typeof globalThis.showDirectoryPicker === 'function';
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Asks the user to choose their data folder. Must be called from a click.
|
|
29
|
+
*
|
|
30
|
+
* @param options.id Groups the picker's memory of where it last opened. Keeping
|
|
31
|
+
* this stable across apps means the second view of the same data opens the
|
|
32
|
+
* picker already pointing at the right folder.
|
|
33
|
+
* @returns The chosen directory
|
|
34
|
+
*/
|
|
35
|
+
export async function pickDataFolder(options) {
|
|
36
|
+
const picker = globalThis.showDirectoryPicker;
|
|
37
|
+
if (!picker) {
|
|
38
|
+
throw protocolError('FOLDER_UNAVAILABLE', 'This browser cannot open a data folder.', 'The File System Access API is available in Chrome, Edge and Opera on the ' +
|
|
39
|
+
'desktop. Elsewhere, sign in with a recovery code and this device will ' +
|
|
40
|
+
'keep its own copy that syncs with your peers.');
|
|
41
|
+
}
|
|
42
|
+
return picker({ id: options?.id ?? 'weave-pod', mode: 'readwrite', startIn: 'documents' });
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Asks what this origin is currently allowed to do with a folder.
|
|
46
|
+
* @param handle The directory in question
|
|
47
|
+
* @param mode The access being asked about
|
|
48
|
+
* @returns `granted`, `denied`, or `prompt` when the user has yet to be asked
|
|
49
|
+
*/
|
|
50
|
+
export async function queryFolderPermission(handle, mode = 'readwrite') {
|
|
51
|
+
const query = handle.queryPermission;
|
|
52
|
+
if (!query)
|
|
53
|
+
return 'granted'; // a handle from a runtime without the extension
|
|
54
|
+
return query.call(handle, { mode });
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Makes sure this origin may use a folder, prompting if it has to.
|
|
58
|
+
*
|
|
59
|
+
* The prompt needs a user gesture, so `request` should only be true on a path
|
|
60
|
+
* that began with a click — call it with `false` on page load to find out
|
|
61
|
+
* whether a button needs showing at all.
|
|
62
|
+
*
|
|
63
|
+
* @param handle The directory to check
|
|
64
|
+
* @param options.request Whether to prompt when permission is not already given
|
|
65
|
+
* @param options.mode The access needed
|
|
66
|
+
* @returns Whether the folder is usable
|
|
67
|
+
*/
|
|
68
|
+
export async function ensureFolderPermission(handle, options) {
|
|
69
|
+
const mode = options?.mode ?? 'readwrite';
|
|
70
|
+
if ((await queryFolderPermission(handle, mode)) === 'granted')
|
|
71
|
+
return true;
|
|
72
|
+
if (!options?.request)
|
|
73
|
+
return false;
|
|
74
|
+
const request = handle.requestPermission;
|
|
75
|
+
if (!request)
|
|
76
|
+
return false;
|
|
77
|
+
return (await request.call(handle, { mode })) === 'granted';
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Opens the tiny database that remembers which folder this origin was pointed at.
|
|
81
|
+
* @returns The database
|
|
82
|
+
*/
|
|
83
|
+
function openHandleDb() {
|
|
84
|
+
return new Promise((resolve, reject) => {
|
|
85
|
+
const request = globalThis.indexedDB.open(HANDLE_DB, 1);
|
|
86
|
+
request.onupgradeneeded = () => {
|
|
87
|
+
if (!request.result.objectStoreNames.contains(HANDLE_STORE)) {
|
|
88
|
+
request.result.createObjectStore(HANDLE_STORE);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
request.onsuccess = () => resolve(request.result);
|
|
92
|
+
request.onerror = () => reject(request.error);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Remembers a folder so the next visit can skip the picker.
|
|
97
|
+
*
|
|
98
|
+
* Only the handle is stored, never the contents — this origin keeps a pointer,
|
|
99
|
+
* and the data stays where the user put it.
|
|
100
|
+
*
|
|
101
|
+
* @param handle The directory to remember
|
|
102
|
+
*/
|
|
103
|
+
export async function rememberDataFolder(handle) {
|
|
104
|
+
const db = await openHandleDb();
|
|
105
|
+
try {
|
|
106
|
+
await new Promise((resolve, reject) => {
|
|
107
|
+
const tx = db.transaction(HANDLE_STORE, 'readwrite');
|
|
108
|
+
tx.objectStore(HANDLE_STORE).put(handle, HANDLE_KEY);
|
|
109
|
+
tx.oncomplete = () => resolve();
|
|
110
|
+
tx.onerror = () => reject(tx.error);
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
finally {
|
|
114
|
+
db.close();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* The folder this origin used last, if the browser still has it.
|
|
119
|
+
*
|
|
120
|
+
* Says nothing about permission — a recalled handle usually needs
|
|
121
|
+
* {@link ensureFolderPermission} with a gesture before it can be read.
|
|
122
|
+
*
|
|
123
|
+
* @returns The remembered directory, or null
|
|
124
|
+
*/
|
|
125
|
+
export async function recallDataFolder() {
|
|
126
|
+
try {
|
|
127
|
+
const db = await openHandleDb();
|
|
128
|
+
try {
|
|
129
|
+
return await new Promise((resolve, reject) => {
|
|
130
|
+
const tx = db.transaction(HANDLE_STORE, 'readonly');
|
|
131
|
+
const request = tx.objectStore(HANDLE_STORE).get(HANDLE_KEY);
|
|
132
|
+
request.onsuccess = () => resolve(request.result ?? null);
|
|
133
|
+
request.onerror = () => reject(request.error);
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
finally {
|
|
137
|
+
db.close();
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/** Forgets the remembered folder. The folder and everything in it stay put. */
|
|
145
|
+
export async function forgetDataFolder() {
|
|
146
|
+
try {
|
|
147
|
+
const db = await openHandleDb();
|
|
148
|
+
try {
|
|
149
|
+
await new Promise((resolve, reject) => {
|
|
150
|
+
const tx = db.transaction(HANDLE_STORE, 'readwrite');
|
|
151
|
+
tx.objectStore(HANDLE_STORE).delete(HANDLE_KEY);
|
|
152
|
+
tx.oncomplete = () => resolve();
|
|
153
|
+
tx.onerror = () => reject(tx.error);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
finally {
|
|
157
|
+
db.close();
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
catch {
|
|
161
|
+
// Nothing to forget.
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=directory-access.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"directory-access.js","sourceRoot":"","sources":["../../src/storage/directory-access.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module encrypted-adapter
|
|
3
|
+
* Encryption at rest for the parts of a store that would otherwise give the
|
|
4
|
+
* whole folder away.
|
|
5
|
+
*
|
|
6
|
+
* A private space encrypts every body before signing, which protects it from
|
|
7
|
+
* the relay and from peers who were never invited. It does not protect it from
|
|
8
|
+
* anyone holding the folder, because the AES key for that space is written into
|
|
9
|
+
* the same store as the ciphertext it opens. This closes that: the space key and
|
|
10
|
+
* the space record go in sealed under a key derived from the account seed, so
|
|
11
|
+
* they are readable only once the folder has actually been unlocked.
|
|
12
|
+
*
|
|
13
|
+
* Scoped deliberately narrowly. Expressions and MST nodes pass straight through:
|
|
14
|
+
* their bodies are already encrypted where it matters, they are content
|
|
15
|
+
* addressed, and sealing them would cost the property that makes a folder worth
|
|
16
|
+
* having — that you can open it and see what is in it. What stays legible is
|
|
17
|
+
* each record's author, timestamp and collection, plus anything in a space its
|
|
18
|
+
* owner made public.
|
|
19
|
+
*/
|
|
20
|
+
import type { StorageAdapter } from '../types.js';
|
|
21
|
+
/**
|
|
22
|
+
* What gets sealed by default: the space registry — each space, its key, and
|
|
23
|
+
* the write secret of a shared one — and nothing else.
|
|
24
|
+
*
|
|
25
|
+
* A prefix match, so each must end in its colon: `space:` does not cover
|
|
26
|
+
* `spaceinvite:`, and an invite secret left out here is anyone-with-the-folder
|
|
27
|
+
* joining a space in your place.
|
|
28
|
+
*/
|
|
29
|
+
export declare const DEFAULT_ENCRYPTED_PREFIXES: ReadonlyArray<string>;
|
|
30
|
+
export interface EncryptedAdapterOptions {
|
|
31
|
+
/** Key prefixes whose values are sealed. Defaults to {@link DEFAULT_ENCRYPTED_PREFIXES}. */
|
|
32
|
+
readonly prefixes?: ReadonlyArray<string>;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Wraps a storage adapter so that selected values are encrypted on the way in
|
|
36
|
+
* and decrypted on the way out.
|
|
37
|
+
*
|
|
38
|
+
* Each value is bound to its storage key (it is the AES-GCM additional data),
|
|
39
|
+
* so sealed values cannot be swapped between entries. A value under a sealed
|
|
40
|
+
* prefix that was not sealed is refused, not trusted: anyone who can write the
|
|
41
|
+
* folder could otherwise plant a space, or a key, in the clear.
|
|
42
|
+
*
|
|
43
|
+
* @param inner The adapter doing the actual storing
|
|
44
|
+
* @param key An AES-GCM key, from `deriveVaultKey`
|
|
45
|
+
* @param options Which keys to seal
|
|
46
|
+
* @returns An adapter with the same contract
|
|
47
|
+
*/
|
|
48
|
+
export declare function createEncryptedAdapter(inner: StorageAdapter, key: CryptoKey, options?: EncryptedAdapterOptions): StorageAdapter;
|
|
49
|
+
//# sourceMappingURL=encrypted-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encrypted-adapter.d.ts","sourceRoot":"","sources":["../../src/storage/encrypted-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAW,MAAM,aAAa,CAAC;AAa3D;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,EAAE,aAAa,CAAC,MAAM,CAAyD,CAAC;AAEvH,MAAM,WAAW,uBAAuB;IACtC,4FAA4F;IAC5F,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAC3C;AAQD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,SAAS,EACd,OAAO,CAAC,EAAE,uBAAuB,GAChC,cAAc,CAoEhB"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module encrypted-adapter
|
|
3
|
+
* Encryption at rest for the parts of a store that would otherwise give the
|
|
4
|
+
* whole folder away.
|
|
5
|
+
*
|
|
6
|
+
* A private space encrypts every body before signing, which protects it from
|
|
7
|
+
* the relay and from peers who were never invited. It does not protect it from
|
|
8
|
+
* anyone holding the folder, because the AES key for that space is written into
|
|
9
|
+
* the same store as the ciphertext it opens. This closes that: the space key and
|
|
10
|
+
* the space record go in sealed under a key derived from the account seed, so
|
|
11
|
+
* they are readable only once the folder has actually been unlocked.
|
|
12
|
+
*
|
|
13
|
+
* Scoped deliberately narrowly. Expressions and MST nodes pass straight through:
|
|
14
|
+
* their bodies are already encrypted where it matters, they are content
|
|
15
|
+
* addressed, and sealing them would cost the property that makes a folder worth
|
|
16
|
+
* having — that you can open it and see what is in it. What stays legible is
|
|
17
|
+
* each record's author, timestamp and collection, plus anything in a space its
|
|
18
|
+
* owner made public.
|
|
19
|
+
*/
|
|
20
|
+
import { concatBytes } from '../utils/encoding.js';
|
|
21
|
+
/**
|
|
22
|
+
* Marks a value this module wrote.
|
|
23
|
+
*
|
|
24
|
+
* Without it there is no way to tell ciphertext from a value stored before the
|
|
25
|
+
* folder had a lock, and "try to decrypt, fall back to raw on failure" turns
|
|
26
|
+
* every genuine key mismatch into silently wrong data.
|
|
27
|
+
*/
|
|
28
|
+
const MAGIC = new Uint8Array([0x77, 0x65, 0x61, 0x76, 0x65, 0x65, 0x02, 0x00]); // "weavee\x02\x00" — Weave, encrypted, v2
|
|
29
|
+
const IV_BYTES = 12;
|
|
30
|
+
/**
|
|
31
|
+
* What gets sealed by default: the space registry — each space, its key, and
|
|
32
|
+
* the write secret of a shared one — and nothing else.
|
|
33
|
+
*
|
|
34
|
+
* A prefix match, so each must end in its colon: `space:` does not cover
|
|
35
|
+
* `spaceinvite:`, and an invite secret left out here is anyone-with-the-folder
|
|
36
|
+
* joining a space in your place.
|
|
37
|
+
*/
|
|
38
|
+
export const DEFAULT_ENCRYPTED_PREFIXES = ['space:', 'spacekey:', 'spaceinvite:', 'spacerole:'];
|
|
39
|
+
/** Whether these bytes were written sealed. */
|
|
40
|
+
function isSealed(bytes) {
|
|
41
|
+
if (bytes.length < MAGIC.length + IV_BYTES)
|
|
42
|
+
return false;
|
|
43
|
+
return MAGIC.every((byte, index) => bytes[index] === byte);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Wraps a storage adapter so that selected values are encrypted on the way in
|
|
47
|
+
* and decrypted on the way out.
|
|
48
|
+
*
|
|
49
|
+
* Each value is bound to its storage key (it is the AES-GCM additional data),
|
|
50
|
+
* so sealed values cannot be swapped between entries. A value under a sealed
|
|
51
|
+
* prefix that was not sealed is refused, not trusted: anyone who can write the
|
|
52
|
+
* folder could otherwise plant a space, or a key, in the clear.
|
|
53
|
+
*
|
|
54
|
+
* @param inner The adapter doing the actual storing
|
|
55
|
+
* @param key An AES-GCM key, from `deriveVaultKey`
|
|
56
|
+
* @param options Which keys to seal
|
|
57
|
+
* @returns An adapter with the same contract
|
|
58
|
+
*/
|
|
59
|
+
export function createEncryptedAdapter(inner, key, options) {
|
|
60
|
+
const prefixes = options?.prefixes ?? DEFAULT_ENCRYPTED_PREFIXES;
|
|
61
|
+
const shouldSeal = (storageKey) => prefixes.some((prefix) => storageKey.startsWith(prefix));
|
|
62
|
+
const bound = (storageKey) => new TextEncoder().encode(storageKey);
|
|
63
|
+
async function seal(storageKey, value) {
|
|
64
|
+
const iv = globalThis.crypto.getRandomValues(new Uint8Array(IV_BYTES));
|
|
65
|
+
const ciphertext = await globalThis.crypto.subtle.encrypt({ name: 'AES-GCM', iv: iv, additionalData: bound(storageKey) }, key, value);
|
|
66
|
+
return concatBytes(MAGIC, iv, new Uint8Array(ciphertext));
|
|
67
|
+
}
|
|
68
|
+
async function unseal(storageKey, bytes) {
|
|
69
|
+
if (!isSealed(bytes))
|
|
70
|
+
throw new Error(`${storageKey} should be sealed, and is not — refusing it`);
|
|
71
|
+
const iv = bytes.slice(MAGIC.length, MAGIC.length + IV_BYTES);
|
|
72
|
+
const ciphertext = bytes.slice(MAGIC.length + IV_BYTES);
|
|
73
|
+
const plain = await globalThis.crypto.subtle.decrypt({ name: 'AES-GCM', iv: iv, additionalData: bound(storageKey) }, key, ciphertext);
|
|
74
|
+
return new Uint8Array(plain);
|
|
75
|
+
}
|
|
76
|
+
return Object.freeze({
|
|
77
|
+
async get(storageKey) {
|
|
78
|
+
const bytes = await inner.get(storageKey);
|
|
79
|
+
if (!bytes || !shouldSeal(storageKey))
|
|
80
|
+
return bytes;
|
|
81
|
+
return unseal(storageKey, bytes);
|
|
82
|
+
},
|
|
83
|
+
async put(storageKey, value) {
|
|
84
|
+
return inner.put(storageKey, shouldSeal(storageKey) ? await seal(storageKey, value) : value);
|
|
85
|
+
},
|
|
86
|
+
async batch(ops) {
|
|
87
|
+
const prepared = await Promise.all(ops.map(async (op) => op.type === 'put' && shouldSeal(op.key)
|
|
88
|
+
? { type: 'put', key: op.key, value: await seal(op.key, op.value) }
|
|
89
|
+
: op));
|
|
90
|
+
return inner.batch(prepared);
|
|
91
|
+
},
|
|
92
|
+
// Key names are not sealed, so listing, deleting and existence checks are
|
|
93
|
+
// unchanged. A space id is a content hash and gives nothing away; its name
|
|
94
|
+
// and its key live in the value, which is sealed.
|
|
95
|
+
delete: (storageKey) => inner.delete(storageKey),
|
|
96
|
+
has: (storageKey) => inner.has(storageKey),
|
|
97
|
+
list: (prefix) => inner.list(prefix),
|
|
98
|
+
// Expressions carry their own encryption where the space calls for it.
|
|
99
|
+
queryExpressions: (collection, limit, cursor) => inner.queryExpressions(collection, limit, cursor),
|
|
100
|
+
putExpression: inner.putExpression.bind(inner),
|
|
101
|
+
getExpression: (id) => inner.getExpression(id),
|
|
102
|
+
deleteExpression: (id) => inner.deleteExpression(id),
|
|
103
|
+
close: () => inner.close(),
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
//# sourceMappingURL=encrypted-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encrypted-adapter.js","sourceRoot":"","sources":["../../src/storage/encrypted-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module folder-adapter
|
|
3
|
+
* A StorageAdapter over a directory the user picked on their own disk.
|
|
4
|
+
*
|
|
5
|
+
* This is the store that escapes the origin sandbox. IndexedDB, localStorage and
|
|
6
|
+
* OPFS ("Origin Private File System" — the name is the specification) are all
|
|
7
|
+
* keyed by origin, so two deployments of the same app can never see each other's
|
|
8
|
+
* data. A directory handle is not: each origin asks the user for permission once
|
|
9
|
+
* and both end up reading the same files. That is what makes an app a *view* on
|
|
10
|
+
* the user's data rather than an owner of a private copy of it.
|
|
11
|
+
*
|
|
12
|
+
* The layout is meant to be legible, because the point is that the folder
|
|
13
|
+
* belongs to the user:
|
|
14
|
+
*
|
|
15
|
+
* ```
|
|
16
|
+
* <folder>/
|
|
17
|
+
* accounts.json who has an account here
|
|
18
|
+
* accounts/<id>/account.json that account's keys, locked
|
|
19
|
+
* accounts/<id>/stores/<name>/
|
|
20
|
+
* kv/<key> MST nodes, the root pointer, space records
|
|
21
|
+
* expressions/<cid>.json
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* **Expressions are the truth; the MST is an index over them.** That inversion
|
|
25
|
+
* is what lets two origins share one folder with no locking. Every expression
|
|
26
|
+
* file is immutable and named by its own content hash, so concurrent writers can
|
|
27
|
+
* only ever add files that agree — the one genuinely mutable thing, the MST root
|
|
28
|
+
* pointer, is derived state that either side can rebuild. {@link reconcileFolder}
|
|
29
|
+
* is what rebuilds it.
|
|
30
|
+
*/
|
|
31
|
+
import type { StorageAdapter } from '../types.js';
|
|
32
|
+
export interface WritableFileLike {
|
|
33
|
+
write(data: Uint8Array | string): Promise<void>;
|
|
34
|
+
close(): Promise<void>;
|
|
35
|
+
}
|
|
36
|
+
export interface FileHandleLike {
|
|
37
|
+
getFile(): Promise<{
|
|
38
|
+
arrayBuffer(): Promise<ArrayBuffer>;
|
|
39
|
+
}>;
|
|
40
|
+
createWritable(): Promise<WritableFileLike>;
|
|
41
|
+
}
|
|
42
|
+
export interface DirectoryHandleLike {
|
|
43
|
+
readonly name: string;
|
|
44
|
+
getFileHandle(name: string, options?: {
|
|
45
|
+
create?: boolean;
|
|
46
|
+
}): Promise<FileHandleLike>;
|
|
47
|
+
getDirectoryHandle(name: string, options?: {
|
|
48
|
+
create?: boolean;
|
|
49
|
+
}): Promise<DirectoryHandleLike>;
|
|
50
|
+
removeEntry(name: string, options?: {
|
|
51
|
+
recursive?: boolean;
|
|
52
|
+
}): Promise<void>;
|
|
53
|
+
keys(): AsyncIterableIterator<string>;
|
|
54
|
+
}
|
|
55
|
+
/** What changed on disk since the last pass */
|
|
56
|
+
export interface FolderReload {
|
|
57
|
+
/** Expression ids that appeared — written by another origin, or another device */
|
|
58
|
+
readonly added: ReadonlyArray<string>;
|
|
59
|
+
/** Expression ids whose files are gone */
|
|
60
|
+
readonly removed: ReadonlyArray<string>;
|
|
61
|
+
}
|
|
62
|
+
export interface FolderAdapter extends StorageAdapter {
|
|
63
|
+
/** Where inside the folder this adapter keeps its files */
|
|
64
|
+
readonly namespace: string;
|
|
65
|
+
/**
|
|
66
|
+
* Re-reads the directory, picking up writes made by another origin or device.
|
|
67
|
+
*
|
|
68
|
+
* There is no filesystem change notification on the web, so freshness is
|
|
69
|
+
* something the caller asks for — on an interval, on focus, or after a sync
|
|
70
|
+
* round. Pair it with {@link reconcileFolder} to fold what turns up into the
|
|
71
|
+
* local tree.
|
|
72
|
+
*/
|
|
73
|
+
reload(): Promise<FolderReload>;
|
|
74
|
+
/** Every expression id currently on disk, whatever the MST believes */
|
|
75
|
+
listExpressionIds(): Promise<ReadonlyArray<string>>;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Reads a file, treating absence as null rather than as a failure.
|
|
79
|
+
* @param dir The directory holding it
|
|
80
|
+
* @param name The filename
|
|
81
|
+
* @returns Its bytes, or null when there is no such file
|
|
82
|
+
*/
|
|
83
|
+
export declare function readFolderFile(dir: DirectoryHandleLike, name: string): Promise<Uint8Array | null>;
|
|
84
|
+
/**
|
|
85
|
+
* Writes a file, replacing whatever was there.
|
|
86
|
+
* @param dir The directory to write into
|
|
87
|
+
* @param name The filename
|
|
88
|
+
* @param bytes The contents
|
|
89
|
+
*/
|
|
90
|
+
export declare function writeFolderFile(dir: DirectoryHandleLike, name: string, bytes: Uint8Array): Promise<void>;
|
|
91
|
+
/**
|
|
92
|
+
* Opens a storage adapter backed by a directory on the user's disk.
|
|
93
|
+
*
|
|
94
|
+
* Expressions are held in memory as well as on disk — a query would otherwise
|
|
95
|
+
* mean one file read per record, and the example app re-lists on every render.
|
|
96
|
+
* The cost is that the working set has to fit in memory.
|
|
97
|
+
*
|
|
98
|
+
* @param root The directory the user picked
|
|
99
|
+
* @param path Where to keep these files inside it, `/` separating
|
|
100
|
+
* subdirectories. The full path from the folder root, so that several
|
|
101
|
+
* accounts can each have a subtree of their own.
|
|
102
|
+
* @returns An adapter over that directory, already populated
|
|
103
|
+
*/
|
|
104
|
+
export declare function createFolderAdapter(root: DirectoryHandleLike, path: string): Promise<FolderAdapter>;
|
|
105
|
+
//# sourceMappingURL=folder-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folder-adapter.d.ts","sourceRoot":"","sources":["../../src/storage/folder-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAuB,MAAM,aAAa,CAAC;AAUvE,MAAM,WAAW,gBAAgB;IAC/B,KAAK,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,IAAI,OAAO,CAAC;QAAE,WAAW,IAAI,OAAO,CAAC,WAAW,CAAC,CAAA;KAAE,CAAC,CAAC;IAC5D,cAAc,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC;IACrF,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IAC/F,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,SAAS,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5E,IAAI,IAAI,qBAAqB,CAAC,MAAM,CAAC,CAAC;CACvC;AAED,+CAA+C;AAC/C,MAAM,WAAW,YAAY;IAC3B,kFAAkF;IAClF,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACtC,0CAA0C;IAC1C,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACzC;AAED,MAAM,WAAW,aAAc,SAAQ,cAAc;IACnD,2DAA2D;IAC3D,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B;;;;;;;OAOG;IACH,MAAM,IAAI,OAAO,CAAC,YAAY,CAAC,CAAC;IAChC,uEAAuE;IACvE,iBAAiB,IAAI,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC;CACrD;AAgED;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,mBAAmB,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CASvG;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,GAAG,EAAE,mBAAmB,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAK9G;AAeD;;;;;;;;;;;;GAYG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,mBAAmB,EACzB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,aAAa,CAAC,CAuJxB"}
|