mimani / mongoose-diff-history Goto Github PK
View Code? Open in Web Editor NEWManage Mongo Collection diff History and versions
License: Apache License 2.0
Manage Mongo Collection diff History and versions
License: Apache License 2.0
I noticed there was something wrong with the diffs stored in histories documents for certain fields on my mongoose Model
I put some console.logs in the saveDiffHistory
function to try to debug:
//currentObject is truncated for readability
currentObject = {
disciplines: [
{
_id: 'D1',
createdAt: "2020-08-28T04:35:25.148Z",
updatedAt:" 2020-10-06T19:32:33.813Z",
name: 'D1',
moreInfo: []
},
],
otherId2: '12345a',
otherId: 12345,
createdAt: "2020-09-18T17:00:31.238Z",
updatedAt: "2020-10-13T22:32:08.122Z",
__v: 0,
answerMap: {
D1: {
SOL08: 'SOL_ANS08-4',
SOL09: 'SOL_ANS09-3'
},
D4: {
SEC01: 'SEC_ANS01-2',
SEC02: 'SEC_ANS02-1',
},
D3: { INF01: 'INF_ANS01-3' }
}
}
queryUpdate = {
'$set': {
'answerMap.D1.SOL09': 'SOL_ANS09-1',
updatedAt: 2020-10-13T22:18:47.381Z
},
'$setOnInsert': { createdAt: 2020-10-13T22:18:47.381Z }
}
keysToBeModified = [ 'answerMap.D1.SOL09', 'updatedAt', 'createdAt' ]
// this is the "original object to diff"
// dbObject is NOT complete - missing the answerMap entry
dbObject = {
updatedAt: 2020-10-13T22:18:14.051Z,
createdAt: 2020-09-18T17:00:31.238Z
}
updateObject = {
updatedAt: 2020-10-13T22:18:47.381Z,
createdAt: 2020-09-18T17:00:31.238Z,
answerMap: { D1: { SOL09: 'SOL_ANS09-1' } }
We can see that dbObject
is missing the entry answerMap: { D1: { SOL09: 'SOL_ANS09-3' } }
I think this might be a bug with lodash.pick (v4.4.0 - the latest version available). When I replace lodash.pick
with the pick
from the full lodash library (v4.17.15) it works as expected.
I get an error immediately after installation:
- error Error: Cannot find module '../package.json'
at webpackEmptyContext (C:\...\...\MyProject\.next\server\app\api\cards\route.js:55:10)
at eval (webpack-internal:///(sc_server)/./node_modules/jsondiffpatch/src/main.js:38:105)
at (sc_server)/./node_modules/jsondiffpatch/src/main.js (C:\...\MyProject\.next\server\app\api\cards\route.js:367:1)
at __webpack_require__ (C:\...\MyProject\.next\server\webpack-runtime.js:33:43)
at eval (webpack-internal:///(sc_server)/./node_modules/mongoose-diff-history/diffHistory.js:9:22)
at (sc_server)/./node_modules/mongoose-diff-history/diffHistory.js (C:\...\MyProject\.next\server\app\api\cards\route.js:409:1)
at __webpack_require__ (C:\...\MyProject\.next\server\webpack-runtime.js:33:43)
at eval (webpack-internal:///(sc_server)/./src/app/api/CardModel.ts:10:21)
at (sc_server)/./src/app/api/CardModel.ts (C:\...\MyProject\.next\server\app\api\cards\route.js:1377:1)
at __webpack_require__ (C:\...\MyProject\.next\server\webpack-runtime.js:33:43)
at eval (webpack-internal:///(sc_server)/./src/app/api/dbModels.ts:8:68)
at (sc_server)/./src/app/api/dbModels.ts (C:\...\MyProject\.next\server\app\api\cards\route.js:1410:1)
at __webpack_require__ (C:\...\MyProject\.next\server\webpack-runtime.js:33:43)
at eval (webpack-internal:///(sc_server)/./src/app/api/cards/route.ts:5:67)
at (sc_server)/./src/app/api/cards/route.ts (C:\...\MyProject\.next\server\app\api\cards\route.js:1399:1)
at __webpack_require__ (C:\...\MyProject\.next\server\webpack-runtime.js:33:43)
at eval (webpack-internal:///(sc_server)/./node_modules/next/dist/build/webpack/loaders/next-app-loader.js?name=app%2Fapi%2Fcards%2Froute&page=%2Fapi%2Fcards%2Froute&appPaths=&pagePath=private-next-app-dir%2Fapi%2Fcards%2Froute.ts&appDir=C%3A%5CUsers%5Cyedid%5CDocuments%5CProjects%5Csharesphere%5Csrc%5Capp&pageExtensions=tsx&pageExtensions=ts&pageExtensions=jsx&pageExtensions=js&rootDir=C%3A%5CUsers%5Cyedid%5CDocuments%5CProjects%5Csharesphere&isDev=true&tsconfigPath=tsconfig.json&basePath=&assetPrefix=&nextConfigOutput=&preferredRegion=&middlewareConfig=e30%3D!:15:129)
at (sc_server)/./node_modules/next/dist/build/webpack/loaders/next-app-loader.js?name=app%2Fapi%2Fcards%2Froute&page=%2Fapi%2Fcards%2Froute&appPaths=&pagePath=private-next-app-dir%2Fapi%2Fcards%2Froute.ts&appDir=C%3A%5CUsers%5Cyedid%5CDocuments%5CProjects%5Csharesphere%5Csrc%5Capp&pageExtensions=tsx&pageExtensions=ts&pageExtensions=jsx&pageExtensions=js&rootDir=C%3A%5CUsers%5Cyedid%5CDocuments%5CProjects%5Csharesphere&isDev=true&tsconfigPath=tsconfig.json&basePath=&assetPrefix=&nextConfigOutput=&preferredRegion=&middlewareConfig=e30%3D! (C:\...\MyProject\.next\server\app\api\cards\route.js:139:1)
at __webpack_require__ (C:\...\MyProject\.next\server\webpack-runtime.js:33:43)
at __webpack_exec__ (C:\...\MyProject\.next\server\app\api\cards\route.js:1464:39)
at C:\...\MyProject\.next\server\app\api\cards\route.js:1465:28
at Object.<anonymous> (C:\...\MyProject\.next\server\app\api\cards\route.js:1468:3)
at Module._compile (node:internal/modules/cjs/loader:1254:14)
at Module._extensions..js (node:internal/modules/cjs/loader:1308:10)
at Module.load (node:internal/modules/cjs/loader:1117:32)
at Module._load (node:internal/modules/cjs/loader:958:12)
at Module.require (node:internal/modules/cjs/loader:1141:19)
at require (node:internal/modules/cjs/helpers:110:18)
at requirePage (C:\...\MyProject\node_modules\next\dist\server\require.js:112:75)
at C:\...\MyProject\node_modules\next\dist\server\load-components.js:80:84
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at async loadComponentsImpl (C:\...\MyProject\node_modules\next\dist\server\load-components.js:80:26)
at async DevServer.findPageComponentsImpl (C:\...\MyProject\node_modules\next\dist\server\next-server.js:772:36)
at async DevServer.findPageComponents (C:\...\MyProject\node_modules\next\dist\server\dev\next-dev-server.js:1269:20)
at async DevServer.renderPageComponent (C:\...\MyProject\node_modules\next\dist\server\base-server.js:1354:24)
at async DevServer.renderToResponseImpl (C:\...\MyProject\node_modules\next\dist\server\base-server.js:1398:32)
at async DevServer.pipeImpl (C:\...\MyProject\node_modules\next\dist\server\base-server.js:645:25)
at async Object.fn (C:\...\MyProject\node_modules\next\dist\server\next-server.js:1153:21)
at async Router.execute (C:\...\MyProject\node_modules\next\dist\server\router.js:315:32)
at async DevServer.runImpl (C:\...\MyProject\node_modules\next\dist\server\base-server.js:619:29)
at async DevServer.run (C:\...\MyProject\node_modules\next\dist\server\dev\next-dev-server.js:908:20)
at async DevServer.handleRequestImpl (C:\...\MyProject\node_modules\next\dist\server\base-server.js:546:20) {
digest: undefined
}
I am working on a project and would like to incorporate mongoose-diff-history in that project.
When looking at the license (GPL-2.0) it would require me to release the full source of my project.
I have no intention to do this with regards to my own private written code for this specific project.
Are there perhaps plans to switch to a different license which would allow me to use this library in this closed-source project?
have tried both ways :-
// import * as diffHistory from "mongoose-diff-history/diffHistory"
// var diffHistory = require('mongoose-diff-history/diffHistory')
tried to declare in typing.d.ts also.
issue remain the same.
it always got stuck at this :-
Mongoose: user.findOne({ _bsontype: 'ObjectID', id: <Buffer 5f c8 e8 1d 50 b7 91 51 ea 39 8c b8>}, { projection: {} })
Mongoose: user.find({ _id: ObjectId("5fdb10f4bf8780f7ee0a7799") }, { projection: {} })
Note:- I am trying to setup it in my existing project and trying to update my old documents
How to include user to the history ?
I have user object in req.user object how to include that to history ?
after upgrading to version 1.6 if upsert:true will throw the following exception
/Users/dev/node_modules/mongodb/lib/utils.js:132
throw err;
^
Error: The given operator "$setOnInsert" is not implemented yet.
at Function.assignUpdateOperation ()
error coming from "assign(dbObject, queryObject._update)," ?
try it with mongoose "mongoose": "^4.13.18" and "mongoose": "^5.4.17"
rolling back to "mongoose-diff-history": "^1.5.3 - the program works
I am using mongoose v4.3.4. When I want to update my model using findOneAndUpdate
, I am continuously getting this error message. #error #question
mongoose-diff-history/package.json
Line 23 in 80000e4
You should change from git://github.com/ into https://github.com/, builds and github actions are failing
AS suggested here https://stackoverflow.com/questions/70663523/the-unauthenticated-git-protocol-on-port-9418-is-no-longer-supported
Mongoose 5 was released a few months ago.
Is there any intention of updating the mongoose-diff-history to support it?
In any case, here's the how-to-upgrade: https://github.com/Automattic/mongoose/blob/master/migrating_to_5.md
I could try to update, I don't think there should be big issues with it :-)
.update works like charm, however, my codebase is full of updateOne & updateMany.
I noticed .updateOne in the tests, but I can't seem to get it to work.
const diffHistory = require('mongoose-diff-history/diffHistory')
requestSchema = new Schema({...})
requestSchema.plugin(diffHistory.plugin);
That logs the difference
await Request.update({ _id: request._id }, { isSwitched: true, switchedToRequestId: newRequest._id })
But the following doesn't
await Request.updateOne({ _id: request._id }, { isSwitched: true, switchedToRequestId: newRequest._id })
I have this document where i saved the URL and sources in array of object. Here is an example of how my json look like:
{
"data":
[
{
"firstName":"John",
"lastName":"Doe",
"sources": [{
"url": "https://www.successcds.net/learn-english/articles-definition-examples-types-exercises.html",
"title":"URL example",
},
{
"url": "https://websitebuilders.com/how-to/web-at-a-glance/url-examples/",
"title":"URL example 2"
}
]
}
This is what happened in diff when I try to edit and save the url from
https://websitebuilders.com/how-to/web-at-a-glance/url-examples/ to https://websitebuilders.com/how-to/web-at-a-glance/url-examples/#Example_1httpswebsitebuilderscom:
{
"diff":{
"sources": {"1": "url": ["@@-57,8+57,41@@xamples/+#Example_1httpswebsitebuilderscom", "0", "2"]}
}
}
If you notice, the url are completely butchered. It seems like it combines both url (original and updated) into one encoded url. Somehow it happen to some of the url only especially the long one. Can you check please?
`const schema = new mongoose.Schema({
exString: String,
exBoolean: Boolean,
exNumber: Number,
exObject: {
text: String,
value: Number
},
exArrayOfString: [String],
exArrayOfObject: [{
text: String,
value: String
}]
}, {
timestamps: true,
toJSON: { virtuals: true },
toObject: { virtuals: true }
})`
Save not trigger when create entity. So its not working when I want to save in history information about when item was created. It just keeps the updates.
mongoose-diff-history/diffHistory.js
Line 268 in 1fb081a
Hi Saurabh,
As it is now, username
is a string in the History model. In a real world scenario you want to save more information about the user then just the username. For example a user object might look like:
{
'username': 'peter',
'fullname': 'Peter Kalkman',
'email': '[email protected]',
'mobile': '06-11111111',
'roles': [
'ADMIN', 'USER'
]
}
Any thoughts on allowing an ANY schema for user?
I was searching some method to store history my data, so I found your plugin and i like the approach you take.
However, when I update some document that has an array of subdocuments attached to it, this array is also stored in the histories
collection, even no change has occured to the subdocuments.
I've read #15 , but it's not my case, since no data is changed in my subdocuments array.
Let me explain how it works.
I have a collection for projects and for each project, a users array that contains objects that represent the role and permissions of a user within the project. My document is something like:
{
"_id" : ObjectId("5a8f35ca37e1fd04963a29ec"),
"users" : [
{
"user" : ObjectId("5a8f35c937e1fd04963a29d6"),
"role" : "Owner",
"_id" : ObjectId("5a8f35ca37e1fd04963a29ed"),
"permissions" : [
"members_edit",
"members_remove",
"project_edit"
],
"createdAt" : ISODate("2018-02-26T18:25:57.088Z"),
"updatedAt" : ISODate("2018-02-26T18:25:57.088Z")
},
{
"updatedAt" : ISODate("2018-02-26T18:28:37.901Z"),
"createdAt" : ISODate("2018-02-26T18:26:22.645Z"),
"user" : ObjectId("5a8f35c937e1fd04963a29d5"),
"role" : "Manager",
"_id" : ObjectId("5a94514e29010702bf37145d"),
"permissions" : [
"project_edit"
]
}
],
"description" : "Some cool description",
"name" : "Project #1",
"__v" : 0,
"createdAt" : ISODate("2018-02-26T18:23:17.659Z"),
"updatedAt" : ISODate("2018-02-26T19:18:04.416Z")
}
And when I update the description, for example, the document in the histories
collection is:
{
"_id" : ObjectId("5a945d6cb817aa035e9eec18"),
"updatedAt" : ISODate("2018-02-26T19:18:04.428Z"),
"createdAt" : ISODate("2018-02-26T19:18:04.428Z"),
"collectionId" : ObjectId("5a8f35ca37e1fd04963a29ec"),
"collectionName" : "Project",
"diff" : {
"description" : [
"Some cool description",
"Another cool description"
],
"users" : [
[
{
"permissions" : [
"members_edit",
"members_remove",
"project_edit"
],
"createdAt" : "2018-02-26T18:25:57.088Z",
"_id" : "5a8f35ca37e1fd04963a29ed",
"role" : "Owner",
"user" : "5a8f35c937e1fd04963a29d6"
},
{
"permissions" : [
"project_edit"
],
"_id" : "5a94514e29010702bf37145d",
"role" : "Manager",
"user" : "5a8f35c937e1fd04963a29d5",
"createdAt" : "2018-02-26T18:26:22.645Z"
}
],
0,
0
]
},
"user" : ObjectId("5a8f35c937e1fd04963a29d6"),
"version" : 0,
"__v" : 0
My model and controller is as it follows:
/* model.js */
const mongoose = require('mongoose')
const diffHistory = require('mongoose-diff-history/diffHistory')
const ProjectUserSchema = mongoose.Schema({
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User' },
role: { type: String, trim: true },
permissions: [String]
},
{
timestamps: true
})
const ProjectSchema = mongoose.Schema({
name: { type: String },
description: { type: String },
users: [ ProjetUserSchema ]
},
{
usePushEach: true,
timestamps: true
})
ProjectSchema.plugin(diffHistory.plugin, { omit: ['updatedAt', 'users.updatedAt'] });
module.exports = mongoose.model('Project', ProjectSchema)
/* controller.js */
...
const updateProject = (req, res, next) => {
Project.findOne({ _id: req.params.id }, '-users', (err, project) => {
if (err) return next(err)
/* check if user can edit the project */
if (userHasPermission(project, req.user, 'project_edit')) {
for (let attribute in req.body) {
project[attribute] = req.body[attribute]
}
project.__user = req.user._id
project.save(err => {
if (err) return next(err)
res.json({ error: false, project_id: project._id })
})
} else {
res.status(403).json({ message: 'Forbidden' }) // user is forbidden
}
})
}
...
Please, could you take a look to check what I'm doing wrong?
Also, note that I pass the field updatedAt
to the plugin to omit. When I use mongoose timestamps, the updatedAt
always is updated and put in histories
collection. Is there some way to ignore updatedAt
field natively in the plugin?
Hello,
changedAt is not returning anything because it's being saved in mongo as updatedAt
Thanks.
Are either or both supported?
is it possible to allow the mongoose-history to be included in the a transaction https://mongoosejs.com/docs/transactions.html,
e.g. if the original changes to the document was aborted, the history will also be rollback (removed)
Looking at your code, it should be possible by passing __session to your code in diffHistory.js
the program just need to pass __session:session along with __user and __reason as option and pick up by your program and include the __session during save and made history part of the transaction.
const saveDiffObject = (currentObject, original, updated, opts, metaData) => {
const { __user: user, __reason: reason, __session: session } = metaData || currentObject;
.......
return History.findOne({ collectionId, collectionName })
.sort('-version')
.then(lastHistory => {
const history = new History({
collectionId,
collectionName,
diff,
user,
reason,
version: lastHistory ? lastHistory.version + 1 : 0
});
if (session) {
// console.log('history in transaction');
return history.save({ session });
}
return history.save();
});
};
I find this library really helpful in storing history of documents.
However, when there are any changes made to the document and we pass in a Javascript object, mongoose will replace the old _id properties in subdocument and replace it with new values.
For example:
I have a document as follows:
"completionCriteria": [
{
"formula" : {
"quantifier" : "all"
},
"_id" : ObjectId("5996d4524d98c8001558c7b9"),
"action" : "complete",
"requiredCondition" : "complete"
},
{
"formula" : {
"quantifier" : "any"
},
"_id" : ObjectId("5996d4524d98c8001558c7b8"),
"action" : "inComplete",
"requiredCondition" : "inComplete"
},
{
"formula" : {
"quantifier" : "all"
},
"_id" : ObjectId("5996d4524d98c8001558c7b7"),
"action" : "notAttempted",
"requiredCondition" : "notAttempted"
}
]
Now when I change the document without making any changes to the completionCriteria array, the array remains the same but the _id property array elements change and thus this gets mentioned in History document as follows:
"completionCriteria": {
"0" : {
"_id" : [
"5996d4524d98c8001558c7b9",
"5996d4914d98c8001558c7d5"
]
},
"1" : {
"_id" : [
"5996d4524d98c8001558c7b8",
"5996d4914d98c8001558c7d4"
]
},
"2" : {
"_id" : [
"5996d4524d98c8001558c7b7",
"5996d4914d98c8001558c7d3"
]
},
"_t" : "a"
}
Is there any way to ignore the _id property because this is automatically generated by mongoose and thus can be ignored?
When picking out fields for the original object vs updated object, two issues arise when a '$' or '$[]' is present in the update query:
dbObject
) doesn't find the field leaving the object missing that field.updatedObject
) assumes that the key name is '$', and you cannot use '$' as a field name for the log object.I have follow the docs and it was quite simple so I highly doubt its has to do with my code. I'm on the latest version of mongoose-diff-history (1.6.1)
Here's what i have in my code:
router.get("/:company_id/histories",
function (req, res, next) {
Company.findById(req.params.company_id).exec(function (err, company) {
if (!company) {
return next(err)
}
diffHistory.getHistories('Company', req.params.company_id, ['description'], function (err, histories) {
if (err){ return next(err);}
res.json(histories);
})
})
});
mongoose-dff-history doesn't work for 'create' or 'save' operation. It should log it into history table for save operation too.
I think in diffHistory.js
schema.pre('save', function (next) {
if (this.isNew) {
console.log("---------in save-------");
this.constructor
.findOne({ _id: this._id })
.then(original => saveDiffObject(this, original, this, opts))
.then(() => next())
.catch(next);
}
return next();
});
this would be the solution, it works for me.
Can we update it in the package so that in latest version it will be available or can we have some configuration option to enable history log for save/create operation too.
I added this to an existing project and it throws the following error on save/update:
UnhandledPromiseRejectionWarning: Error: key $set must not start with '$'
npm install mongoose-diff-history -s
var mongoose = require('mongoose'),
diffHistory = require('mongoose-diff-history/diffHistory'),
schema = new mongoose.Schema({ ... });
schema.plugin(diffHistory.plugin);
Hello mimani
// try to find an id property, otherwise just use the index in the array const objectHash = (obj, idx) => obj._id || obj.id ||
$$index: ${idx}`;
const diffPatcher = require('jsondiffpatch').create({ objectHash, textDiff:{minLength:1000} });
`
Did you planing to implement functionality that allow to path options to jsondiffpatch.create?
Hi.
I have a document, its schema looks like this.
new mongoose.Schema({
items: [
{
name: String,
currentValue: Number
}],
totalCurrentValue: Number,
}, {timestamps: true});
I've added items.currentValue
and totalCurrentValue
to the omit options, and its omitting totalCurrentValue
correctly, but its not ignoring changes on 'items.currentValue'.
Note: By default, mongoose adds and _id field on every array item, i've checked it, and the _id its not modified, because i'm only modifying the currentValue field, sometimes adding it or removing or just updating, and not modifying the entire array, so this its not the reason of the issue.
The diff object looks like this, when i add the items.currentValue'
field.
{
items: {
0: {
currentValue: 2,
},
_t: "a"
}
Thanks in advice
Is it possible to release a new version/tag with new license information?
Thx a lot :)
FeverFerry
I can't download the package from npm.
I stay stuck at this:
/usr/bin/node /usr/lib/node_modules/npm/bin/npm-cli.js install --scripts-prepend-node-path=auto
[ ...............] \ fetchMetadata: sill resolveWithNewModule [email protected] checking installable status
Then it fails:
/usr/bin/node /usr/lib/node_modules/npm/bin/npm-cli.js install --scripts-prepend-node-path=auto
npm ERR! Error while executing:
npm ERR! /usr/bin/git ls-remote -h -t git://github.com/izigibran/omit-deep.git
npm ERR!
npm ERR! fatal: unable to connect to github.com:
npm ERR! github.com[0: 140.82.121.4]: errno=Connection timed out
npm ERR!
npm ERR!
npm ERR! exited with error code: 128
I see that since 2022 Github doesn't accept git://
urls anymore, and that they should be replaced with https://
: https://stackoverflow.com/a/72391698/13976355
Maybe it's the case for this package @mimani ?
Hi @mimani
Great module! Is there a way to store diffs in a separate collection/database so that queries to the main collection don't become expensive over time as historical information increases?
Kind regards,
Issues with failing tests on latest (v5 AToW) version of Mongoose.
See: #47
Hello. I'm planning to fork and make some significant changes to this package and was wondering if:
If requested I would be happy to offer you more details about what I intend to change.
Is it possible to add custom index by configuration (for example for the collectionId) else this will be a nice feature.
Hi,
Great library, works fantastic!!!
Any plans to support deletion (removal) of documents in the near future?
Regards,
Peter Kalkman
To reproduce, update the version in /example/package.json to "mongoose-diff-history": "1.5.3" and run the app node server.
You get the following error when trying to search for history: TypeError: History.find(...).lean(...).cursor is not a function.
Also the documentation for the GET/PUT requests in the README file need updating to include the employees path, i.e. localhost:3000/employees/:employeeId.
TypeError: History.find(...).lean(...).cursor is not a function at Object.getHistories (C:\Users\..\Downloads\mongoose-diff-history-master\mongoose-diff-history-master\example\node_modules\mongoose-diff-history\diffHistory.js:125:10) at C:\Users\..\Downloads\mongoose-diff-history-master\mongoose-diff-history-master\example\routes\employees.js:149:21 at Query.<anonymous> (C:\Users\..\Downloads\mongoose-diff-history-master\mongoose-diff-history-master\example\node_modules\mongoose\lib\query.js:2149:28) at C:\Users\..\Downloads\mongoose-diff-history-master\mongoose-diff-history-master\example\node_modules\kareem\index.js:177:19 at C:\Users\..\Downloads\mongoose-diff-history-master\mongoose-diff-history-master\example\node_modules\kareem\index.js:109:16 at _combinedTickCallback (internal/process/next_tick.js:131:7) at process._tickCallback (internal/process/next_tick.js:180:9)
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.