Skip to content

Latest commit

 

History

History
505 lines (323 loc) · 11.1 KB

README.md

File metadata and controls

505 lines (323 loc) · 11.1 KB

Project logo

Unified JavaScript API for Fission + remoteStorage.

Comparisons

Long names…

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',
});

API Guide

Setup

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',
});

ZDRWrap

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

ZDRScope

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

Storage

ZDRStorageWriteObject(path, object)

Call JSON.stringify; write to storage.

Returns input object.

ZDRStorageWriteFile(path, data, mimetype)

mimetype used by remoteStorage.

Write to storage.

Returns input.

ZDRStorageReadObject(path)

Read from storage; call JSON.parse.

Returns object.

ZDRStorageReadFile(path)

Read from storage.

Returns data.

ZDRStorageListingObjects(path)

Read from storage in path directory (non-recursive); group by path.

Returns key/value object.

ZDRStoragePaths(path)

Fetch paths (non-recursive).

Returns array of paths.

ZDRStoragePathsRecursive(path)

Fetch paths recursively.

Returns flat array of paths.

ZDRStorageDeleteFile(path)

Delete from storage.

Returns null.

ZDRStorageDeleteFolderRecursive(path)

Delete folder and subcontents recursively from storage.

Returns input.

Cloud

ZDRCloudConnect(address)

Start authorization process corresponding to ZDRParamLibrary.

Returns undefined.

ZDRCloudReconnect()

Retry authorization process in case of expiry or denied access.

Returns undefined.

ZDRCloudDisconnect()

Log out.

Returns undefined.

Model (optional)

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',
});

ZDRSchema

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

Specify a validation function

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
}

Specify custom methods

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',
});

Receive object sync notifications

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);
      },

      // …

    }],

    // …

  }],

  // …

});

ZDRModelPath(object)

Returns object path via ZDRSchemaPath.

ZDRModelWriteObject(object)

Validate with ZDRSchemaDispatchValidate, reject if truthy; write object to path from ZDRModelPath.

Returns input.

ZDRModelListObjects()

Read all objects recursively (where paths conform to logic in ZDRSchema).

Returns array of objects.

ZDRModelDeleteObject(object)

Delete object from storage.

Returns input.

Multiple protocols

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();
};

ZDRClient (for custom protocol only)

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

ZDRProtocolRemoteStorage()

Returns string.

ZDRProtocolFission()

Returns string.

ZDRProtocolCustom()

Returns string.

ZDRPreferenceProtocol(protocol)

Stores input as the protocol preference if none is set.

Returns the protocol preference.

ZDRPreferenceProtocolClear()

Clears the protocol preference.

Returns null.

ZDRPreferenceProtocolConnect(protocol)

Sets the protocol preference as 'to be migrated' if connecting to a different protocol.

Returns input.

ZDRPreferenceProtocolMigrate()

Returns the 'to be migrated' protocol if set.

ZDRPreferenceProtocolMigrateClear()

Clears the 'to be migrated' protocol.

Returns input object.

❤️

Help me keep creating projects that are public, accessible for free, and open-source.

Send a gift

License

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.

Questions

Feel free to reach out on Mastodon or Twitter.