{"id":13806001,"url":"https://github.com/ampatspell/ember-cli-sofa","last_synced_at":"2025-10-28T15:53:18.014Z","repository":{"id":57223485,"uuid":"71108483","full_name":"ampatspell/ember-cli-sofa","owner":"ampatspell","description":"CouchDB persistence library for Ember.js","archived":false,"fork":false,"pushed_at":"2017-09-07T16:25:06.000Z","size":1471,"stargazers_count":4,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-08-09T05:28:58.245Z","etag":null,"topics":["couchdb","emberjs","javascript"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ampatspell.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-10-17T06:52:07.000Z","updated_at":"2020-12-12T14:39:49.000Z","dependencies_parsed_at":"2022-08-30T02:10:25.924Z","dependency_job_id":null,"html_url":"https://github.com/ampatspell/ember-cli-sofa","commit_stats":null,"previous_names":[],"tags_count":29,"template":false,"template_full_name":null,"purl":"pkg:github/ampatspell/ember-cli-sofa","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ampatspell%2Fember-cli-sofa","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ampatspell%2Fember-cli-sofa/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ampatspell%2Fember-cli-sofa/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ampatspell%2Fember-cli-sofa/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ampatspell","download_url":"https://codeload.github.com/ampatspell/ember-cli-sofa/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ampatspell%2Fember-cli-sofa/sbom","scorecard":{"id":190437,"data":{"date":"2025-08-11","repo":{"name":"github.com/ampatspell/ember-cli-sofa","commit":"4e497ddb4eaee157f22ea75ffba39b9138689282"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":0,"reason":"Found 0/13 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.md:0","Info: FSF or OSI recognized license: MIT License: LICENSE.md:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 21 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-16T20:33:50.334Z","repository_id":57223485,"created_at":"2025-08-16T20:33:50.334Z","updated_at":"2025-08-16T20:33:50.334Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":275131119,"owners_count":25410832,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-14T02:00:10.474Z","response_time":75,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["couchdb","emberjs","javascript"],"created_at":"2024-08-04T01:01:07.041Z","updated_at":"2025-10-28T15:53:12.970Z","avatar_url":"https://github.com/ampatspell.png","language":"JavaScript","readme":"# ember-cli-sofa\n\n\u003e This documentation is also available at http://sofa.amateurinmotion.com\n\n`Sofa` is full featured and easy to use CouchDB model persistence library for Ember.js.\n\nIt makes it easy to map CouchDB documents to application models, query and persist them, manage queriable to-one, to-many relationships and relationship inverses, create and load document attachments. Also create, drop databases, authenticate users by using CouchDB sessions, create and update design documents (javascript and mango views).\n\nThis is is one of the easiest ways to start coding your Ember.js app which persists data in the cloud. All you need is CouchDB running somewhere. You can also deploy Ember.js app itself as an CouchApp in the same CouchDB database and, with simple CouchDB and optionally nginx configuration, serve it to your users.\n\nBoth CouchDB 1.6 and 2.0 are supported.\n\nWhile Sofa has already most of commonly required features implemented, there is quite a few things in the roadmap like:\n\n* collection and relationship pagination, search, filtering support\n* local PouchDB support\n* document changes listener for local PouchDB and remote CouchDB instances\n* per-database model name to document type mapping, embedded models and so on\n\n## Example applications\n\n* [Portfolio](https://github.com/ampatspell/portfolio) – Photography portfolio app (which showcases model inheritance, attachments, relationships, queries, collections)\n\n## Install\n\n```bash\nember install ember-cli-sofa\n```\n\n## Quickstart\n\nThis section will guide you through sofa configuration and most common features.\n\nLet's start with setting it up.\n\n### Service\n\n\u003e Be sure to remove `ember-data` addon from your `package.json` as Ember Data also registers `store` service and those two will clash. Or just name your sofa service differently.\n\nEasiest way to start using sofa is to create a `store` service by extending `Store`:\n\n``` javascript\n// services/store.js\nimport { Store } from 'sofa';\n\nexport default Store.extend({\n\n  databaseOptionsForIdentifier(identifier) {\n    let url = 'http://127.0.0.1:5984';\n    if(identifier === 'main') {\n      return { url, name: 'great-app' };\n    }\n  }\n\n});\n```\n\nThe only required override is `databaseOptionsForIdentifier` which is used to determine CouchDB `url` and database `name` for given database `identifier`. This is done because this way actual database `url` and `name` is detached from database `identifier` which is used throughout the application and can be easily changed based on environment and deployment configuration.\n\nNow we have a service, let's declare a global `store` variable and try it out in the console:\n\n``` javascript\n// instance-initializers/sofa-develop.js\nexport default {\n  name: 'sofa:develop',\n  initialize(app) {\n    window.store = app.lookup('service:store');\n    window.log = console.log.bind(console);\n    window.err = err =\u003e console.error(err.toJSON ? err.toJSON() : err.stack);\n  }\n};\n```\n\n### Console\n\nLet's start by logging in as an CouchDB `_admin`. This might come in handy if database does not exist. Open your browser's console and type this thing in:\n\n``` javascript\nstore.get('db.main.couch.documents.session').save('\u003cadmin\u003e', '\u003cpassword\u003e').then(log, err)\n// → {ok: true, name: ...}\n```\n\nNow let's make sure that `main` database exists:\n\n``` javascript\nstore.get('db.main.documents.database').info().then(log, err)\n// depending if database actually exists:\n//  → {db_name: \"great-app\", ... }\n//  → {error: \"not_found\", reason: \"Database does not exist.\", status: 404}\n```\n\nSo, if database doesn't exist, we can easily create it by calling `create()`:\n\n``` javascript\nstore.get('db.main.documents.database').create().then(log, err)\n// → {ok: true}\n```\n\nGood, now that we have created the database and it's time to start filling it up with some random documents.\n\nFor now reason, let's save a doc:\n\n``` javascript\nstore.get('db.main.documents').save({\n  _id: 'first',\n  message: 'To whom it may concern: It is springtime. It is late afternoon.',\n  author: 'Kurt Vonnegut'\n}).then(log, err)\n// → {ok: true, id: \"first\", rev: \"1-8ed895a12ea8c1389116bcbaff0b7262\"}\n```\n\nLoad the same doc:\n\n``` javascript\nstore.get('db.main.documents').load('first').then(log, err)\n// → {\n//     _id: \"first\",\n//     _rev: \"1-8ed895a12ea8c1389116bcbaff0b7262\",\n//     message: \"To whom it may concern: It is springtime. It is late afternoon.\",\n//     author: \"Kurt Vonnegut\"\n//   }\n```\n\nAnd delete it:\n\n``` javascript\nstore.get('db.main.documents').delete('first', '1-8ed895a12ea8c1389116bcbaff0b7262').then(log, err)\n// → {ok: true, id: \"first\", rev: \"2-39d1e13f087a31499c222f8c4657fdb1\"}\n```\n\nEasy enough, right? See JSON.* sections below for overview about raw CouchDB JSON API wrappers.\n\nBut now it's time to create a model class and let sofa manage all the document saving, loading, querying details.\n\n### Models\n\nIn previous section we where playing with a document which had `message` and `author`, let's reimplement the same by using two models: `Message` and `Author`.\n\n``` javascript\n// models/message.js\nimport { Model, attr, belongsTo } from 'sofa';\n\nexport default Model.extend({\n\n  text: attr('string'),\n  author: belongsTo('author', { inverse: 'messages' }),\n\n});\n```\n\n``` javascript\n// models/author.js\nimport { Model, attr, hasMany } from 'sofa';\n\nexport default Model.extend({\n\n  fullName: attr('string'),\n  messages: hasMany('message', { inverse: 'author', query: 'author-messages' }),\n\n});\n```\n\n``` javascript\n// queries/author-messages.js\nimport Ember from 'ember';\nimport { Query } from 'sofa';\n\nconst {\n  computed\n} = Ember;\n\nexport default Query.extend({\n\n  find: computed('model.docId', function() {\n    let author = this.get('model.docId');\n\n    // mango\n    // return { selector: { author } } };\n\n    // ddoc\n    return { ddoc: 'message', view: 'by-author', key: author };\n  }),\n\n});\n```\n\n``` javascript\n// sofa/databases/main.js\n/* global emit */\nimport Ember from 'ember';\nimport { Database } from 'sofa';\n\nconst {\n  RSVP: { all, resolve }\n} = Ember;\n\nconst message = {\n  views: {\n    'by-author': {\n      map(doc) {\n        if(doc.type !== 'message') {\n          return;\n        }\n        emit(doc.author, null);\n      }\n    }\n  }\n};\n\nexport default Database.extend({\n\n  loginIfNeeded(name, password) {\n    if(!name) {\n      return resolve();\n    }\n    let session = this.get('couch.session');\n    session.setProperties({ name, password });\n    return session.save();\n  },\n\n  // provide `_admin` name and password if needed\n  insertDesignDocuments(name, password) {\n    return this.loginIfNeeded(name, password).then(() =\u003e {\n      let design = this.get('documents.design');\n      return all([\n        design.save('message', message)\n      ]);\n    });\n  }\n\n});\n```\n\n``` javascript\nstore.get('db.main').insertDesignDocuments('duck', 'secret duck').then(log, err);\n// → [ {ok: true, id: \"_design/message\", rev: \"1-58d85750a59785112252278496836757\", saved: true} ]\n```\n\n\u003e Note: we will be saving documents with CouchDB auto-generated _id which may differ\n\n``` javascript\ndb = store.get('db.main');\n\nauthor = db.model('author', { fullName: 'Kurt Vonnegut' });\n\nauthor.getProperties('id', 'isNew', 'isDirty')\n// → {id: undefined, isNew: true, isDirty: true}\n\nauthor.save();\n\nauthor.getProperties('id', 'isNew', 'isDirty')\n// → {id: \"930c5583a5c3693a71cd3d6aba1b32c7\", isNew: false, isDirty: false}\n\nmessage = db.model('message', { author, text: 'To whom it may concern: It is springtime. It is late afternoon.' });\n\nauthor.get('messages.firstObject') === message;\n// → true\n\nmessage.save();\nmessage.getProperties('id', 'isNew', 'isDirty')\n// → {id: \"930c5583a5c3693a71cd3d6aba1b34a4\", isNew: false, isDirty: false}\n```\n\nAnd reload the browser so there is no cached models in sofa store\n\n``` javascript\ndb = store.get('db.main');\n\ndb.load('author', '930c5583a5c3693a71cd3d6aba1b32c7').then(author =\u003e window.author = author)\nauthor.get('messages.isLoaded') // this kicks off the query load\n// → false\n// XHR finished loading: GET \"http://127.0.0.1:5984/great-app/_design/message/_view/by-author?key=%22930c5…\nauthor.get('messages.isLoaded')\n// → true\n\nmessage = author.get('messages.firstObject')\nmessage.get('text')\n// → \"To whom it may concern: It is springtime. It is late afternoon.\"\nmessage.get('author') === author\n// → true\n```\n\n\n## Model\n\n### id\n### rev\n### type\n### attachments\n### docId\n### modelName\n### database\n### state\n\n* isNew\n* isLoading\n* isLoaded\n* isDirty\n* isSaving\n* isDeleted\n* isError\n* error\n\n### serialize()\n### save()\n### load()\n### reload()\n### delete()\n### willCreate()\n### willSave()\n### willDelete()\n\n## Model Properties\n\n### attributes\n\n### id\n### prefix\n### rev\n### type\n### attr\n\n### attachments\n\n### relationships\n\n### belongsTo \u0026 hasOne\n\n### hasMany\n\n## BelongsTo Loaded Proxy\n\n### state\n### isLoading\n### isLoaded\n### isError\n### error\n### promise\n\n## HasMany Loaded Proxy\n\n### state\n### isLoading\n### isLoaded\n### isError\n### error\n### promise\n\n## HasMany Persisted Proxy\n\n### state\n### isLoading\n### isError\n### error\n\n## Collection\n\n...\n\n## Query\n\n...\n\n## Store\n\n### database(identifier) → Store.Database\n### databaseOptionsForIdentifier(identifier)\n### modelClassForName(modelName) → Model class\n### modelNames → [ String, ... ]\n### model(modelName, props) → Model\n\n## Store.Database\n\n### identifier → String\n### store → Store\n### couch → Couch\n### documents → JSON.Database\n### collection(name, opts) → Collection\n### modelClassForName(modelName) → Model class\n### model(modelName, props) → Model\n### existing(modelName, id, opts) → Model\n### load(modelName, id, opts) → Promise - Model\n### view(opts) → Promise - [ Model, ... ]\n### mango(opts) → Promise - [ Model, ... ]\n### all(opts) → Promise - [ Model, ... ]\n### find(opts) → Promise - [ Model, ... ]\n### first(opts) → Promise - [ Model, ... ]\n### push(doc, opts) → Model, status or undefined\n### security → Store.Database.Security\n### db → Store.Databases\n\n``` javascript\nlet main = store.get('db.main');\n```\n\n## Store.Couch\n\n### documents → JSON.Couch\n### url → String\n### session → Store.Couch.Session\n\n## Store.Database.Security\n\n### database → Store.Database\n### documents → JSON.Database.Security\n### admins → Security.Pair\n### members → Security.Pair\n### load() →\n### save() →\n### clear()\n### state → Object\n\n* isLoading\n* isLoaded\n* isDirty\n* isSaving\n* isError\n* error\n\n## Security.Pair\n\n### security →\n### key → String\n### names → [ String, ... ]\n### roles → [ String, ... ]\n\n## Store.Couch.Session\n\n### couch → Store.Couch\n### documents → JSON.Couch.Session\n### isAuthenticated → boolean\n### name → String\n### password → String\n### roles → [ String, ... ]\n### restore() → Promise\n### load() → Promise\n### save() → Proise\n### delete() → Promise\n### state → Object\n\n* isLoading\n* isLoaded\n* isDirty\n* isSaving\n* isError\n* error\n\n## JSON.Couch\n\n### url\n### normalizedUrl\n### session\n### request(opts)\n### info()\n### uuids(count)\n### database(name)\n\n## JSON.Couch.Session\n\n### couch\n### request(opts)\n### load()\n### save(name, password)\n### delete()\n\n## JSON.Database\n\n### couch\n### name\n### security\n### design\n### database\n### mango\n### url\n### request(opts)\n### info()\n### load(id, opts)\n### save(doc, opts)\n### delete(id, rev, opts)\n### view(ddoc, name, opts)\n### all(opts)\n\n## JSON.Database.Design\n\n### database\n### id(name)\n### load(name, opts)\n### save(name, object)\n### delete(name, opts)\n\n## JSON.Database.Security\n\n### database\n### request(opts)\n### load()\n### save(object)\n\n## JSON.Database.Database\n\n### database\n### request(opts)\n### info()\n### create(opts)\n### delete(opts)\n### recreate(opts)\n\n## JSON.Database.Mango\n\n### database\n### request(opts)\n### find(opts)\n### explain(opts)\n### save(ddoc, name, index)\n### delete(ddoc, name)\n### all()\n","funding_links":[],"categories":["Packages"],"sub_categories":["Data Management"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fampatspell%2Fember-cli-sofa","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fampatspell%2Fember-cli-sofa","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fampatspell%2Fember-cli-sofa/lists"}