
Security News
Attackers Are Hunting High-Impact Node.js Maintainers in a Coordinated Social Engineering Campaign
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.
mobiletto-orm
Advanced tools
A simple object-relational mapper (ORM) for mobiletto storage.
Mobiletto supports connections to Amazon S3, Backblaze B2, and local filesystems.
I would be sincerely grateful for any contribution via Patreon
You can install mobiletto-orm via npm or yarn
# install with npm
npm i mobiletto-orm
# install with yarn
yarn add mobiletto-orm
To access the mobiletto-orm source:
# Clone source and install dependencies
git clone https://github.com/cobbzilla/mobiletto-orm.git
cd mobiletto-orm
yarn install
mobiletto-orm depends on mobiletto-base, which does not include any storage drivers.
To enable a particular storage driver, first add the dependency to your project:
# Use npm to install the storage driver(s) that you will need
npm i mobiletto-driver-s3
npm i mobiletto-driver-b2
npm i mobiletto-driver-local
npm i mobiletto-driver-indexeddb
# Or, use yarn to install the storage driver(s) that you will need
yarn add mobiletto-driver-s3
yarn add mobiletto-driver-b2
yarn add mobiletto-driver-local
yarn add mobiletto-driver-indexeddb
In your code, before using mobiletto to connect to storage, register the driver:
const { registerDriver } = require('mobiletto-base')
registerDriver('s3', require('mobiletto-driver-s3'))
registerDriver('b2', require('mobiletto-driver-b2'))
registerDriver('local', require('mobiletto-driver-local'))
registerDriver('indexeddb', require('mobiletto-driver-indexeddb'))
const orm = require('mobiletto-orm')
// Register mobiletto storage drivers (described above)
// How to create mobiletto connections: https://github.com/cobbzilla/mobiletto/blob/master/README.md#Basic-usage
const conns = [ ...array of connections... ]
// Objects and indexes will be replicated across all mobiletto connections
// The 'conns' parameter below could also be an async function that returns an array of connections
const factory = orm.repositoryFactory(conns)
// Objects are stored in type-specific repositories
// A repository is backed by a directory on each mobiletto connection
const repository = factory.repository({
typeName: 'Account',
fields: {
username: {
required: true, // field is required
min: 5, // min 5 chars
max: 100, // max 100 chars
regex: /[A-Z\d+]+/gi, // validate against a regex
index: true, // enable findBy('username', someUsername)
updatable: false // updates will be silently ignored
},
email: {
required: true, // field is required
min: 8, // min 8 chars
max: 100, // max 100 chars
// a reasonable email regex
regex: /^[A-Z\d][A-Z\d._%+-]*@[A-Z\d.-]+\.[A-Z]{2,6}$/gi,
index: true // enable findBy('email', someEmailAddress)
},
bio: {
max: 1000 // max 1000 chars (field is optional)
},
yearJoined: {
minValue: 2023 // minimum numeric value
maxValue: 2123 // maxmimum numeric value
}
}
})
const username = 'some_username'
const email = 'jimmy@example.com'
// Every object has a unique 'id' field that is always required and must be unique
// However, if typeDef supports alternateID (default enables) you can use 'username' or 'email' as the 'id'
// See Alternate IDs below for more info
// If an object with the same id already exists, a MobilettoOrmValidationError will be thrown
// If a race condition is detected (simultaneous create), a MobilettoOrmSyncError will be throw
const newUser = repository.create({
username: username,
email: email,
password: 'some_hashed_password'
})
// Find by username. This works because the field has 'index: true'
const foundByUsername = repository.findBy('username', username)
// Find by email. This works because the field has 'index: true'
const foundByEmail = repository.findBy('email', email)
// Find all accounts
const everyone = repository.findAll()
// Find all accounts, even removed ones
const everyone = repository.findAllIncludingRemoved()
// Find by arbitrary predicate
const matches = repository.find(obj => functionThatReturnsTrueIfObjectMatches(obj))
// Find by arbitrary predicate, including removed objects
const matchesIncludingRemoved = repository.find(obj => predicate(obj), { removed: true })
// When creating changes, you must always specify the 'id' of the object to update
// But alternate IDs (see below) will be used if present
// Any other changes are optional
const changes = {
username,
bio: 'this is my biography'
}
// When calling 'update' you must supply the previous version, this helps avoid race conditions
// If a race condition is detected (simultaneous changes), a MobilettoOrmSyncError will be throw
const updatedUser = repository.update(changes, newUser.version)
// When calling 'remove' you must supply the previous version, this helps avoid race conditions
// If a race condition is detected (simultaneous changes), a MobilettoOrmSyncError will be throw
// The tombstone retains the object ID, ctime
const tombstone = repository.remove(username, updatedUser.version)
// Call 'purge' to clean up all the files. You must call 'remove' before calling 'purge'
// The following are all equivalent statements. Note that in our example, username was the
// object ID, and is thus also the tombstone id
const purged1 = repository.purge(tombstone)
const purged2 = repository.purge(tombstone.id)
const purged3 = repository.purge(username)
The repositoryFactory function is the way to start working with mobiletto-orm
If you're unfamiliar with how to create mobiletto connections, now is a great time to read up. It's fairly simple.
When you create a repositoryFactory, you pass an array of mobiletto connections, or an async function that
returns a Promise that resolves to an array of mobiletto connections.
The typeName property is a string that designates the name of the type.
Type names must be globally unique within your app.
Type names cannot contain the % or ~ characters.
Every type has some built-in fields:
Within a type definition object that you might pass to the repository function, the fields property
is a JSON object, where the keys are the field names, and the values are objects that describes that
field's configuration.
The simplest field declaration is
myAnythingField: {}
This allows anything to be stored in the field. The field can also be omitted or set to null.
The next simplest field declaration is:
myRequiredField: { required: true }
This creates a field that is required. Calls to create or update where the object passed in
does not define this field (or where the field's value is null or the empty string), then a validation
error (of type MobilettoOrmValidationError) will be thrown back to the caller.
Other field configuration properties are outlined below:
myExampleField: {
# this field can only be set upon creation
# updates to this field will be silently ignored
updatable: false,
# the type of the field
# valid values are: 'string', 'number', 'boolean', 'array', 'object'
# incorrectly-typed values result in a validation errors
type: 'string',
# restrict to a specific set of values
# caveat: because this field doesn't define `required: true`, a null value is also valid
values: ['Some-Default-Value', 'foo', 'bar'],
# a separate set of labels to use, when presenting the above values in a user interface
# if not defined, the `value` array will be used
labels: ['the default thing', 'the foo thing', 'the bar thing'],
# Instead of the above separate `values` and `labels` arrays, use a single `items` array
items: [
{ value: 'Some-Default-Value', label: 'the default thing'},
{ value: 'foo', label: 'the foo thing'},
{ value: 'bar', label: 'the bar thing'}
]
# when creating a new object, use this default value if myExampleField is empty
default: 'Some-Default-Value'
}
myExampleStringField: {
control: 'password', # in a user interface, use a password field (do not show the value)
min: 10, # minimum string length of 10 characters
max: 200, # maximum string length of 200 characters
regex: /^[A-Z]+$/gi # values must match this regex
}
myExampleNumberField: {
minValue: 100, # value must be greater than or equal to this minimum numeric value
max: 1000, # value must be less than or equal to this maximum numeric value
regex: /^[\d]+$/gi # values must match this regex
}
myMultivaluedField: {
# value must be an array of these values
# note: if required is false/undefined, then an empty or null array is also valid
multi: ['apple', 'banana', 'peach', 'plum', 'eggplant', 'squash', 'durian', 'pear']
}
The type property of a field definition determines what values are allowed when calling create or update.
The type can be string, number, boolean, array, or object
The id property always has a type of string
You usually don't have to set the type on a field, because it can be implied:
min, max or regex property, the field's implied type is stringminValue or maxValue property, the field's implied type is numberdefault value, the field's implied type will be the type of the default valuevalues array of valid values, the field's implied type will be the type of the first element in the arraytype and none of the above applies, the field's type will be stringThe control field is a suggestion to other code about what kind of user-interface control would be best
to set the value for this field.
The control can be:
text: a text box. the default value if nothing more specific can be determinedpassword: a text box that does not show its contents to the userlabel: a read-only display view of the valuetextarea: a larger text editing areaselect: select one item from a listmulti: multi-select 1+ items from a listflag: a yes/no valuehidden: do not show this field at all in a user interfacesystem: do not show this field at all in a user interface, even to admins/superusersIf no control is set on a field, the default control is:
boolean, then the control is flagmulti array, then the control is multivalues array, then the control is select (for example a single-selection drop-down)password, then the control is passwordcontrol is textThese type definition properties are optional.
The basePath property specifies a directory prefix when writing to the mobiletto connections.
The default basePath is '' (no prefix).
The maxVersions property specifies how many (most recent) versions of an object will be retained.
Older versions are deleted. The default maxVersions is 5.
The minWrites property specifies how many of the underlying storage must have a successful write
to consider a create/update operation a success.
If fewer than this many writes succeed, the entire operation fails and any successful writes are deleted.
The default value is 0, which means that all writes must succeed. Set to 1 and only a single write must succeed.
The alternateIdFields property is an array of strings. If an object is passed to create or update and
does not have an id field, but does have one of these fields, then the first field that has a non-empty
string value will be used as the id.
The default set of alternateIdFields is: ['name', 'username', 'email']
If you prefer that a particular TypeDef should always require an explicitly set id, then
set alternateIdFields to [] or null on your type definition object.
The name of the type, given by typeName, and whatever value the id field holds will become part of the
underlying filename to the JSON representation of the object.
This means that the typeName and the id field must be coerced into a filesystem-friendly names.
mobiletto-orm coerces these values using: encodeURIComponent(id).replaceAll('%', '~')
This invocation ensures that repeated invocations yield the same result.
Because of a subtle collision risk if typeName or id value contains a literal % or ~ character,
these characters are not allowed in typeName or id values
FAQs
mobiletto object-relational-mapper (ORM)
The npm package mobiletto-orm receives a total of 182 weekly downloads. As such, mobiletto-orm popularity was classified as not popular.
We found that mobiletto-orm demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Multiple high-impact npm maintainers confirm they have been targeted in the same social engineering campaign that compromised Axios.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.