@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,280 @@
|
|
|
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 { utf8Encode, utf8Decode } from '../utils/encoding.js';
|
|
32
|
+
const EXPRESSION_SUFFIX = '.json';
|
|
33
|
+
/**
|
|
34
|
+
* Percent-encodes a storage key into a filename.
|
|
35
|
+
*
|
|
36
|
+
* Only lowercase is left unescaped, which looks excessive until you remember
|
|
37
|
+
* that macOS and Windows filesystems are case-insensitive by default: without
|
|
38
|
+
* this, the keys `space:Abc` and `space:abc` would quietly become one file and
|
|
39
|
+
* one of the two values would be lost.
|
|
40
|
+
*
|
|
41
|
+
* @param key A storage key
|
|
42
|
+
* @returns A filename safe on every filesystem, and reversible
|
|
43
|
+
*/
|
|
44
|
+
function encodeKey(key) {
|
|
45
|
+
let out = '';
|
|
46
|
+
for (const byte of utf8Encode(key)) {
|
|
47
|
+
const char = String.fromCharCode(byte);
|
|
48
|
+
out += /[a-z0-9._-]/.test(char) ? char : `%${byte.toString(16).padStart(2, '0').toUpperCase()}`;
|
|
49
|
+
}
|
|
50
|
+
return out;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Reverses {@link encodeKey}.
|
|
54
|
+
* @param name A filename written by this adapter
|
|
55
|
+
* @returns The storage key it stands for
|
|
56
|
+
*/
|
|
57
|
+
function decodeKey(name) {
|
|
58
|
+
const bytes = [];
|
|
59
|
+
for (let i = 0; i < name.length; i++) {
|
|
60
|
+
if (name[i] === '%') {
|
|
61
|
+
bytes.push(Number.parseInt(name.slice(i + 1, i + 3), 16));
|
|
62
|
+
i += 2;
|
|
63
|
+
}
|
|
64
|
+
else {
|
|
65
|
+
bytes.push(name.charCodeAt(i));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return utf8Decode(new Uint8Array(bytes));
|
|
69
|
+
}
|
|
70
|
+
/** Whether a rejection means "that file isn't there", which is usually not an error. */
|
|
71
|
+
function isNotFound(error) {
|
|
72
|
+
return error instanceof Error && error.name === 'NotFoundError';
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Walks — and creates — a chain of subdirectories.
|
|
76
|
+
* @param root The directory to start from
|
|
77
|
+
* @param segments Path segments, already safe for a filename
|
|
78
|
+
* @returns The directory at the end of the path
|
|
79
|
+
*/
|
|
80
|
+
async function resolvePath(root, segments) {
|
|
81
|
+
let current = root;
|
|
82
|
+
for (const segment of segments) {
|
|
83
|
+
current = await current.getDirectoryHandle(segment, { create: true });
|
|
84
|
+
}
|
|
85
|
+
return current;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Reads a file, treating absence as null rather than as a failure.
|
|
89
|
+
* @param dir The directory holding it
|
|
90
|
+
* @param name The filename
|
|
91
|
+
* @returns Its bytes, or null when there is no such file
|
|
92
|
+
*/
|
|
93
|
+
export async function readFolderFile(dir, name) {
|
|
94
|
+
try {
|
|
95
|
+
const handle = await dir.getFileHandle(name);
|
|
96
|
+
const file = await handle.getFile();
|
|
97
|
+
return new Uint8Array(await file.arrayBuffer());
|
|
98
|
+
}
|
|
99
|
+
catch (error) {
|
|
100
|
+
if (isNotFound(error))
|
|
101
|
+
return null;
|
|
102
|
+
throw error;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Writes a file, replacing whatever was there.
|
|
107
|
+
* @param dir The directory to write into
|
|
108
|
+
* @param name The filename
|
|
109
|
+
* @param bytes The contents
|
|
110
|
+
*/
|
|
111
|
+
export async function writeFolderFile(dir, name, bytes) {
|
|
112
|
+
const handle = await dir.getFileHandle(name, { create: true });
|
|
113
|
+
const writable = await handle.createWritable();
|
|
114
|
+
await writable.write(bytes);
|
|
115
|
+
await writable.close();
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Deletes a file, ignoring one that is already gone.
|
|
119
|
+
* @param dir The directory holding it
|
|
120
|
+
* @param name The filename
|
|
121
|
+
*/
|
|
122
|
+
async function removeFile(dir, name) {
|
|
123
|
+
try {
|
|
124
|
+
await dir.removeEntry(name);
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
if (!isNotFound(error))
|
|
128
|
+
throw error;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Opens a storage adapter backed by a directory on the user's disk.
|
|
133
|
+
*
|
|
134
|
+
* Expressions are held in memory as well as on disk — a query would otherwise
|
|
135
|
+
* mean one file read per record, and the example app re-lists on every render.
|
|
136
|
+
* The cost is that the working set has to fit in memory.
|
|
137
|
+
*
|
|
138
|
+
* @param root The directory the user picked
|
|
139
|
+
* @param path Where to keep these files inside it, `/` separating
|
|
140
|
+
* subdirectories. The full path from the folder root, so that several
|
|
141
|
+
* accounts can each have a subtree of their own.
|
|
142
|
+
* @returns An adapter over that directory, already populated
|
|
143
|
+
*/
|
|
144
|
+
export async function createFolderAdapter(root, path) {
|
|
145
|
+
const segments = path.split('/').filter(Boolean).map(encodeKey);
|
|
146
|
+
if (segments.length === 0) {
|
|
147
|
+
throw new Error('A folder adapter needs a path to live in.');
|
|
148
|
+
}
|
|
149
|
+
const base = await resolvePath(root, segments);
|
|
150
|
+
const kvDir = await base.getDirectoryHandle('kv', { create: true });
|
|
151
|
+
const expressionsDir = await base.getDirectoryHandle('expressions', { create: true });
|
|
152
|
+
const expressions = new Map();
|
|
153
|
+
// Content-addressed nodes never change, so caching them is free. The root
|
|
154
|
+
// pointer does change, which is why `reload` throws the whole cache away
|
|
155
|
+
// rather than trying to be clever about which entries are still good.
|
|
156
|
+
const kvCache = new Map();
|
|
157
|
+
async function readExpressionFile(name) {
|
|
158
|
+
const bytes = await readFolderFile(expressionsDir, name);
|
|
159
|
+
if (!bytes)
|
|
160
|
+
return null;
|
|
161
|
+
try {
|
|
162
|
+
return JSON.parse(utf8Decode(bytes));
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
// A half-written file from a writer that died mid-flush. It will either be
|
|
166
|
+
// rewritten or stay unreadable; either way it is not ours to repair.
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
async function reload() {
|
|
171
|
+
const seen = new Set();
|
|
172
|
+
const added = [];
|
|
173
|
+
for await (const name of expressionsDir.keys()) {
|
|
174
|
+
if (!name.endsWith(EXPRESSION_SUFFIX))
|
|
175
|
+
continue;
|
|
176
|
+
const id = decodeKey(name.slice(0, -EXPRESSION_SUFFIX.length));
|
|
177
|
+
seen.add(id);
|
|
178
|
+
if (expressions.has(id))
|
|
179
|
+
continue;
|
|
180
|
+
const expression = await readExpressionFile(name);
|
|
181
|
+
if (!expression)
|
|
182
|
+
continue;
|
|
183
|
+
expressions.set(id, expression);
|
|
184
|
+
added.push(id);
|
|
185
|
+
}
|
|
186
|
+
const removed = [...expressions.keys()].filter((id) => !seen.has(id));
|
|
187
|
+
for (const id of removed)
|
|
188
|
+
expressions.delete(id);
|
|
189
|
+
kvCache.clear();
|
|
190
|
+
return { added, removed };
|
|
191
|
+
}
|
|
192
|
+
await reload();
|
|
193
|
+
return Object.freeze({
|
|
194
|
+
namespace: path,
|
|
195
|
+
reload,
|
|
196
|
+
async listExpressionIds() {
|
|
197
|
+
return [...expressions.keys()];
|
|
198
|
+
},
|
|
199
|
+
async get(key) {
|
|
200
|
+
const cached = kvCache.get(key);
|
|
201
|
+
if (cached !== undefined)
|
|
202
|
+
return cached;
|
|
203
|
+
const bytes = await readFolderFile(kvDir, encodeKey(key));
|
|
204
|
+
kvCache.set(key, bytes);
|
|
205
|
+
return bytes;
|
|
206
|
+
},
|
|
207
|
+
async put(key, value) {
|
|
208
|
+
await writeFolderFile(kvDir, encodeKey(key), value);
|
|
209
|
+
kvCache.set(key, value);
|
|
210
|
+
},
|
|
211
|
+
async delete(key) {
|
|
212
|
+
await removeFile(kvDir, encodeKey(key));
|
|
213
|
+
kvCache.set(key, null);
|
|
214
|
+
},
|
|
215
|
+
async has(key) {
|
|
216
|
+
const cached = kvCache.get(key);
|
|
217
|
+
if (cached !== undefined)
|
|
218
|
+
return cached !== null;
|
|
219
|
+
return (await readFolderFile(kvDir, encodeKey(key))) !== null;
|
|
220
|
+
},
|
|
221
|
+
async list(prefix = '') {
|
|
222
|
+
const keys = [];
|
|
223
|
+
for await (const name of kvDir.keys()) {
|
|
224
|
+
const key = decodeKey(name);
|
|
225
|
+
if (!key.startsWith(prefix))
|
|
226
|
+
continue;
|
|
227
|
+
keys.push(key);
|
|
228
|
+
// Another writer created it after this process looked and found
|
|
229
|
+
// nothing. The file is on disk now, so the remembered miss is stale.
|
|
230
|
+
if (kvCache.get(key) === null)
|
|
231
|
+
kvCache.delete(key);
|
|
232
|
+
}
|
|
233
|
+
return keys;
|
|
234
|
+
},
|
|
235
|
+
async queryExpressions(collection, limit = 50, cursor) {
|
|
236
|
+
// Ordered so a cursor means the same thing on every device, which a
|
|
237
|
+
// directory listing on its own would not guarantee.
|
|
238
|
+
const ordered = [...expressions.values()]
|
|
239
|
+
.filter((expression) => expression.collection === collection)
|
|
240
|
+
.sort((a, b) => a.createdAt.localeCompare(b.createdAt) || a.id.localeCompare(b.id));
|
|
241
|
+
const start = cursor ? ordered.findIndex((expression) => expression.id === cursor) + 1 : 0;
|
|
242
|
+
return ordered.slice(start, start + limit);
|
|
243
|
+
},
|
|
244
|
+
async putExpression(expression) {
|
|
245
|
+
await writeFolderFile(expressionsDir, encodeKey(expression.id) + EXPRESSION_SUFFIX, utf8Encode(JSON.stringify(expression)));
|
|
246
|
+
expressions.set(expression.id, expression);
|
|
247
|
+
},
|
|
248
|
+
async getExpression(id) {
|
|
249
|
+
const known = expressions.get(id);
|
|
250
|
+
if (known)
|
|
251
|
+
return known;
|
|
252
|
+
// Not in memory: another origin may have written it since the last reload.
|
|
253
|
+
const expression = await readExpressionFile(encodeKey(id) + EXPRESSION_SUFFIX);
|
|
254
|
+
if (expression)
|
|
255
|
+
expressions.set(id, expression);
|
|
256
|
+
return expression;
|
|
257
|
+
},
|
|
258
|
+
async deleteExpression(id) {
|
|
259
|
+
await removeFile(expressionsDir, encodeKey(id) + EXPRESSION_SUFFIX);
|
|
260
|
+
expressions.delete(id);
|
|
261
|
+
},
|
|
262
|
+
async batch(ops) {
|
|
263
|
+
for (const op of ops) {
|
|
264
|
+
if (op.type === 'put') {
|
|
265
|
+
await writeFolderFile(kvDir, encodeKey(op.key), op.value);
|
|
266
|
+
kvCache.set(op.key, op.value);
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
await removeFile(kvDir, encodeKey(op.key));
|
|
270
|
+
kvCache.set(op.key, null);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
},
|
|
274
|
+
async close() {
|
|
275
|
+
expressions.clear();
|
|
276
|
+
kvCache.clear();
|
|
277
|
+
},
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
//# sourceMappingURL=folder-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folder-adapter.js","sourceRoot":"","sources":["../../src/storage/folder-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module folder-reconcile
|
|
3
|
+
* Making a shared folder converge, without anybody having to take a lock.
|
|
4
|
+
*
|
|
5
|
+
* Two origins — or two devices behind Dropbox, iCloud or Syncthing — can write
|
|
6
|
+
* into one folder at the same time. Almost nothing there can actually conflict:
|
|
7
|
+
* expression files are named by their own content hash, so two writers either
|
|
8
|
+
* produce different files or produce byte-identical ones. The single mutable
|
|
9
|
+
* thing is the MST root pointer, and a lost write to it costs nothing, because
|
|
10
|
+
* the tree is derived state.
|
|
11
|
+
*
|
|
12
|
+
* So the rule is: **the set of expression files wins.** Reconciling means
|
|
13
|
+
* rebuilding the tree until it agrees with the directory — adding what turned up
|
|
14
|
+
* and dropping what went away. That is the whole concurrency story.
|
|
15
|
+
*/
|
|
16
|
+
import type { StorageProvider } from './storage-provider.js';
|
|
17
|
+
import type { FolderAdapter } from './folder-adapter.js';
|
|
18
|
+
import type { Expression } from '../types.js';
|
|
19
|
+
/** What a pass over the folder found */
|
|
20
|
+
export interface FolderReconciliation {
|
|
21
|
+
/** Expression files that appeared since the last pass — another origin's writes */
|
|
22
|
+
readonly added: ReadonlyArray<string>;
|
|
23
|
+
/** Expression files that are gone */
|
|
24
|
+
readonly removed: ReadonlyArray<string>;
|
|
25
|
+
/**
|
|
26
|
+
* Tree entries that had to be fixed to match the directory.
|
|
27
|
+
*
|
|
28
|
+
* Usually empty, and that is the expected case rather than a lucky one: both
|
|
29
|
+
* origins read and write the same root pointer, so the tree one of them built
|
|
30
|
+
* is normally already right for the other. This fills in when a pointer write
|
|
31
|
+
* was lost — two writers racing, or a folder that came back from Dropbox with
|
|
32
|
+
* files the pointer never saw.
|
|
33
|
+
*/
|
|
34
|
+
readonly repaired: ReadonlyArray<string>;
|
|
35
|
+
/** Whether anything moved at all — worth a re-render when true */
|
|
36
|
+
readonly changed: boolean;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Re-reads the folder and brings the Merkle tree back in line with it.
|
|
40
|
+
*
|
|
41
|
+
* Safe to call as often as you like: with nothing new on disk it is two
|
|
42
|
+
* directory listings and no writes.
|
|
43
|
+
*
|
|
44
|
+
* @param storage The provider whose tree should be brought up to date
|
|
45
|
+
* @param adapter The folder adapter underneath it
|
|
46
|
+
* @param accept Whether a version found on disk may be placed. Anyone who can
|
|
47
|
+
* write the folder can drop a file in it — another site given the folder, a
|
|
48
|
+
* sync service — so what turns up there is checked like anything arriving
|
|
49
|
+
* from a peer. A version refused now stays on disk and is asked about again.
|
|
50
|
+
* @returns What moved
|
|
51
|
+
*/
|
|
52
|
+
export declare function reconcileFolder(storage: StorageProvider, adapter: FolderAdapter, accept?: (expression: Expression) => Promise<boolean>): Promise<FolderReconciliation>;
|
|
53
|
+
//# sourceMappingURL=folder-reconcile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folder-reconcile.d.ts","sourceRoot":"","sources":["../../src/storage/folder-reconcile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,wCAAwC;AACxC,MAAM,WAAW,oBAAoB;IACnC,mFAAmF;IACnF,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACtC,qCAAqC;IACrC,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACxC;;;;;;;;OAQG;IACH,QAAQ,CAAC,QAAQ,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACzC,kEAAkE;IAClE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;CAC3B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAsB,eAAe,CACnC,OAAO,EAAE,eAAe,EACxB,OAAO,EAAE,aAAa,EACtB,MAAM,GAAE,CAAC,UAAU,EAAE,UAAU,KAAK,OAAO,CAAC,OAAO,CAAoB,GACtE,OAAO,CAAC,oBAAoB,CAAC,CAoC/B"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module folder-reconcile
|
|
3
|
+
* Making a shared folder converge, without anybody having to take a lock.
|
|
4
|
+
*
|
|
5
|
+
* Two origins — or two devices behind Dropbox, iCloud or Syncthing — can write
|
|
6
|
+
* into one folder at the same time. Almost nothing there can actually conflict:
|
|
7
|
+
* expression files are named by their own content hash, so two writers either
|
|
8
|
+
* produce different files or produce byte-identical ones. The single mutable
|
|
9
|
+
* thing is the MST root pointer, and a lost write to it costs nothing, because
|
|
10
|
+
* the tree is derived state.
|
|
11
|
+
*
|
|
12
|
+
* So the rule is: **the set of expression files wins.** Reconciling means
|
|
13
|
+
* rebuilding the tree until it agrees with the directory — adding what turned up
|
|
14
|
+
* and dropping what went away. That is the whole concurrency story.
|
|
15
|
+
*/
|
|
16
|
+
/**
|
|
17
|
+
* Re-reads the folder and brings the Merkle tree back in line with it.
|
|
18
|
+
*
|
|
19
|
+
* Safe to call as often as you like: with nothing new on disk it is two
|
|
20
|
+
* directory listings and no writes.
|
|
21
|
+
*
|
|
22
|
+
* @param storage The provider whose tree should be brought up to date
|
|
23
|
+
* @param adapter The folder adapter underneath it
|
|
24
|
+
* @param accept Whether a version found on disk may be placed. Anyone who can
|
|
25
|
+
* write the folder can drop a file in it — another site given the folder, a
|
|
26
|
+
* sync service — so what turns up there is checked like anything arriving
|
|
27
|
+
* from a peer. A version refused now stays on disk and is asked about again.
|
|
28
|
+
* @returns What moved
|
|
29
|
+
*/
|
|
30
|
+
export async function reconcileFolder(storage, adapter, accept = async () => true) {
|
|
31
|
+
const disk = await adapter.reload();
|
|
32
|
+
const onDisk = new Set(await adapter.listExpressionIds());
|
|
33
|
+
// Versions another writer put on disk that this tree has not placed. Each
|
|
34
|
+
// goes through the ordering rule like anything else: it becomes current,
|
|
35
|
+
// is kept as history, or loses and is dropped — the same decision the other
|
|
36
|
+
// writer reached, so both converge on the same tree.
|
|
37
|
+
const indexed = new Set((await storage.entries()).map((entry) => entry.value));
|
|
38
|
+
const missing = [...onDisk].filter((id) => !indexed.has(id));
|
|
39
|
+
const placed = [];
|
|
40
|
+
for (const id of missing) {
|
|
41
|
+
const expression = await adapter.getExpression(id);
|
|
42
|
+
// A file can vanish between the listing and the read — another writer
|
|
43
|
+
// dropping a superseded version. Nothing to add, and the next pass agrees.
|
|
44
|
+
if (expression && (await accept(expression))) {
|
|
45
|
+
await storage.addExpression(expression);
|
|
46
|
+
placed.push(id);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
// Entries whose file is gone: another writer superseded and dropped it, and
|
|
50
|
+
// its replacement was placed above.
|
|
51
|
+
const stale = [...new Set((await storage.entries()).map((entry) => entry.value))].filter((id) => !onDisk.has(id) && !missing.includes(id));
|
|
52
|
+
for (const id of stale) {
|
|
53
|
+
if (!(await adapter.getExpression(id)))
|
|
54
|
+
await storage.removeExpression(id);
|
|
55
|
+
}
|
|
56
|
+
// A refused file is not a change: it would otherwise redraw every pass.
|
|
57
|
+
const repaired = [...placed, ...stale];
|
|
58
|
+
return {
|
|
59
|
+
added: disk.added,
|
|
60
|
+
removed: disk.removed,
|
|
61
|
+
repaired,
|
|
62
|
+
changed: disk.added.length > 0 || disk.removed.length > 0 || repaired.length > 0,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=folder-reconcile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"folder-reconcile.js","sourceRoot":"","sources":["../../src/storage/folder-reconcile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AA0BH;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,OAAwB,EACxB,OAAsB,EACtB,SAAuD,KAAK,IAAI,EAAE,CAAC,IAAI;IAEvE,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,MAAM,EAAE,CAAC;IACpC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,MAAM,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAAC;IAE1D,0EAA0E;IAC1E,yEAAyE;IACzE,4EAA4E;IAC5E,qDAAqD;IACrD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IAC/E,MAAM,OAAO,GAAG,CAAC,GAAG,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7D,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,MAAM,EAAE,IAAI,OAAO,EAAE,CAAC;QACzB,MAAM,UAAU,GAAG,MAAM,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;QACnD,sEAAsE;QACtE,2EAA2E;QAC3E,IAAI,UAAU,IAAI,CAAC,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;YAC7C,MAAM,OAAO,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;YACxC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAClB,CAAC;IACH,CAAC;IAED,4EAA4E;IAC5E,oCAAoC;IACpC,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,MAAM,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;IAC3I,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE,CAAC;QACvB,IAAI,CAAC,CAAC,MAAM,OAAO,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;YAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC;IAC7E,CAAC;IAED,wEAAwE;IACxE,MAAM,QAAQ,GAAG,CAAC,GAAG,MAAM,EAAE,GAAG,KAAK,CAAC,CAAC;IACvC,OAAO;QACL,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,QAAQ;QACR,OAAO,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;KACjF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module storage
|
|
3
|
+
* Exports all storage related functionality.
|
|
4
|
+
*/
|
|
5
|
+
export * from './indexeddb-adapter.js';
|
|
6
|
+
export * from './mst.js';
|
|
7
|
+
export * from './storage-provider.js';
|
|
8
|
+
export * from './directory-access.js';
|
|
9
|
+
export type { DirectoryHandleLike } from './folder-adapter.js';
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,wBAAwB,CAAC;AACvC,cAAc,UAAU,CAAC;AACzB,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC;AACtC,YAAY,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,wBAAwB,CAAC;AACvC,cAAc,UAAU,CAAC;AACzB,cAAc,uBAAuB,CAAC;AACtC,cAAc,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module indexeddb-adapter
|
|
3
|
+
* IndexedDB implementation of StorageAdapter for P2P network.
|
|
4
|
+
*/
|
|
5
|
+
import type { StorageAdapter } from '../types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Creates an IndexedDB backed storage adapter.
|
|
8
|
+
* @param dbName The name of the database (defaults to 'weave-storage')
|
|
9
|
+
* @returns A promise resolving to the StorageAdapter
|
|
10
|
+
*/
|
|
11
|
+
export declare function createIndexedDBAdapter(dbName?: string): Promise<StorageAdapter>;
|
|
12
|
+
//# sourceMappingURL=indexeddb-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexeddb-adapter.d.ts","sourceRoot":"","sources":["../../src/storage/indexeddb-adapter.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAuB,MAAM,aAAa,CAAC;AAyCvE;;;;GAIG;AACH,wBAAsB,sBAAsB,CAAC,MAAM,GAAE,MAAwB,GAAG,OAAO,CAAC,cAAc,CAAC,CAuKtG"}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @module indexeddb-adapter
|
|
3
|
+
* IndexedDB implementation of StorageAdapter for P2P network.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Bumped to 2 when MST nodes gained a `height` field. Opening an older
|
|
7
|
+
* database wipes it rather than reading nodes the tree can no longer parse.
|
|
8
|
+
*/
|
|
9
|
+
const DB_VERSION = 2;
|
|
10
|
+
/**
|
|
11
|
+
* Promisify an IDBRequest.
|
|
12
|
+
* @param request The IDBRequest to promisify
|
|
13
|
+
* @returns A promise that resolves with the request result
|
|
14
|
+
*/
|
|
15
|
+
function idbRequest(request) {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
request.onsuccess = () => resolve(request.result);
|
|
18
|
+
request.onerror = () => reject(request.error);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Helper to manage IDBTransactions.
|
|
23
|
+
* @param db The IDB database instance
|
|
24
|
+
* @param stores The stores to include in the transaction
|
|
25
|
+
* @param mode The transaction mode ('readonly' | 'readwrite')
|
|
26
|
+
* @returns The transaction and a promise that resolves when it completes
|
|
27
|
+
*/
|
|
28
|
+
function idbTransaction(db, stores, mode) {
|
|
29
|
+
const tx = db.transaction(stores, mode);
|
|
30
|
+
const complete = new Promise((resolve, reject) => {
|
|
31
|
+
tx.oncomplete = () => resolve();
|
|
32
|
+
tx.onerror = () => reject(tx.error);
|
|
33
|
+
tx.onabort = () => reject(new Error('Transaction aborted'));
|
|
34
|
+
});
|
|
35
|
+
return { tx, complete };
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Creates an IndexedDB backed storage adapter.
|
|
39
|
+
* @param dbName The name of the database (defaults to 'weave-storage')
|
|
40
|
+
* @returns A promise resolving to the StorageAdapter
|
|
41
|
+
*/
|
|
42
|
+
export async function createIndexedDBAdapter(dbName = 'weave-storage') {
|
|
43
|
+
const db = await new Promise((resolve, reject) => {
|
|
44
|
+
const request = globalThis.indexedDB.open(dbName, DB_VERSION);
|
|
45
|
+
request.onupgradeneeded = (event) => {
|
|
46
|
+
const db = request.result;
|
|
47
|
+
const previousVersion = event.oldVersion;
|
|
48
|
+
// v1 stored MST nodes without a height, which the current tree cannot
|
|
49
|
+
// read. There is no way to migrate them — the old structure was a single
|
|
50
|
+
// flat node with no tree to recover — so the local cache is dropped and
|
|
51
|
+
// rebuilt by syncing with peers.
|
|
52
|
+
if (previousVersion > 0 && previousVersion < 2) {
|
|
53
|
+
for (const name of ['kv', 'expressions']) {
|
|
54
|
+
if (db.objectStoreNames.contains(name))
|
|
55
|
+
db.deleteObjectStore(name);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Store for key-value (MST nodes, root cid, etc)
|
|
59
|
+
if (!db.objectStoreNames.contains('kv')) {
|
|
60
|
+
db.createObjectStore('kv');
|
|
61
|
+
}
|
|
62
|
+
// Store for expressions
|
|
63
|
+
if (!db.objectStoreNames.contains('expressions')) {
|
|
64
|
+
const expStore = db.createObjectStore('expressions', { keyPath: 'id' });
|
|
65
|
+
expStore.createIndex('collection', 'collection', { unique: false });
|
|
66
|
+
expStore.createIndex('author', 'author', { unique: false });
|
|
67
|
+
expStore.createIndex('createdAt', 'createdAt', { unique: false });
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
request.onsuccess = () => resolve(request.result);
|
|
71
|
+
request.onerror = () => reject(request.error);
|
|
72
|
+
});
|
|
73
|
+
return Object.freeze({
|
|
74
|
+
async get(key) {
|
|
75
|
+
const { tx } = idbTransaction(db, ['kv'], 'readonly');
|
|
76
|
+
const store = tx.objectStore('kv');
|
|
77
|
+
const result = await idbRequest(store.get(key));
|
|
78
|
+
return result ? new Uint8Array(result) : null;
|
|
79
|
+
},
|
|
80
|
+
async put(key, value) {
|
|
81
|
+
const { tx, complete } = idbTransaction(db, ['kv'], 'readwrite');
|
|
82
|
+
const store = tx.objectStore('kv');
|
|
83
|
+
store.put(value.buffer, key);
|
|
84
|
+
await complete;
|
|
85
|
+
},
|
|
86
|
+
async delete(key) {
|
|
87
|
+
const { tx, complete } = idbTransaction(db, ['kv'], 'readwrite');
|
|
88
|
+
const store = tx.objectStore('kv');
|
|
89
|
+
store.delete(key);
|
|
90
|
+
await complete;
|
|
91
|
+
},
|
|
92
|
+
async has(key) {
|
|
93
|
+
const { tx } = idbTransaction(db, ['kv'], 'readonly');
|
|
94
|
+
const store = tx.objectStore('kv');
|
|
95
|
+
const count = await idbRequest(store.count(key));
|
|
96
|
+
return count > 0;
|
|
97
|
+
},
|
|
98
|
+
async list(prefix = '') {
|
|
99
|
+
const { tx } = idbTransaction(db, ['kv'], 'readonly');
|
|
100
|
+
const store = tx.objectStore('kv');
|
|
101
|
+
const keys = [];
|
|
102
|
+
let request;
|
|
103
|
+
if (prefix) {
|
|
104
|
+
const bound = prefix + '\uFFFF';
|
|
105
|
+
request = store.openCursor(IDBKeyRange.bound(prefix, bound, false, false));
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
request = store.openCursor();
|
|
109
|
+
}
|
|
110
|
+
await new Promise((resolve, reject) => {
|
|
111
|
+
request.onsuccess = (event) => {
|
|
112
|
+
const cursor = event.target.result;
|
|
113
|
+
if (cursor) {
|
|
114
|
+
keys.push(cursor.key);
|
|
115
|
+
cursor.continue();
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
resolve();
|
|
119
|
+
}
|
|
120
|
+
};
|
|
121
|
+
request.onerror = () => reject(request.error);
|
|
122
|
+
});
|
|
123
|
+
return keys;
|
|
124
|
+
},
|
|
125
|
+
async queryExpressions(collection, limit = 50, cursor) {
|
|
126
|
+
const { tx } = idbTransaction(db, ['expressions'], 'readonly');
|
|
127
|
+
const store = tx.objectStore('expressions');
|
|
128
|
+
const index = store.index('collection');
|
|
129
|
+
const range = IDBKeyRange.only(collection);
|
|
130
|
+
const request = index.openCursor(range);
|
|
131
|
+
const results = [];
|
|
132
|
+
let advanced = !cursor;
|
|
133
|
+
await new Promise((resolve, reject) => {
|
|
134
|
+
request.onsuccess = (event) => {
|
|
135
|
+
const idbCursor = event.target.result;
|
|
136
|
+
if (!idbCursor || results.length >= limit) {
|
|
137
|
+
resolve();
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (!advanced && cursor && idbCursor.primaryKey === cursor) {
|
|
141
|
+
advanced = true;
|
|
142
|
+
idbCursor.continue();
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (advanced) {
|
|
146
|
+
results.push(idbCursor.value);
|
|
147
|
+
}
|
|
148
|
+
idbCursor.continue();
|
|
149
|
+
};
|
|
150
|
+
request.onerror = () => reject(request.error);
|
|
151
|
+
});
|
|
152
|
+
return results;
|
|
153
|
+
},
|
|
154
|
+
async putExpression(expression) {
|
|
155
|
+
const { tx, complete } = idbTransaction(db, ['expressions'], 'readwrite');
|
|
156
|
+
const store = tx.objectStore('expressions');
|
|
157
|
+
store.put(expression);
|
|
158
|
+
await complete;
|
|
159
|
+
},
|
|
160
|
+
async getExpression(id) {
|
|
161
|
+
const { tx } = idbTransaction(db, ['expressions'], 'readonly');
|
|
162
|
+
const store = tx.objectStore('expressions');
|
|
163
|
+
const result = await idbRequest(store.get(id));
|
|
164
|
+
return result || null;
|
|
165
|
+
},
|
|
166
|
+
async deleteExpression(id) {
|
|
167
|
+
const { tx, complete } = idbTransaction(db, ['expressions'], 'readwrite');
|
|
168
|
+
const store = tx.objectStore('expressions');
|
|
169
|
+
store.delete(id);
|
|
170
|
+
await complete;
|
|
171
|
+
},
|
|
172
|
+
async batch(ops) {
|
|
173
|
+
const { tx, complete } = idbTransaction(db, ['kv'], 'readwrite');
|
|
174
|
+
const store = tx.objectStore('kv');
|
|
175
|
+
for (const op of ops) {
|
|
176
|
+
if (op.type === 'put') {
|
|
177
|
+
store.put(op.value.buffer, op.key);
|
|
178
|
+
}
|
|
179
|
+
else if (op.type === 'delete') {
|
|
180
|
+
store.delete(op.key);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
await complete;
|
|
184
|
+
},
|
|
185
|
+
async close() {
|
|
186
|
+
db.close();
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=indexeddb-adapter.js.map
|