Zero Data Wrap
Unified JavaScript API for Fission + remoteStorage.
Yes, it's long and verbose and how I do things generally, but ideally your final code looks like this:
// write `{"id":"batmobile","clean":true,"washed":"…"}` to /bravo/cars/batmobile.json
await api.alfa.cars.wash({
id: 'batmobile',
});
const api = await zerodatawrap.ZDRWrap({
// include then pass directly
ZDRParamLibrary: RemoteStorage, // or webnative
// read/write permissions
ZDRParamScopes: [{
// code-level identifier
ZDRScopeKey: 'alfa',
// top-level storage directory
ZDRScopeDirectory: 'bravo',
}],
});
// write `{"foo":"bar"}` to /bravo/charlie.json
await api.alfa.ZDRStorageWriteObject('charlie.json', {
foo: 'bar',
});
param | type | notes |
---|---|---|
ZDRParamLibrary Required |
pass RemoteStorage or webnative or an object conforming to ZDRClient |
|
ZDRParamScopes Required |
array of ZDRScope objects |
|
ZDRParamDispatchError | function | called on network or sync errors |
ZDRParamDispatchConnected | function | called if linked to a cloud account |
ZDRParamDispatchOnline | function | called when network is online |
ZDRParamDispatchOffline | function | called when network is offline |
param | type | notes |
---|---|---|
ZDRScopeKey Required |
string, non-empty, trimmed | convenience accessor for code only |
ZDRScopeDirectory Required |
string, non-empty, trimmed, no slashes | top-level directory for claiming read/write access |
ZDRScopeCreatorDirectory | string, non-empty, trimmed | if Fission, sets permissions.app instead of permissions.fs |
ZDRScopeSchemas | array of ZDRSchema objects |
defines model helpers |
ZDRScopeIsPublic | boolean | use public directory on read/write |
Call JSON.stringify
; write to storage.
Returns input object.
mimetype
used by remoteStorage.
Write to storage.
Returns input.
Read from storage; call JSON.parse
.
Returns object.
Read from storage.
Returns data.
Read from storage in path directory (non-recursive); group by path.
Returns key/value object.
Fetch paths (non-recursive).
Returns array of paths.
Fetch paths recursively.
Returns flat array of paths.
Delete from storage.
Returns null.
Delete folder and subcontents recursively from storage.
Returns input.
Start authorization process corresponding to ZDRParamLibrary
.
Returns undefined.
Retry authorization process in case of expiry or denied access.
Returns undefined.
Log out.
Returns undefined.
const api = zerodatawrap.ZDRWrap({
// …
ZDRParamScopes: [{
// …
// map schemas to paths
ZDRScopeSchemas: [{
// code-level identifier
ZDRSchemaKey: 'cars',
// path for a given object
ZDRSchemaPath (object) {
return `cars/${ object.id }.json`;
},
// object information for a given path
ZDRSchemaStub (path) {
return {
id: path.split('/').pop().split('.json').shift(),
};
},
}],
// …
}],
// …
});
// write `{"id":"batmobile"}` to /bravo/cars/batmobile.json
await api.alfa.cars.ZDRModelWriteObject({
id: 'batmobile',
});
param | type | notes |
---|---|---|
ZDRSchemaKey Required |
string, non-empty, trimmed | convenience accessor for code only |
ZDRSchemaPath Required |
function | constructs the path for a given object |
ZDRSchemaStub Required |
function | constructs object information for a given path, used for filtering paths in recursion and routing sync events |
ZDRSchemaMethods | object | defines methods to be accessed from the api interface (bound to this ) |
ZDRSchemaDispatchValidate | function | called before ZDRModelWriteObject |
ZDRSchemaDispatchSyncCreate | function | called on remote create remoteStorage only |
ZDRSchemaDispatchSyncUpdate | function | called on remote update remoteStorage only |
ZDRSchemaDispatchSyncDelete | function | called on remote delete remoteStorage only |
ZDRSchemaDispatchSyncConflict | function | called on remote conflict remoteStorage only Note: this passes the remoteStorage change event directly |
const api = zerodatawrap.ZDRWrap({
// …
ZDRParamScopes: [{
// …
ZDRScopeSchemas: [{
// …
// truthy values cause a promise rejection
ZDRSchemaDispatchValidate (object) {
if (typeof object.id !== 'string') {
return {
not: 'so fast',
};
}
},
// …
}],
// …
}],
// …
});
// rejects
try {
await api.alfa.cars.ZDRModelWriteObject({
id: 123,
});
} catch (truthy) {
console.log(truthy.not); // so fast
}
const api = zerodatawrap.ZDRWrap({
// …
ZDRParamScopes: [{
// …
ZDRScopeSchemas: [{
// …
// logic to be done on save
ZDRSchemaMethods: {
clean (car) {
return this.alfa.cars.ZDRModelWriteObject(Object.assign(car, {
clean: true,
washed: new Date(),
}));
},
},
// …
}],
// …
}],
// …
});
// write `{"id":"batmobile","clean":true,"washed":"…"}` to /bravo/cars/batmobile.json
await api.alfa.cars.clean({
id: 'batmobile',
});
const api = zerodatawrap.ZDRWrap({
// …
ZDRParamScopes: [{
// …
ZDRScopeSchemas: [{
// …
// update the interface for remote changes
ZDRSchemaDispatchSyncCreate (object) {
console.log('create', object);
},
ZDRSchemaDispatchSyncUpdate (object) {
console.log('update', object);
},
ZDRSchemaDispatchSyncDelete (object) {
console.log('delete', object);
},
// handle conflict
ZDRSchemaDispatchSyncConflict (event) {
console.log('conflict', event);
},
// …
}],
// …
}],
// …
});
Returns object path via ZDRSchemaPath
.
Validate with ZDRSchemaDispatchValidate
, reject if truthy; write object to path from ZDRModelPath
.
Returns input.
Read all objects recursively (where paths conform to logic in ZDRSchema
).
Returns array of objects.
Delete object from storage.
Returns input.
When supporting multiple protocols, the library can help track which one was selected.
const api = await zerodatawrap.ZDRWrap({
ZDRParamLibrary: (function() {
// get the selected protocol, default to `ZDRProtocolCustom`
const selected = zerodatawrap.ZDRPreferenceProtocol(zerodatawrap.ZDRProtocolCustom());
if (selected === zerodatawrap.ZDRProtocolRemoteStorage()) {
return RemoteStorage;
}
if (selected === zerodatawrap.ZDRProtocolFission()) {
return webnative;
}
return {
ZDRClientWriteFile (path, data) {
console.log('custom protocol write', [...arguments]);
},
ZDRClientReadFile (path) {
console.log('custom protocol read', [...arguments]);
},
ZDRClientListObjects (path) {
console.log('custom protocol list objects', [...arguments]);
},
ZDRClientDelete (path) {
console.log('custom protocol delete', [...arguments]);
},
};
})(),
// …
});
Move from one protocol to another by generating APIs from preferences:
if (zerodatawrap.ZDRPreferenceProtocolMigrate()) {
// generate apis from protocol
const wrap = function (protocol) {
return zerodatawrap.ZDRWrap({
// …
});
};
const source = await wrap(zerodatawrap.ZDRPreferenceProtocolMigrate());
const destination = await wrap(zerodatawrap.ZDRPreferenceProtocol());
// get all objects (this is simplified, should be recursive)
await Promise.all(Object.entries(await source.App.ZDRStorageListingObjects('')).map(async function ([key, value]) {
// write to destination
await destination.App.ZDRStorageWriteObject(key, value);
// delete from source
await source.App.ZDRStorageDeleteFile(key);
}));
// clear migrate preference to avoid repeating
zerodatawrap.ZDRPreferenceProtocolMigrateClear();
// call disconnect to do any other cleanup
source.ZDRCloudDisconnect();
};
function | notes |
---|---|
ZDRClientWriteFile Required |
called by ZDRStorageWriteFile |
ZDRClientReadFile Required |
called by ZDRStorageReadFile |
ZDRClientListObjects Required |
called by ZDRStorageListingObjects |
ZDRClientDelete Required |
called by ZDRStorageDeleteFile |
ZDRClientPrepare | called before returning wrapper |
ZDRClientConnect | called by ZDRCloudConnect |
ZDRClientReconnect | called by ZDRCloudReconnect |
ZDRClientDisconnect | called by ZDRCloudDisconnect |
Returns string.
Returns string.
Returns string.
Stores input as the protocol preference if none is set.
Returns the protocol preference.
Clears the protocol preference.
Returns null.
Sets the protocol preference as 'to be migrated' if connecting to a different protocol.
Returns input.
Returns the 'to be migrated' protocol if set.
Clears the 'to be migrated' protocol.
Returns input object.
Help me keep creating projects that are public, accessible for free, and open-source.
The code is released under a Hippocratic License, modified to exclude its use for surveillance capitalism and also to require large for-profit entities to purchase a paid license.