A suite of repositories with semantic versioning of objects and their dependencies. Think Bower, but for anything, not just JS code
The Problem:
You've got lots of things (code, data, whatever), and you've got multiple versions of your things. Furthermore your things only work when paired with the right versions of other things.
The Solution:
Version-repo's are typed repositories that store named values, their version, and their requirements (dependencies). Version repo's take care of the logic of finding a complete and consistent set of resources for a given query.
Furthermore, there are a variety of version-repo's which simplify the process of storing your object on disk, in memory, or in a database; serving and querying them http/https, and perfomning tranformations when storing and/or fetching your resources (e.g. stringifying / parsing JSON objects stored on disk).
API
Storing resources
Resources are stored using the create()
method, like so:
var R = ;var repo = ;repo;repo;repo;repo;
Retrieving resources
Resources are queried using the fetch()
method, which return an array of matching resources.
repo;// [{ name: "A",// version: "2.0.0",// value: "Something different" }]
Note that because we didn't specify a version, the latest version of the resource was returned
In the event that you really only want the specified resource and not the
dependencies, the fetchOne()
method will do the trick:
repo;// { name: "A",// version: "2.0.0",// value: "Something different" }
older versions of a resource can be queried by passing a version
parameter:
repovalue // "My great thing"
and we can use the a query object of for as shorthand for an array of name/version pairs:
repovalue // "My great thing"repovalue // "My great thing"repovalue // "The best thing yet"repovalue // "The best thing yet"repovalue // "The best thing yet"
Fetching Multiple Resources:
So far we've stored several vesions of a single resource, but the real purpose of the version repo's is to manage multiple resources and their versions. For this, we'll neeed a more complicated example:
repo;repo;repo;repo;repo;repo; repo
with our more mature repository, we can query the complete set of resources
that are required for by our query using the feth()
method:
repo
similarly, the depends()
method will return list of resources which match
your query, (but not the resources themselves...)
// Calculate dependencies repo // {A:"2.0.0",B:"1.1.4",}repo // {A:"2.0.0",B:"1.1.4",C:"1.1.1"}
and to get the list of resources, and their dependencies, but not the values,
we can pass an options object to fetch, specifying the novealue:true
repo
Conveniently, attempting to fetch an conflicted set of resources throws a
Version Conflict
error, when there is no set of resources
which satisfies your query:
// Each of these raises a Version Conflict errorrepo reporepo
Updating Resources:
A resource either can be updated using either update()
method or the
insert()
method with the option upsert:true
:
reporepo
However by default you may only update the latest version of the resource,
which can be changed by setting update:"any"
or update:"none"
when
instantiating the repo.
Deleting Resources:
A resource can be deleted using the del()
method:
repo.del({name:'A',version:'2.0.0'})
However by default you may only delete the latest version of the resource,
which can be changed by setting delete:"any"
or delete:"none"
when
instantiating the repo.
Repositories Classes
MemoryRepo (API: Synchronous, Stored Types: Any)
A synchronous repository which keeps resources in memory.
Constructor parameters
- config: An object with the following attributes:
- update: (optional) one of "latest" (default), "any", "none"
- delete: (optional) one of "latest" (default), "any", "none"
Example :
var my_repo =
ReadonlyBuffer (API: Async, Stored Types: Any)
A ReadOnly Buffer repository is a read-only wrapper which keeps local copies of resources queried from another 'host' repository. This is particularly useful if the host repo is on another physical machine, for example to reduce the number of network requests of mobile apps. Local resources are stored in memory and calls to create/update/delete methods are forwarded onto the host repository.
Constructor parameters
- repo: A version-repo instance
Example :
var host_repo = var my_readonly_repo =host_repo
sTransform (API: Synchronous, Stored Types: Any)
A Synchronous repo which forwards all requests to another version repo and performs transformations of the stored values on create / update (storifying) and fetch (de-storifying).
This is particularly useful for wrapping string-only repositories, such as the
FileRepo
in the
version-repo-node package,
with parse-on-read and stringify-on-write logic. Another use case is for
storing and dispatching copies of objects stored in a repo, in whicn case one
of the many deep-copy functions may be used for storifying, and de-storifiying
values. Transformers could also provide validate-on-save logic by using an
object validator such as the awesome AJV library as a storify, and trivial
function for destorifying (e.g. function(x){return x;}
)
Constructor parameters
- repo: A version-repo instance of type
S
. - storify: A function used to transform objects as they are stored (
create()
ed) or updated. - destorify: A function used to transform stored objects when they are retrieved (
fetch()
ed).
Example :
var host_repo = var my_repo = host_repoJSONstringifyJSONparse
the same examlple in TypeScript with generic typing:
var host_repo = <string>var my_repo = <stringany>host_repoJSONstringifyJSONparse
dTransform (API: Synchronous, Stored Types: Any)
An asynchronous (i.e. Deffered) repo which forwards all requests to another version repo and performs transformations of the objects in transit.
This is particularly useful for wrapping asynchronous repo's with limited storage types, such as the File and Remote (HTTP/S) Repo's in version-repo-node)
Constructor parameters
- repo: A version-repo instance of type
S
. - storify: A function used to transform objects on storage on create / update. (
funciton(x:T):S
) - destorify: A function used to transform objects from storage on fetch. (
funciton(x:T):S
)
Note that the host repo may have a synchronous API, and the storify and/or de-storify functions may return transformed values or Promised for the transformed values.
Examples:
var string_only_repo = var my_async_repo = string_only_repoJSONstringifyJSONparse
the same examlple in TypeScript with generic types:
var string_only_repo = new MemoryRepo<string>()var my_async_repo = new dTransform<string,any>(string_only_repo,JSON.stringify,JSON.parse)
Working with TypeScript
Every repo's that can potentially store any type of object accept a type parameter:
and synchronous and deferred transform repositories accept two type parameters which specify the type of the underlying repo, and the type for the API it exposes. In this example, a FileRepo is used to store serialized objects on disk, and an deferred transform repo is used to manage the serialization / de-serialization:
repo: new repo.sTransformnew repo.MemoryRepo, x, x});;
Some repo's can store only a limited set of values, eg. the FileRepo can only accept sting values.
General API
This package is written in typescripts so explicitly importing .d.ts
file should not be required.
However generic repo interfaces are defined in src/typings.d.ts
, and the
synchronous API is provided here for tautological purposes:
Synchronous API
// an object type used for queries: // an object type used for creating and updating resources // the synchronous repo interface
Asynchronous API
Method signatures are the same as the synchronous versions, but return a bluebird promise for each return value.
Examples:
Note that these examples also demonstrate version-repo-node and make use of temp
// Create an in-memory versioned repositoryvar repo = my_mem_repo= repo // Create a repo using the local file system (NodeJS)var temp = // requires npm install temp path = node_repo = temp_dir = temp my_file_repo = node_repo // Wrap the file system repo in a read-only buffervar buffered_file_repo = repo; // Expose the buffered file repo via an Express HTTP servervar express = app = ; app;var server = 'function' === typeof app ? http : app;server; // Create a "remote" repo which provides access to the express router router via http:var address = server;if !address server; address = server;var protocol = server instanceof httpsServer ? 'https:' : 'http:';var hostname = addressaddress;if hostname === '0.0.0.0' || hostname === '::' hostname = '127.0.0.1';var base_url = protocol + '//' + hostname + ':' + addressport ;var remote_repo = 'base_url':base_url + '/my_repo' // Create a transformer which transforms strings from the remote repo to JSON// objects (and back, when storing objects):json_repo = ; // for parsing stored strings // ----------------------// Now some actual CRUD// ---------------------- // store a string in the file-system based repomy_file_repo