forked from dachan/dach
implemented creation of boards, their listing, admin login and initialization of the database
This commit is contained in:
parent
aa72ada0ab
commit
5a1e2a7730
|
@ -1,4 +1,4 @@
|
|||
<mxfile host="Electron" modified="2023-10-24T18:05:59.655Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/22.0.2 Chrome/114.0.5735.289 Electron/25.8.4 Safari/537.36" etag="k26cYTekuvP0_SzdJTuV" version="22.0.2" type="device">
|
||||
<mxfile host="Electron" modified="2023-10-24T20:22:25.901Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/22.0.2 Chrome/114.0.5735.289 Electron/25.8.4 Safari/537.36" etag="PYxiMayFbUWG-9iEeUeq" version="22.0.2" type="device">
|
||||
<diagram id="R2lEEEUBdFMjLlhIrx00" name="Page-1">
|
||||
<mxGraphModel dx="1669" dy="479" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0" extFonts="Permanent Marker^https://fonts.googleapis.com/css?family=Permanent+Marker">
|
||||
<root>
|
||||
|
@ -57,7 +57,7 @@
|
|||
<mxRectangle width="30" height="30" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
</mxCell>
|
||||
<mxCell id="IqcACfFecUyFIL0cjSVW-14" value="threads_ids int[] NOT NULL" style="shape=partialRectangle;overflow=hidden;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;" parent="IqcACfFecUyFIL0cjSVW-12" vertex="1">
|
||||
<mxCell id="IqcACfFecUyFIL0cjSVW-14" value="threads_ids int[]" style="shape=partialRectangle;overflow=hidden;connectable=0;fillColor=none;top=0;left=0;bottom=0;right=0;align=left;spacingLeft=6;" parent="IqcACfFecUyFIL0cjSVW-12" vertex="1">
|
||||
<mxGeometry x="30" width="220" height="30" as="geometry">
|
||||
<mxRectangle width="220" height="30" as="alternateBounds" />
|
||||
</mxGeometry>
|
||||
|
|
|
@ -45,6 +45,11 @@
|
|||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
|
||||
},
|
||||
"node_modules/bcryptjs": {
|
||||
"version": "2.4.3",
|
||||
"resolved": "https://registry.npmjs.org/bcryptjs/-/bcryptjs-2.4.3.tgz",
|
||||
"integrity": "sha512-V/Hy/X9Vt7f3BbPJEi8BdVFMByHi+jNXrYkW3huaybV/kQ0KJg0Y6PkEMbn+zeT+i+SiKZ/HMqJGIIt4LZDqNQ=="
|
||||
},
|
||||
"node_modules/body-parser": {
|
||||
"version": "1.20.1",
|
||||
"resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz",
|
||||
|
@ -77,6 +82,14 @@
|
|||
"concat-map": "0.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-writer": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz",
|
||||
"integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/bytes": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
|
||||
|
@ -204,6 +217,17 @@
|
|||
"npm": "1.2.8000 || >= 1.4.16"
|
||||
}
|
||||
},
|
||||
"node_modules/dotenv": {
|
||||
"version": "16.3.1",
|
||||
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
|
||||
"integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/motdotla/dotenv?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/ee-first": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
|
||||
|
@ -285,6 +309,32 @@
|
|||
"node": ">= 0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/express-session": {
|
||||
"version": "1.17.3",
|
||||
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.3.tgz",
|
||||
"integrity": "sha512-4+otWXlShYlG1Ma+2Jnn+xgKUZTMJ5QD3YvfilX3AcocOAbIkVylSWEklzALe/+Pu4qV6TYBj5GwOBFfdKqLBw==",
|
||||
"dependencies": {
|
||||
"cookie": "0.4.2",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~2.0.0",
|
||||
"on-headers": "~1.0.2",
|
||||
"parseurl": "~1.3.3",
|
||||
"safe-buffer": "5.2.1",
|
||||
"uid-safe": "~2.1.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/express-session/node_modules/cookie": {
|
||||
"version": "0.4.2",
|
||||
"resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
|
||||
"integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/filelist": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz",
|
||||
|
@ -486,6 +536,15 @@
|
|||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "4.1.5",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
|
||||
"integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
|
||||
"dependencies": {
|
||||
"pseudomap": "^1.0.2",
|
||||
"yallist": "^2.1.2"
|
||||
}
|
||||
},
|
||||
"node_modules/media-typer": {
|
||||
"version": "0.3.0",
|
||||
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
|
||||
|
@ -494,6 +553,39 @@
|
|||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/memorystore": {
|
||||
"version": "1.6.7",
|
||||
"resolved": "https://registry.npmjs.org/memorystore/-/memorystore-1.6.7.tgz",
|
||||
"integrity": "sha512-OZnmNY/NDrKohPQ+hxp0muBcBKrzKNtHr55DbqSx9hLsYVNnomSAMRAtI7R64t3gf3ID7tHQA7mG4oL3Hu9hdw==",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.0",
|
||||
"lru-cache": "^4.0.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/memorystore/node_modules/debug": {
|
||||
"version": "4.3.4",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
|
||||
"integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/memorystore/node_modules/ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
},
|
||||
"node_modules/merge-descriptors": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz",
|
||||
|
@ -580,6 +672,19 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/on-headers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
||||
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/packet-reader": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz",
|
||||
"integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ=="
|
||||
},
|
||||
"node_modules/parseurl": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
|
||||
|
@ -593,6 +698,124 @@
|
|||
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
|
||||
"integrity": "sha512-5DFkuoqlv1uYQKxy8omFBeJPQcdoE07Kv2sferDCrAq1ohOU+MSDswDIbnx3YAM60qIOnYa53wBhXW0EbMonrQ=="
|
||||
},
|
||||
"node_modules/pg": {
|
||||
"version": "8.11.3",
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.11.3.tgz",
|
||||
"integrity": "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==",
|
||||
"dependencies": {
|
||||
"buffer-writer": "2.0.0",
|
||||
"packet-reader": "1.0.0",
|
||||
"pg-connection-string": "^2.6.2",
|
||||
"pg-pool": "^3.6.1",
|
||||
"pg-protocol": "^1.6.0",
|
||||
"pg-types": "^2.1.0",
|
||||
"pgpass": "1.x"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"pg-cloudflare": "^1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"pg-native": ">=3.0.1"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"pg-native": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/pg-cloudflare": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz",
|
||||
"integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==",
|
||||
"optional": true
|
||||
},
|
||||
"node_modules/pg-connection-string": {
|
||||
"version": "2.6.2",
|
||||
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.2.tgz",
|
||||
"integrity": "sha512-ch6OwaeaPYcova4kKZ15sbJ2hKb/VP48ZD2gE7i1J+L4MspCtBMAx8nMgz7bksc7IojCIIWuEhHibSMFH8m8oA=="
|
||||
},
|
||||
"node_modules/pg-int8": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
||||
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-pool": {
|
||||
"version": "3.6.1",
|
||||
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.1.tgz",
|
||||
"integrity": "sha512-jizsIzhkIitxCGfPRzJn1ZdcosIt3pz9Sh3V01fm1vZnbnCMgmGl5wvGGdNN2EL9Rmb0EcFoCkixH4Pu+sP9Og==",
|
||||
"peerDependencies": {
|
||||
"pg": ">=8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pg-protocol": {
|
||||
"version": "1.6.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.0.tgz",
|
||||
"integrity": "sha512-M+PDm637OY5WM307051+bsDia5Xej6d9IR4GwJse1qA1DIhiKlksvrneZOYQq42OM+spubpcNYEo2FcKQrDk+Q=="
|
||||
},
|
||||
"node_modules/pg-types": {
|
||||
"version": "2.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
|
||||
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
|
||||
"dependencies": {
|
||||
"pg-int8": "1.0.1",
|
||||
"postgres-array": "~2.0.0",
|
||||
"postgres-bytea": "~1.0.0",
|
||||
"postgres-date": "~1.0.4",
|
||||
"postgres-interval": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/pgpass": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
|
||||
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
|
||||
"dependencies": {
|
||||
"split2": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-array": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
||||
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-bytea": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
|
||||
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-date": {
|
||||
"version": "1.0.7",
|
||||
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
|
||||
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/postgres-interval": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
|
||||
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
|
||||
"dependencies": {
|
||||
"xtend": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/proxy-addr": {
|
||||
"version": "2.0.7",
|
||||
"resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
|
||||
|
@ -605,6 +828,11 @@
|
|||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/pseudomap": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
|
||||
"integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ=="
|
||||
},
|
||||
"node_modules/qs": {
|
||||
"version": "6.11.0",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz",
|
||||
|
@ -619,6 +847,14 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/random-bytes": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
|
||||
"integrity": "sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==",
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/range-parser": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
|
||||
|
@ -739,6 +975,14 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/split2": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
|
||||
"engines": {
|
||||
"node": ">= 10.x"
|
||||
}
|
||||
},
|
||||
"node_modules/statuses": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
|
||||
|
@ -778,6 +1022,17 @@
|
|||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/uid-safe": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
|
||||
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
|
||||
"dependencies": {
|
||||
"random-bytes": "~1.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/unpipe": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
|
||||
|
@ -801,6 +1056,19 @@
|
|||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
"integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
|
||||
"engines": {
|
||||
"node": ">=0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/yallist": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
|
||||
"integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A=="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
node_modules/
|
||||
npm-debug.log
|
||||
debug.log
|
||||
doco/
|
||||
tests/bench.js
|
||||
*.png
|
|
@ -0,0 +1,18 @@
|
|||
language: node_js
|
||||
|
||||
node_js:
|
||||
- 0.10
|
||||
- 0.12
|
||||
- 4
|
||||
- 6
|
||||
|
||||
before_script: npm -g install testjs
|
||||
|
||||
env:
|
||||
- CXX=g++-4.8
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- g++-4.8
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"vsicons.presets.angular": false
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
bcrypt.js
|
||||
---------
|
||||
Copyright (c) 2012 Nevins Bartolomeo <nevins.bartolomeo@gmail.com>
|
||||
Copyright (c) 2012 Shane Girish <shaneGirish@gmail.com>
|
||||
Copyright (c) 2014 Daniel Wirtz <dcode@dcode.io>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
isaac.js
|
||||
--------
|
||||
Copyright (c) 2012 Yves-Marie K. Rinquin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,251 @@
|
|||
bcrypt.js
|
||||
=========
|
||||
Optimized bcrypt in JavaScript with zero dependencies. Compatible to the C++ [bcrypt](https://npmjs.org/package/bcrypt)
|
||||
binding on node.js and also working in the browser.
|
||||
|
||||
<a href="https://travis-ci.org/dcodeIO/bcrypt.js"><img alt="build static" src="https://travis-ci.org/dcodeIO/bcrypt.js.svg?branch=master" /></a> <a href="https://npmjs.org/package/bcryptjs"><img src="https://img.shields.io/npm/v/bcryptjs.svg" alt=""></a> <a href="https://npmjs.org/package/bcryptjs"><img src="https://img.shields.io/npm/dm/bcryptjs.svg" alt=""></a> <a href="https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=dcode%40dcode.io&item_name=Open%20Source%20Software%20Donation&item_number=dcodeIO%2Fbcrypt.js"><img alt="donate ❤" src="https://img.shields.io/badge/donate-❤-ff2244.svg"></a>
|
||||
|
||||
|
||||
Security considerations
|
||||
-----------------------
|
||||
Besides incorporating a salt to protect against rainbow table attacks, bcrypt is an adaptive function: over time, the
|
||||
iteration count can be increased to make it slower, so it remains resistant to brute-force search attacks even with
|
||||
increasing computation power. ([see](http://en.wikipedia.org/wiki/Bcrypt))
|
||||
|
||||
While bcrypt.js is compatible to the C++ bcrypt binding, it is written in pure JavaScript and thus slower ([about 30%](https://github.com/dcodeIO/bcrypt.js/wiki/Benchmark)), effectively reducing the number of iterations that can be
|
||||
processed in an equal time span.
|
||||
|
||||
The maximum input length is 72 bytes (note that UTF8 encoded characters use up to 4 bytes) and the length of generated
|
||||
hashes is 60 characters.
|
||||
|
||||
Usage
|
||||
-----
|
||||
The library is compatible with CommonJS and AMD loaders and is exposed globally as `dcodeIO.bcrypt` if neither is
|
||||
available.
|
||||
|
||||
### node.js
|
||||
|
||||
On node.js, the inbuilt [crypto module](http://nodejs.org/api/crypto.html)'s randomBytes interface is used to obtain
|
||||
secure random numbers.
|
||||
|
||||
`npm install bcryptjs`
|
||||
|
||||
```js
|
||||
var bcrypt = require('bcryptjs');
|
||||
...
|
||||
```
|
||||
|
||||
### Browser
|
||||
|
||||
In the browser, bcrypt.js relies on [Web Crypto API](http://www.w3.org/TR/WebCryptoAPI)'s getRandomValues
|
||||
interface to obtain secure random numbers. If no cryptographically secure source of randomness is available, you may
|
||||
specify one through [bcrypt.setRandomFallback](https://github.com/dcodeIO/bcrypt.js#setrandomfallbackrandom).
|
||||
|
||||
```js
|
||||
var bcrypt = dcodeIO.bcrypt;
|
||||
...
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```js
|
||||
require.config({
|
||||
paths: { "bcrypt": "/path/to/bcrypt.js" }
|
||||
});
|
||||
require(["bcrypt"], function(bcrypt) {
|
||||
...
|
||||
});
|
||||
```
|
||||
|
||||
Usage - Sync
|
||||
------------
|
||||
To hash a password:
|
||||
|
||||
```javascript
|
||||
var bcrypt = require('bcryptjs');
|
||||
var salt = bcrypt.genSaltSync(10);
|
||||
var hash = bcrypt.hashSync("B4c0/\/", salt);
|
||||
// Store hash in your password DB.
|
||||
```
|
||||
|
||||
To check a password:
|
||||
|
||||
```javascript
|
||||
// Load hash from your password DB.
|
||||
bcrypt.compareSync("B4c0/\/", hash); // true
|
||||
bcrypt.compareSync("not_bacon", hash); // false
|
||||
```
|
||||
|
||||
Auto-gen a salt and hash:
|
||||
|
||||
```javascript
|
||||
var hash = bcrypt.hashSync('bacon', 8);
|
||||
```
|
||||
|
||||
Usage - Async
|
||||
-------------
|
||||
To hash a password:
|
||||
|
||||
```javascript
|
||||
var bcrypt = require('bcryptjs');
|
||||
bcrypt.genSalt(10, function(err, salt) {
|
||||
bcrypt.hash("B4c0/\/", salt, function(err, hash) {
|
||||
// Store hash in your password DB.
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
To check a password:
|
||||
|
||||
```javascript
|
||||
// Load hash from your password DB.
|
||||
bcrypt.compare("B4c0/\/", hash, function(err, res) {
|
||||
// res === true
|
||||
});
|
||||
bcrypt.compare("not_bacon", hash, function(err, res) {
|
||||
// res === false
|
||||
});
|
||||
|
||||
// As of bcryptjs 2.4.0, compare returns a promise if callback is omitted:
|
||||
bcrypt.compare("B4c0/\/", hash).then((res) => {
|
||||
// res === true
|
||||
});
|
||||
```
|
||||
|
||||
Auto-gen a salt and hash:
|
||||
|
||||
```javascript
|
||||
bcrypt.hash('bacon', 8, function(err, hash) {
|
||||
});
|
||||
```
|
||||
|
||||
**Note:** Under the hood, asynchronisation splits a crypto operation into small chunks. After the completion of a chunk, the execution of the next chunk is placed on the back of [JS event loop queue](https://developer.mozilla.org/en/docs/Web/JavaScript/EventLoop), thus efficiently sharing the computational resources with the other operations in the queue.
|
||||
|
||||
API
|
||||
---
|
||||
### setRandomFallback(random)
|
||||
|
||||
Sets the pseudo random number generator to use as a fallback if neither node's `crypto` module nor the Web Crypto
|
||||
API is available. Please note: It is highly important that the PRNG used is cryptographically secure and that it is
|
||||
seeded properly!
|
||||
|
||||
| Parameter | Type | Description
|
||||
|-----------------|-----------------|---------------
|
||||
| random | *function(number):!Array.<number>* | Function taking the number of bytes to generate as its sole argument, returning the corresponding array of cryptographically secure random byte values.
|
||||
| **@see** | | http://nodejs.org/api/crypto.html
|
||||
| **@see** | | http://www.w3.org/TR/WebCryptoAPI/
|
||||
|
||||
**Hint:** You might use [isaac.js](https://github.com/rubycon/isaac.js) as a CSPRNG but you still have to make sure to
|
||||
seed it properly.
|
||||
|
||||
### genSaltSync(rounds=, seed_length=)
|
||||
|
||||
Synchronously generates a salt.
|
||||
|
||||
| Parameter | Type | Description
|
||||
|-----------------|-----------------|---------------
|
||||
| rounds | *number* | Number of rounds to use, defaults to 10 if omitted
|
||||
| seed_length | *number* | Not supported.
|
||||
| **@returns** | *string* | Resulting salt
|
||||
| **@throws** | *Error* | If a random fallback is required but not set
|
||||
|
||||
### genSalt(rounds=, seed_length=, callback)
|
||||
|
||||
Asynchronously generates a salt.
|
||||
|
||||
| Parameter | Type | Description
|
||||
|-----------------|-----------------|---------------
|
||||
| rounds | *number | function(Error, string=)* | Number of rounds to use, defaults to 10 if omitted
|
||||
| seed_length | *number | function(Error, string=)* | Not supported.
|
||||
| callback | *function(Error, string=)* | Callback receiving the error, if any, and the resulting salt
|
||||
| **@returns** | *Promise* | If `callback` has been omitted
|
||||
| **@throws** | *Error* | If `callback` is present but not a function
|
||||
|
||||
### hashSync(s, salt=)
|
||||
|
||||
Synchronously generates a hash for the given string.
|
||||
|
||||
| Parameter | Type | Description
|
||||
|-----------------|-----------------|---------------
|
||||
| s | *string* | String to hash
|
||||
| salt | *number | string* | Salt length to generate or salt to use, default to 10
|
||||
| **@returns** | *string* | Resulting hash
|
||||
|
||||
### hash(s, salt, callback, progressCallback=)
|
||||
|
||||
Asynchronously generates a hash for the given string.
|
||||
|
||||
| Parameter | Type | Description
|
||||
|-----------------|-----------------|---------------
|
||||
| s | *string* | String to hash
|
||||
| salt | *number | string* | Salt length to generate or salt to use
|
||||
| callback | *function(Error, string=)* | Callback receiving the error, if any, and the resulting hash
|
||||
| progressCallback | *function(number)* | Callback successively called with the percentage of rounds completed (0.0 - 1.0), maximally once per `MAX_EXECUTION_TIME = 100` ms.
|
||||
| **@returns** | *Promise* | If `callback` has been omitted
|
||||
| **@throws** | *Error* | If `callback` is present but not a function
|
||||
|
||||
### compareSync(s, hash)
|
||||
|
||||
Synchronously tests a string against a hash.
|
||||
|
||||
| Parameter | Type | Description
|
||||
|-----------------|-----------------|---------------
|
||||
| s | *string* | String to compare
|
||||
| hash | *string* | Hash to test against
|
||||
| **@returns** | *boolean* | true if matching, otherwise false
|
||||
| **@throws** | *Error* | If an argument is illegal
|
||||
|
||||
### compare(s, hash, callback, progressCallback=)
|
||||
|
||||
Asynchronously compares the given data against the given hash.
|
||||
|
||||
| Parameter | Type | Description
|
||||
|-----------------|-----------------|---------------
|
||||
| s | *string* | Data to compare
|
||||
| hash | *string* | Data to be compared to
|
||||
| callback | *function(Error, boolean)* | Callback receiving the error, if any, otherwise the result
|
||||
| progressCallback | *function(number)* | Callback successively called with the percentage of rounds completed (0.0 - 1.0), maximally once per `MAX_EXECUTION_TIME = 100` ms.
|
||||
| **@returns** | *Promise* | If `callback` has been omitted
|
||||
| **@throws** | *Error* | If `callback` is present but not a function
|
||||
|
||||
### getRounds(hash)
|
||||
|
||||
Gets the number of rounds used to encrypt the specified hash.
|
||||
|
||||
| Parameter | Type | Description
|
||||
|-----------------|-----------------|---------------
|
||||
| hash | *string* | Hash to extract the used number of rounds from
|
||||
| **@returns** | *number* | Number of rounds used
|
||||
| **@throws** | *Error* | If `hash` is not a string
|
||||
|
||||
### getSalt(hash)
|
||||
|
||||
Gets the salt portion from a hash. Does not validate the hash.
|
||||
|
||||
| Parameter | Type | Description
|
||||
|-----------------|-----------------|---------------
|
||||
| hash | *string* | Hash to extract the salt from
|
||||
| **@returns** | *string* | Extracted salt part
|
||||
| **@throws** | *Error* | If `hash` is not a string or otherwise invalid
|
||||
|
||||
|
||||
Command line
|
||||
------------
|
||||
`Usage: bcrypt <input> [salt]`
|
||||
|
||||
If the input has spaces inside, simply surround it with quotes.
|
||||
|
||||
Downloads
|
||||
---------
|
||||
* [Distributions](https://github.com/dcodeIO/bcrypt.js/tree/master/dist)
|
||||
* [ZIP-Archive](https://github.com/dcodeIO/bcrypt.js/archive/master.zip)
|
||||
* [Tarball](https://github.com/dcodeIO/bcrypt.js/tarball/master)
|
||||
|
||||
Credits
|
||||
-------
|
||||
Based on work started by Shane Girish at [bcrypt-nodejs](https://github.com/shaneGirish/bcrypt-nodejs) (MIT-licensed),
|
||||
which is itself based on [javascript-bcrypt](http://code.google.com/p/javascript-bcrypt/) (New BSD-licensed).
|
||||
|
||||
License
|
||||
-------
|
||||
New-BSD / MIT ([see](https://github.com/dcodeIO/bcrypt.js/blob/master/LICENSE))
|
|
@ -0,0 +1,25 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
var path = require("path"),
|
||||
bcrypt = require(path.join(__dirname, '..', 'index.js')),
|
||||
pkg = require(path.join(__dirname, '..', 'package.json'));
|
||||
|
||||
if (process.argv.length < 3) {
|
||||
process.stderr.write([ // No dependencies, so we do it from hand.
|
||||
"",
|
||||
" |_ _ _ _ |_",
|
||||
" |_)(_| \\/|_)|_ v"+pkg['version']+" (c) "+pkg['author'],
|
||||
" / | "
|
||||
].join('\n')+'\n\n'+" Usage: "+path.basename(process.argv[1])+" <input> [rounds|salt]\n");
|
||||
process.exit(1);
|
||||
} else {
|
||||
var salt;
|
||||
if (process.argv.length > 3) {
|
||||
salt = process.argv[3];
|
||||
var rounds = parseInt(salt, 10);
|
||||
if (rounds == salt)
|
||||
salt = bcrypt.genSaltSync(rounds);
|
||||
} else
|
||||
salt = bcrypt.genSaltSync();
|
||||
process.stdout.write(bcrypt.hashSync(process.argv[2], salt)+"\n");
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"name": "bcryptjs",
|
||||
"description": "Optimized bcrypt in plain JavaScript with zero dependencies.",
|
||||
"version": "2.4.3",
|
||||
"main": "dist/bcrypt.min.js",
|
||||
"license": "New-BSD",
|
||||
"homepage": "http://dcode.io/",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/dcodeIO/bcrypt.js.git"
|
||||
},
|
||||
"keywords": ["bcrypt", "password", "auth", "authentication", "encryption", "crypt", "crypto"],
|
||||
"dependencies": {},
|
||||
"devDependencies": {},
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
Distributions
|
||||
=============
|
||||
bcrypt.js is available as the following distributions:
|
||||
|
||||
* **[bcrypt.js](https://github.com/dcodeIO/bcrypt.js/blob/master/dist/bcrypt.js)**
|
||||
contains the commented source code.
|
||||
|
||||
* **[bcrypt.min.js](https://github.com/dcodeIO/bcrypt.js/blob/master/dist/bcrypt.min.js)**
|
||||
has been compiled with Closure Compiler using advanced optimizations.
|
||||
|
||||
* **[bcrypt.min.map](https://github.com/dcodeIO/bcrypt.js/blob/master/dist/bcrypt.min.map)**
|
||||
contains the source map generated by Closure Compiler.
|
||||
|
||||
* **[bcrypt.min.js.gz](https://github.com/dcodeIO/bcrypt.js/blob/master/dist/bcrypt.min.js.gz)**
|
||||
has also been gzipped using `-9`.
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
bcrypt.js (c) 2013 Daniel Wirtz <dcode@dcode.io>
|
||||
Released under the Apache License, Version 2.0
|
||||
see: https://github.com/dcodeIO/bcrypt.js for details
|
||||
*/
|
||||
(function(u,r){"function"===typeof define&&define.amd?define([],r):"function"===typeof require&&"object"===typeof module&&module&&module.exports?module.exports=r():(u.dcodeIO=u.dcodeIO||{}).bcrypt=r()})(this,function(){function u(e){if("undefined"!==typeof module&&module&&module.exports)try{return require("crypto").randomBytes(e)}catch(d){}try{var c;(self.crypto||self.msCrypto).getRandomValues(c=new Uint32Array(e));return Array.prototype.slice.call(c)}catch(b){}if(!w)throw Error("Neither WebCryptoAPI nor a crypto module is available. Use bcrypt.setRandomFallback to set an alternative");
|
||||
return w(e)}function r(e,d){for(var c=0,b=0,a=0,f=e.length;a<f;++a)e.charCodeAt(a)===d.charCodeAt(a)?++c:++b;return 0>c?!1:0===b}function H(e){var d=[],c=0;I.encodeUTF16toUTF8(function(){return c>=e.length?null:e.charCodeAt(c++)},function(b){d.push(b)});return d}function x(e,d){var c=0,b=[],a,f;if(0>=d||d>e.length)throw Error("Illegal len: "+d);for(;c<d;){a=e[c++]&255;b.push(s[a>>2&63]);a=(a&3)<<4;if(c>=d){b.push(s[a&63]);break}f=e[c++]&255;a|=f>>4&15;b.push(s[a&63]);a=(f&15)<<2;if(c>=d){b.push(s[a&
|
||||
63]);break}f=e[c++]&255;a|=f>>6&3;b.push(s[a&63]);b.push(s[f&63])}return b.join("")}function B(e,d){var c=0,b=e.length,a=0,f=[],g,m,h;if(0>=d)throw Error("Illegal len: "+d);for(;c<b-1&&a<d;){h=e.charCodeAt(c++);g=h<q.length?q[h]:-1;h=e.charCodeAt(c++);m=h<q.length?q[h]:-1;if(-1==g||-1==m)break;h=g<<2>>>0;h|=(m&48)>>4;f.push(z(h));if(++a>=d||c>=b)break;h=e.charCodeAt(c++);g=h<q.length?q[h]:-1;if(-1==g)break;h=(m&15)<<4>>>0;h|=(g&60)>>2;f.push(z(h));if(++a>=d||c>=b)break;h=e.charCodeAt(c++);m=h<q.length?
|
||||
q[h]:-1;h=(g&3)<<6>>>0;h|=m;f.push(z(h));++a}b=[];for(c=0;c<a;c++)b.push(f[c].charCodeAt(0));return b}function v(e,d,c,b){var a,f=e[d],g=e[d+1],f=f^c[0];a=b[f>>>24];a+=b[256|f>>16&255];a^=b[512|f>>8&255];a+=b[768|f&255];g=g^a^c[1];a=b[g>>>24];a+=b[256|g>>16&255];a^=b[512|g>>8&255];a+=b[768|g&255];f=f^a^c[2];a=b[f>>>24];a+=b[256|f>>16&255];a^=b[512|f>>8&255];a+=b[768|f&255];g=g^a^c[3];a=b[g>>>24];a+=b[256|g>>16&255];a^=b[512|g>>8&255];a+=b[768|g&255];f=f^a^c[4];a=b[f>>>24];a+=b[256|f>>16&255];a^=b[512|
|
||||
f>>8&255];a+=b[768|f&255];g=g^a^c[5];a=b[g>>>24];a+=b[256|g>>16&255];a^=b[512|g>>8&255];a+=b[768|g&255];f=f^a^c[6];a=b[f>>>24];a+=b[256|f>>16&255];a^=b[512|f>>8&255];a+=b[768|f&255];g=g^a^c[7];a=b[g>>>24];a+=b[256|g>>16&255];a^=b[512|g>>8&255];a+=b[768|g&255];f=f^a^c[8];a=b[f>>>24];a+=b[256|f>>16&255];a^=b[512|f>>8&255];a+=b[768|f&255];g=g^a^c[9];a=b[g>>>24];a+=b[256|g>>16&255];a^=b[512|g>>8&255];a+=b[768|g&255];f=f^a^c[10];a=b[f>>>24];a+=b[256|f>>16&255];a^=b[512|f>>8&255];a+=b[768|f&255];g=g^a^
|
||||
c[11];a=b[g>>>24];a+=b[256|g>>16&255];a^=b[512|g>>8&255];a+=b[768|g&255];f=f^a^c[12];a=b[f>>>24];a+=b[256|f>>16&255];a^=b[512|f>>8&255];a+=b[768|f&255];g=g^a^c[13];a=b[g>>>24];a+=b[256|g>>16&255];a^=b[512|g>>8&255];a+=b[768|g&255];f=f^a^c[14];a=b[f>>>24];a+=b[256|f>>16&255];a^=b[512|f>>8&255];a+=b[768|f&255];g=g^a^c[15];a=b[g>>>24];a+=b[256|g>>16&255];a^=b[512|g>>8&255];a+=b[768|g&255];f=f^a^c[16];e[d]=g^c[17];e[d+1]=f;return e}function t(e,d){for(var c=0,b=0;4>c;++c)b=b<<8|e[d]&255,d=(d+1)%e.length;
|
||||
return{key:b,offp:d}}function C(e,d,c){for(var b=0,a=[0,0],f=d.length,g=c.length,m,h=0;h<f;h++)m=t(e,b),b=m.offp,d[h]^=m.key;for(h=0;h<f;h+=2)a=v(a,0,d,c),d[h]=a[0],d[h+1]=a[1];for(h=0;h<g;h+=2)a=v(a,0,d,c),c[h]=a[0],c[h+1]=a[1]}function J(e,d,c,b){for(var a=0,f=[0,0],g=c.length,m=b.length,h,l=0;l<g;l++)h=t(d,a),a=h.offp,c[l]^=h.key;for(l=a=0;l<g;l+=2)h=t(e,a),a=h.offp,f[0]^=h.key,h=t(e,a),a=h.offp,f[1]^=h.key,f=v(f,0,c,b),c[l]=f[0],c[l+1]=f[1];for(l=0;l<m;l+=2)h=t(e,a),a=h.offp,f[0]^=h.key,h=t(e,
|
||||
a),a=h.offp,f[1]^=h.key,f=v(f,0,c,b),b[l]=f[0],b[l+1]=f[1]}function D(e,d,c,b,a){function f(){a&&a(n/c);if(n<c)for(var h=Date.now();n<c&&!(n+=1,C(e,l,k),C(d,l,k),100<Date.now()-h););else{for(n=0;64>n;n++)for(y=0;y<m>>1;y++)v(g,y<<1,l,k);h=[];for(n=0;n<m;n++)h.push((g[n]>>24&255)>>>0),h.push((g[n]>>16&255)>>>0),h.push((g[n]>>8&255)>>>0),h.push((g[n]&255)>>>0);if(b){b(null,h);return}return h}b&&p(f)}var g=E.slice(),m=g.length,h;if(4>c||31<c){h=Error("Illegal number of rounds (4-31): "+c);if(b){p(b.bind(this,
|
||||
h));return}throw h;}if(16!==d.length){h=Error("Illegal salt length: "+d.length+" != 16");if(b){p(b.bind(this,h));return}throw h;}c=1<<c>>>0;var l,k,n=0,y;Int32Array?(l=new Int32Array(F),k=new Int32Array(G)):(l=F.slice(),k=G.slice());J(d,e,l,k);if("undefined"!==typeof b)f();else for(;;)if("undefined"!==typeof(h=f()))return h||[]}function A(e,d,c,b){function a(a){var b=[];b.push("$2");"a"<=f&&b.push(f);b.push("$");10>l&&b.push("0");b.push(l.toString());b.push("$");b.push(x(k,k.length));b.push(x(a,4*
|
||||
E.length-1));return b.join("")}if("string"!==typeof e||"string"!==typeof d){b=Error("Invalid string / salt: Not a string");if(c){p(c.bind(this,b));return}throw b;}var f,g;if("$"!==d.charAt(0)||"2"!==d.charAt(1)){b=Error("Invalid salt version: "+d.substring(0,2));if(c){p(c.bind(this,b));return}throw b;}if("$"===d.charAt(2))f=String.fromCharCode(0),g=3;else{f=d.charAt(2);if("a"!==f&&"b"!==f&&"y"!==f||"$"!==d.charAt(3)){b=Error("Invalid salt revision: "+d.substring(2,4));if(c){p(c.bind(this,b));return}throw b;
|
||||
}g=4}if("$"<d.charAt(g+2)){b=Error("Missing salt rounds");if(c){p(c.bind(this,b));return}throw b;}var m=10*parseInt(d.substring(g,g+1),10),h=parseInt(d.substring(g+1,g+2),10),l=m+h;d=d.substring(g+3,g+25);e=H(e+("a"<=f?"\x00":""));var k=B(d,16);if("undefined"==typeof c)return a(D(e,k,l));D(e,k,l,function(b,d){b?c(b,null):c(null,a(d))},b)}var k={},w=null;try{u(1)}catch(K){}w=null;k.setRandomFallback=function(e){w=e};k.genSaltSync=function(e,d){e=e||10;if("number"!==typeof e)throw Error("Illegal arguments: "+
|
||||
typeof e+", "+typeof d);4>e?e=4:31<e&&(e=31);var c=[];c.push("$2a$");10>e&&c.push("0");c.push(e.toString());c.push("$");c.push(x(u(16),16));return c.join("")};k.genSalt=function(e,d,c){function b(a){p(function(){try{a(null,k.genSaltSync(e))}catch(b){a(b)}})}"function"===typeof d&&(c=d,d=void 0);"function"===typeof e&&(c=e,e=void 0);if("undefined"===typeof e)e=10;else if("number"!==typeof e)throw Error("illegal arguments: "+typeof e);if(c){if("function"!==typeof c)throw Error("Illegal callback: "+
|
||||
typeof c);b(c)}else return new Promise(function(a,c){b(function(b,d){b?c(b):a(d)})})};k.hashSync=function(e,d){"undefined"===typeof d&&(d=10);"number"===typeof d&&(d=k.genSaltSync(d));if("string"!==typeof e||"string"!==typeof d)throw Error("Illegal arguments: "+typeof e+", "+typeof d);return A(e,d)};k.hash=function(e,d,c,b){function a(a){"string"===typeof e&&"number"===typeof d?k.genSalt(d,function(c,d){A(e,d,a,b)}):"string"===typeof e&&"string"===typeof d?A(e,d,a,b):p(a.bind(this,Error("Illegal arguments: "+
|
||||
typeof e+", "+typeof d)))}if(c){if("function"!==typeof c)throw Error("Illegal callback: "+typeof c);a(c)}else return new Promise(function(b,c){a(function(a,d){a?c(a):b(d)})})};k.compareSync=function(e,d){if("string"!==typeof e||"string"!==typeof d)throw Error("Illegal arguments: "+typeof e+", "+typeof d);return 60!==d.length?!1:r(k.hashSync(e,d.substr(0,d.length-31)),d)};k.compare=function(e,d,c,b){function a(a){"string"!==typeof e||"string"!==typeof d?p(a.bind(this,Error("Illegal arguments: "+typeof e+
|
||||
", "+typeof d))):60!==d.length?p(a.bind(this,null,!1)):k.hash(e,d.substr(0,29),function(b,c){b?a(b):a(null,r(c,d))},b)}if(c){if("function"!==typeof c)throw Error("Illegal callback: "+typeof c);a(c)}else return new Promise(function(b,c){a(function(a,d){a?c(a):b(d)})})};k.getRounds=function(e){if("string"!==typeof e)throw Error("Illegal arguments: "+typeof e);return parseInt(e.split("$")[2],10)};k.getSalt=function(e){if("string"!==typeof e)throw Error("Illegal arguments: "+typeof e);if(60!==e.length)throw Error("Illegal hash length: "+
|
||||
e.length+" != 60");return e.substring(0,29)};var p="undefined"!==typeof process&&process&&"function"===typeof process.nextTick?"function"===typeof setImmediate?setImmediate:process.nextTick:setTimeout,s="./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".split(""),q=[-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,0,1,54,55,56,57,58,59,60,61,62,63,-1,-1,-1,-1,-1,-1,-1,2,3,4,5,6,7,8,9,10,11,12,
|
||||
13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,-1,-1,-1,-1,-1,-1,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,-1,-1,-1,-1,-1],z=String.fromCharCode,I=function(){var e={MAX_CODEPOINT:1114111,encodeUTF8:function(d,c){var b=null;"number"===typeof d&&(b=d,d=function(){return null});for(;null!==b||null!==(b=d());)128>b?c(b&127):(2048>b?c(b>>6&31|192):(65536>b?c(b>>12&15|224):(c(b>>18&7|240),c(b>>12&63|128)),c(b>>6&63|128)),c(b&63|128)),b=null},decodeUTF8:function(d,c){for(var b,
|
||||
a,f,e,k=function(a){a=a.slice(0,a.indexOf(null));var b=Error(a.toString());b.name="TruncatedError";b.bytes=a;throw b;};null!==(b=d());)if(0===(b&128))c(b);else if(192===(b&224))null===(a=d())&&k([b,a]),c((b&31)<<6|a&63);else if(224===(b&240))null!==(a=d())&&null!==(f=d())||k([b,a,f]),c((b&15)<<12|(a&63)<<6|f&63);else if(240===(b&248))null!==(a=d())&&null!==(f=d())&&null!==(e=d())||k([b,a,f,e]),c((b&7)<<18|(a&63)<<12|(f&63)<<6|e&63);else throw RangeError("Illegal starting byte: "+b);},UTF16toUTF8:function(d,
|
||||
c){for(var b,a=null;null!==(b=null!==a?a:d());)55296<=b&&57343>=b&&null!==(a=d())&&56320<=a&&57343>=a?(c(1024*(b-55296)+a-56320+65536),a=null):c(b);null!==a&&c(a)},UTF8toUTF16:function(d,c){var b=null;"number"===typeof d&&(b=d,d=function(){return null});for(;null!==b||null!==(b=d());)65535>=b?c(b):(b-=65536,c((b>>10)+55296),c(b%1024+56320)),b=null},encodeUTF16toUTF8:function(d,c){e.UTF16toUTF8(d,function(b){e.encodeUTF8(b,c)})},decodeUTF8toUTF16:function(d,c){e.decodeUTF8(d,function(b){e.UTF8toUTF16(b,
|
||||
c)})},calculateCodePoint:function(d){return 128>d?1:2048>d?2:65536>d?3:4},calculateUTF8:function(d){for(var c,b=0;null!==(c=d());)b+=e.calculateCodePoint(c);return b},calculateUTF16asUTF8:function(d){var c=0,b=0;e.UTF16toUTF8(d,function(a){++c;b+=e.calculateCodePoint(a)});return[c,b]}};return e}();Date.now=Date.now||function(){return+new Date};var F=[608135816,2242054355,320440878,57701188,2752067618,698298832,137296536,3964562569,1160258022,953160567,3193202383,887688300,3232508343,3380367581,1065670069,
|
||||
3041331479,2450970073,2306472731],G=[3509652390,2564797868,805139163,3491422135,3101798381,1780907670,3128725573,4046225305,614570311,3012652279,134345442,2240740374,1667834072,1901547113,2757295779,4103290238,227898511,1921955416,1904987480,2182433518,2069144605,3260701109,2620446009,720527379,3318853667,677414384,3393288472,3101374703,2390351024,1614419982,1822297739,2954791486,3608508353,3174124327,2024746970,1432378464,3864339955,2857741204,1464375394,1676153920,1439316330,715854006,3033291828,
|
||||
289532110,2706671279,2087905683,3018724369,1668267050,732546397,1947742710,3462151702,2609353502,2950085171,1814351708,2050118529,680887927,999245976,1800124847,3300911131,1713906067,1641548236,4213287313,1216130144,1575780402,4018429277,3917837745,3693486850,3949271944,596196993,3549867205,258830323,2213823033,772490370,2760122372,1774776394,2652871518,566650946,4142492826,1728879713,2882767088,1783734482,3629395816,2517608232,2874225571,1861159788,326777828,3124490320,2130389656,2716951837,967770486,
|
||||
1724537150,2185432712,2364442137,1164943284,2105845187,998989502,3765401048,2244026483,1075463327,1455516326,1322494562,910128902,469688178,1117454909,936433444,3490320968,3675253459,1240580251,122909385,2157517691,634681816,4142456567,3825094682,3061402683,2540495037,79693498,3249098678,1084186820,1583128258,426386531,1761308591,1047286709,322548459,995290223,1845252383,2603652396,3431023940,2942221577,3202600964,3727903485,1712269319,422464435,3234572375,1170764815,3523960633,3117677531,1434042557,
|
||||
442511882,3600875718,1076654713,1738483198,4213154764,2393238008,3677496056,1014306527,4251020053,793779912,2902807211,842905082,4246964064,1395751752,1040244610,2656851899,3396308128,445077038,3742853595,3577915638,679411651,2892444358,2354009459,1767581616,3150600392,3791627101,3102740896,284835224,4246832056,1258075500,768725851,2589189241,3069724005,3532540348,1274779536,3789419226,2764799539,1660621633,3471099624,4011903706,913787905,3497959166,737222580,2514213453,2928710040,3937242737,1804850592,
|
||||
3499020752,2949064160,2386320175,2390070455,2415321851,4061277028,2290661394,2416832540,1336762016,1754252060,3520065937,3014181293,791618072,3188594551,3933548030,2332172193,3852520463,3043980520,413987798,3465142937,3030929376,4245938359,2093235073,3534596313,375366246,2157278981,2479649556,555357303,3870105701,2008414854,3344188149,4221384143,3956125452,2067696032,3594591187,2921233993,2428461,544322398,577241275,1471733935,610547355,4027169054,1432588573,1507829418,2025931657,3646575487,545086370,
|
||||
48609733,2200306550,1653985193,298326376,1316178497,3007786442,2064951626,458293330,2589141269,3591329599,3164325604,727753846,2179363840,146436021,1461446943,4069977195,705550613,3059967265,3887724982,4281599278,3313849956,1404054877,2845806497,146425753,1854211946,1266315497,3048417604,3681880366,3289982499,290971E4,1235738493,2632868024,2414719590,3970600049,1771706367,1449415276,3266420449,422970021,1963543593,2690192192,3826793022,1062508698,1531092325,1804592342,2583117782,2714934279,4024971509,
|
||||
1294809318,4028980673,1289560198,2221992742,1669523910,35572830,157838143,1052438473,1016535060,1802137761,1753167236,1386275462,3080475397,2857371447,1040679964,2145300060,2390574316,1461121720,2956646967,4031777805,4028374788,33600511,2920084762,1018524850,629373528,3691585981,3515945977,2091462646,2486323059,586499841,988145025,935516892,3367335476,2599673255,2839830854,265290510,3972581182,2759138881,3795373465,1005194799,847297441,406762289,1314163512,1332590856,1866599683,4127851711,750260880,
|
||||
613907577,1450815602,3165620655,3734664991,3650291728,3012275730,3704569646,1427272223,778793252,1343938022,2676280711,2052605720,1946737175,3164576444,3914038668,3967478842,3682934266,1661551462,3294938066,4011595847,840292616,3712170807,616741398,312560963,711312465,1351876610,322626781,1910503582,271666773,2175563734,1594956187,70604529,3617834859,1007753275,1495573769,4069517037,2549218298,2663038764,504708206,2263041392,3941167025,2249088522,1514023603,1998579484,1312622330,694541497,2582060303,
|
||||
2151582166,1382467621,776784248,2618340202,3323268794,2497899128,2784771155,503983604,4076293799,907881277,423175695,432175456,1378068232,4145222326,3954048622,3938656102,3820766613,2793130115,2977904593,26017576,3274890735,3194772133,1700274565,1756076034,4006520079,3677328699,720338349,1533947780,354530856,688349552,3973924725,1637815568,332179504,3949051286,53804574,2852348879,3044236432,1282449977,3583942155,3416972820,4006381244,1617046695,2628476075,3002303598,1686838959,431878346,2686675385,
|
||||
1700445008,1080580658,1009431731,832498133,3223435511,2605976345,2271191193,2516031870,1648197032,4164389018,2548247927,300782431,375919233,238389289,3353747414,2531188641,2019080857,1475708069,455242339,2609103871,448939670,3451063019,1395535956,2413381860,1841049896,1491858159,885456874,4264095073,4001119347,1565136089,3898914787,1108368660,540939232,1173283510,2745871338,3681308437,4207628240,3343053890,4016749493,1699691293,1103962373,3625875870,2256883143,3830138730,1031889488,3479347698,1535977030,
|
||||
4236805024,3251091107,2132092099,1774941330,1199868427,1452454533,157007616,2904115357,342012276,595725824,1480756522,206960106,497939518,591360097,863170706,2375253569,3596610801,1814182875,2094937945,3421402208,1082520231,3463918190,2785509508,435703966,3908032597,1641649973,2842273706,3305899714,1510255612,2148256476,2655287854,3276092548,4258621189,236887753,3681803219,274041037,1734335097,3815195456,3317970021,1899903192,1026095262,4050517792,356393447,2410691914,3873677099,3682840055,3913112168,
|
||||
2491498743,4132185628,2489919796,1091903735,1979897079,3170134830,3567386728,3557303409,857797738,1136121015,1342202287,507115054,2535736646,337727348,3213592640,1301675037,2528481711,1895095763,1721773893,3216771564,62756741,2142006736,835421444,2531993523,1442658625,3659876326,2882144922,676362277,1392781812,170690266,3921047035,1759253602,3611846912,1745797284,664899054,1329594018,3901205900,3045908486,2062866102,2865634940,3543621612,3464012697,1080764994,553557557,3656615353,3996768171,991055499,
|
||||
499776247,1265440854,648242737,3940784050,980351604,3713745714,1749149687,3396870395,4211799374,3640570775,1161844396,3125318951,1431517754,545492359,4268468663,3499529547,1437099964,2702547544,3433638243,2581715763,2787789398,1060185593,1593081372,2418618748,4260947970,69676912,2159744348,86519011,2512459080,3838209314,1220612927,3339683548,133810670,1090789135,1078426020,1569222167,845107691,3583754449,4072456591,1091646820,628848692,1613405280,3757631651,526609435,236106946,48312990,2942717905,
|
||||
3402727701,1797494240,859738849,992217954,4005476642,2243076622,3870952857,3732016268,765654824,3490871365,2511836413,1685915746,3888969200,1414112111,2273134842,3281911079,4080962846,172450625,2569994100,980381355,4109958455,2819808352,2716589560,2568741196,3681446669,3329971472,1835478071,660984891,3704678404,4045999559,3422617507,3040415634,1762651403,1719377915,3470491036,2693910283,3642056355,3138596744,1364962596,2073328063,1983633131,926494387,3423689081,2150032023,4096667949,1749200295,3328846651,
|
||||
309677260,2016342300,1779581495,3079819751,111262694,1274766160,443224088,298511866,1025883608,3806446537,1145181785,168956806,3641502830,3584813610,1689216846,3666258015,3200248200,1692713982,2646376535,4042768518,1618508792,1610833997,3523052358,4130873264,2001055236,3610705100,2202168115,4028541809,2961195399,1006657119,2006996926,3186142756,1430667929,3210227297,1314452623,4074634658,4101304120,2273951170,1399257539,3367210612,3027628629,1190975929,2062231137,2333990788,2221543033,2438960610,
|
||||
1181637006,548689776,2362791313,3372408396,3104550113,3145860560,296247880,1970579870,3078560182,3769228297,1714227617,3291629107,3898220290,166772364,1251581989,493813264,448347421,195405023,2709975567,677966185,3703036547,1463355134,2715995803,1338867538,1343315457,2802222074,2684532164,233230375,2599980071,2000651841,3277868038,1638401717,4028070440,3237316320,6314154,819756386,300326615,590932579,1405279636,3267499572,3150704214,2428286686,3959192993,3461946742,1862657033,1266418056,963775037,
|
||||
2089974820,2263052895,1917689273,448879540,3550394620,3981727096,150775221,3627908307,1303187396,508620638,2975983352,2726630617,1817252668,1876281319,1457606340,908771278,3720792119,3617206836,2455994898,1729034894,1080033504,976866871,3556439503,2881648439,1522871579,1555064734,1336096578,3548522304,2579274686,3574697629,3205460757,3593280638,3338716283,3079412587,564236357,2993598910,1781952180,1464380207,3163844217,3332601554,1699332808,1393555694,1183702653,3581086237,1288719814,691649499,2847557200,
|
||||
2895455976,3193889540,2717570544,1781354906,1676643554,2592534050,3230253752,1126444790,2770207658,2633158820,2210423226,2615765581,2414155088,3127139286,673620729,2805611233,1269405062,4015350505,3341807571,4149409754,1057255273,2012875353,2162469141,2276492801,2601117357,993977747,3918593370,2654263191,753973209,36408145,2530585658,25011837,3520020182,2088578344,530523599,2918365339,1524020338,1518925132,3760827505,3759777254,1202760957,3985898139,3906192525,674977740,4174734889,2031300136,2019492241,
|
||||
3983892565,4153806404,3822280332,352677332,2297720250,60907813,90501309,3286998549,1016092578,2535922412,2839152426,457141659,509813237,4120667899,652014361,1966332200,2975202805,55981186,2327461051,676427537,3255491064,2882294119,3433927263,1307055953,942726286,933058658,2468411793,3933900994,4215176142,1361170020,2001714738,2830558078,3274259782,1222529897,1679025792,2729314320,3714953764,1770335741,151462246,3013232138,1682292957,1483529935,471910574,1539241949,458788160,3436315007,1807016891,
|
||||
3718408830,978976581,1043663428,3165965781,1927990952,4200891579,2372276910,3208408903,3533431907,1412390302,2931980059,4132332400,1947078029,3881505623,4168226417,2941484381,1077988104,1320477388,886195818,18198404,3786409E3,2509781533,112762804,3463356488,1866414978,891333506,18488651,661792760,1628790961,3885187036,3141171499,876946877,2693282273,1372485963,791857591,2686433993,3759982718,3167212022,3472953795,2716379847,445679433,3561995674,3504004811,3574258232,54117162,3331405415,2381918588,
|
||||
3769707343,4154350007,1140177722,4074052095,668550556,3214352940,367459370,261225585,2610173221,4209349473,3468074219,3265815641,314222801,3066103646,3808782860,282218597,3406013506,3773591054,379116347,1285071038,846784868,2669647154,3771962079,3550491691,2305946142,453669953,1268987020,3317592352,3279303384,3744833421,2610507566,3859509063,266596637,3847019092,517658769,3462560207,3443424879,370717030,4247526661,2224018117,4143653529,4112773975,2788324899,2477274417,1456262402,2901442914,1517677493,
|
||||
1846949527,2295493580,3734397586,2176403920,1280348187,1908823572,3871786941,846861322,1172426758,3287448474,3383383037,1655181056,3139813346,901632758,1897031941,2986607138,3066810236,3447102507,1393639104,373351379,950779232,625454576,3124240540,4148612726,2007998917,544563296,2244738638,2330496472,2058025392,1291430526,424198748,50039436,29584100,3605783033,2429876329,2791104160,1057563949,3255363231,3075367218,3463963227,1469046755,985887462],E=[1332899944,1700884034,1701343084,1684370003,1668446532,
|
||||
1869963892];k.encodeBase64=x;k.decodeBase64=B;return k});
|
Binary file not shown.
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright 2012 The Closure Compiler Authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Definitions for bcrypt.js 2.
|
||||
* @externs
|
||||
* @author Daniel Wirtz <dcode@dcode.io>
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {Object.<string,*>}
|
||||
*/
|
||||
var bcrypt = {};
|
||||
|
||||
/**
|
||||
* @param {?function(number):!Array.<number>} random
|
||||
*/
|
||||
bcrypt.setRandomFallback = function(random) {};
|
||||
|
||||
/**
|
||||
* @param {number=} rounds
|
||||
* @param {number=} seed_length
|
||||
* @returns {string}
|
||||
*/
|
||||
bcrypt.genSaltSync = function(rounds, seed_length) {};
|
||||
|
||||
/**
|
||||
* @param {(number|function(Error, ?string))=} rounds
|
||||
* @param {(number|function(Error, ?string))=} seed_length
|
||||
* @param {function(Error, string=)=} callback
|
||||
*/
|
||||
bcrypt.genSalt = function(rounds, seed_length, callback) {};
|
||||
|
||||
/**
|
||||
* @param {string} s
|
||||
* @param {(number|string)=} salt
|
||||
* @returns {?string}
|
||||
*/
|
||||
bcrypt.hashSync = function(s, salt) {};
|
||||
|
||||
/**
|
||||
* @param {string} s
|
||||
* @param {number|string} salt
|
||||
* @param {function(Error, string=)} callback
|
||||
* @expose
|
||||
*/
|
||||
bcrypt.hash = function(s, salt, callback) {};
|
||||
|
||||
/**
|
||||
* @param {string} s
|
||||
* @param {string} hash
|
||||
* @returns {boolean}
|
||||
* @throws {Error}
|
||||
*/
|
||||
bcrypt.compareSync = function(s, hash) {};
|
||||
|
||||
/**
|
||||
* @param {string} s
|
||||
* @param {string} hash
|
||||
* @param {function(Error, boolean)} callback
|
||||
* @throws {Error}
|
||||
*/
|
||||
bcrypt.compare = function(s, hash, callback) {};
|
||||
|
||||
/**
|
||||
* @param {string} hash
|
||||
* @returns {number}
|
||||
* @throws {Error}
|
||||
*/
|
||||
bcrypt.getRounds = function(hash) {};
|
||||
|
||||
/**
|
||||
* @param {string} hash
|
||||
* @returns {string}
|
||||
* @throws {Error}
|
||||
* @expose
|
||||
*/
|
||||
bcrypt.getSalt = function(hash) {};
|
|
@ -0,0 +1,98 @@
|
|||
/**
|
||||
* @fileoverview Minimal environment for bcrypt.js.
|
||||
* @externs
|
||||
*/
|
||||
|
||||
/**
|
||||
* @param {string} moduleName
|
||||
* returns {*}
|
||||
*/
|
||||
function require(moduleName) {}
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @private
|
||||
*/
|
||||
var Module = function() {};
|
||||
|
||||
/**
|
||||
* @type {*}
|
||||
*/
|
||||
Module.prototype.exports;
|
||||
|
||||
/**
|
||||
* @type {Module}
|
||||
*/
|
||||
var module;
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
var __dirname;
|
||||
|
||||
/**
|
||||
* @type {Object.<string,*>}
|
||||
*/
|
||||
var process = {};
|
||||
|
||||
/**
|
||||
* @param {function()} func
|
||||
*/
|
||||
process.nextTick = function(func) {};
|
||||
|
||||
/**
|
||||
* @param {string} s
|
||||
* @constructor
|
||||
* @extends Array
|
||||
*/
|
||||
var Buffer = function(s) {};
|
||||
|
||||
/**
|
||||
BEGIN_NODE_INCLUDE
|
||||
var crypto = require('crypto');
|
||||
END_NODE_INCLUDE
|
||||
*/
|
||||
|
||||
/**
|
||||
* @type {Object.<string,*>}
|
||||
*/
|
||||
var crypto = {};
|
||||
|
||||
/**
|
||||
* @param {number} n
|
||||
* @returns {Array.<number>}
|
||||
*/
|
||||
crypto.randomBytes = function(n) {};
|
||||
|
||||
/**
|
||||
* @type {Object.<string,*>}
|
||||
*/
|
||||
window.crypto = {};
|
||||
|
||||
/**
|
||||
* @param {Uint8Array|Int8Array|Uint16Array|Int16Array|Uint32Array|Int32Array} array
|
||||
*/
|
||||
window.crypto.getRandomValues = function(array) {};
|
||||
|
||||
/**
|
||||
* @param {string} name
|
||||
* @param {function(...[*]):*} constructor
|
||||
*/
|
||||
var define = function(name, constructor) {};
|
||||
|
||||
/**
|
||||
* @type {boolean}
|
||||
*/
|
||||
define.amd;
|
||||
|
||||
/**
|
||||
* @param {...*} var_args
|
||||
* @returns {string}
|
||||
*/
|
||||
String.fromCodePoint = function(var_args) {};
|
||||
|
||||
/**
|
||||
* @param {number} offset
|
||||
* @returns {number}
|
||||
*/
|
||||
String.prototype.codePointAt = function(offset) {};
|
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
Copyright (c) 2012 Nevins Bartolomeo <nevins.bartolomeo@gmail.com>
|
||||
Copyright (c) 2012 Shane Girish <shaneGirish@gmail.com>
|
||||
Copyright (c) 2013 Daniel Wirtz <dcode@dcode.io>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
module.exports = require("./dist/bcrypt.js");
|
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"name": "bcryptjs",
|
||||
"description": "Optimized bcrypt in plain JavaScript with zero dependencies. Compatible to 'bcrypt'.",
|
||||
"version": "2.4.3",
|
||||
"author": "Daniel Wirtz <dcode@dcode.io>",
|
||||
"contributors": [
|
||||
"Shane Girish <shaneGirish@gmail.com> (https://github.com/shaneGirish)",
|
||||
"Alex Murray <> (https://github.com/alexmurray)",
|
||||
"Nicolas Pelletier <> (https://github.com/NicolasPelletier)",
|
||||
"Josh Rogers <> (https://github.com/geekymole)",
|
||||
"Noah Isaacson <noah@nisaacson.com> (https://github.com/nisaacson)"
|
||||
],
|
||||
"repository": {
|
||||
"type": "url",
|
||||
"url": "https://github.com/dcodeIO/bcrypt.js.git"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/dcodeIO/bcrypt.js/issues"
|
||||
},
|
||||
"keywords": [
|
||||
"bcrypt",
|
||||
"password",
|
||||
"auth",
|
||||
"authentication",
|
||||
"encryption",
|
||||
"crypt",
|
||||
"crypto"
|
||||
],
|
||||
"main": "index.js",
|
||||
"browser": "dist/bcrypt.js",
|
||||
"dependencies": {},
|
||||
"devDependencies": {
|
||||
"testjs": "~1",
|
||||
"closurecompiler": "~1",
|
||||
"metascript": "~0.18",
|
||||
"bcrypt": "latest",
|
||||
"utfx": "~1"
|
||||
},
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"test": "node node_modules/testjs/bin/testjs",
|
||||
"build": "node scripts/build.js",
|
||||
"compile": "node node_modules/closurecompiler/bin/ccjs dist/bcrypt.js --compilation_level=SIMPLE_OPTIMIZATIONS --create_source_map=dist/bcrypt.min.map > dist/bcrypt.min.js",
|
||||
"compress": "gzip -c -9 dist/bcrypt.min.js > dist/bcrypt.min.js.gz",
|
||||
"make": "npm run build && npm run compile && npm run compress && npm test"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
var MetaScript = require("metascript"),
|
||||
path = require("path"),
|
||||
fs = require("fs");
|
||||
|
||||
var rootDir = path.join(__dirname, ".."),
|
||||
srcDir = path.join(rootDir, "src"),
|
||||
distDir = path.join(rootDir, "dist"),
|
||||
pkg = require(path.join(rootDir, "package.json")),
|
||||
filename;
|
||||
|
||||
var scope = {
|
||||
VERSION: pkg.version,
|
||||
ISAAC: false
|
||||
};
|
||||
|
||||
// Make standard build
|
||||
console.log("Building bcrypt.js with scope", JSON.stringify(scope, null, 2));
|
||||
fs.writeFileSync(
|
||||
path.join(distDir, "bcrypt.js"),
|
||||
MetaScript.transform(fs.readFileSync(filename = path.join(srcDir, "wrap.js")), filename, scope, srcDir)
|
||||
);
|
||||
|
||||
// Make isaac build - see: https://github.com/dcodeIO/bcrypt.js/issues/16
|
||||
/* scope.ISAAC = true;
|
||||
console.log("Building bcrypt-isaac.js with scope", JSON.stringify(scope, null, 2));
|
||||
fs.writeFileSync(
|
||||
path.join(distDir, "bcrypt-isaac.js"),
|
||||
MetaScript.transform(fs.readFileSync(filename = path.join(srcDir, "bcrypt.js")), filename, scope, srcDir)
|
||||
); */
|
||||
|
||||
// Update bower.json
|
||||
scope = { VERSION: pkg.version };
|
||||
console.log("Updating bower.json with scope", JSON.stringify(scope, null, 2));
|
||||
fs.writeFileSync(
|
||||
path.join(rootDir, "bower.json"),
|
||||
MetaScript.transform(fs.readFileSync(filename = path.join(srcDir, "bower.json")), filename, scope, srcDir)
|
||||
);
|
|
@ -0,0 +1,327 @@
|
|||
/**
|
||||
* bcrypt namespace.
|
||||
* @type {Object.<string,*>}
|
||||
*/
|
||||
var bcrypt = {};
|
||||
|
||||
/**
|
||||
* The random implementation to use as a fallback.
|
||||
* @type {?function(number):!Array.<number>}
|
||||
* @inner
|
||||
*/
|
||||
var randomFallback = null;
|
||||
|
||||
/**
|
||||
* Generates cryptographically secure random bytes.
|
||||
* @function
|
||||
* @param {number} len Bytes length
|
||||
* @returns {!Array.<number>} Random bytes
|
||||
* @throws {Error} If no random implementation is available
|
||||
* @inner
|
||||
*/
|
||||
function random(len) {
|
||||
/* node */ if (typeof module !== 'undefined' && module && module['exports'])
|
||||
try {
|
||||
return require("crypto")['randomBytes'](len);
|
||||
} catch (e) {}
|
||||
/* WCA */ try {
|
||||
var a; (self['crypto']||self['msCrypto'])['getRandomValues'](a = new Uint32Array(len));
|
||||
return Array.prototype.slice.call(a);
|
||||
} catch (e) {}
|
||||
/* fallback */ if (!randomFallback)
|
||||
throw Error("Neither WebCryptoAPI nor a crypto module is available. Use bcrypt.setRandomFallback to set an alternative");
|
||||
return randomFallback(len);
|
||||
}
|
||||
|
||||
// Test if any secure randomness source is available
|
||||
var randomAvailable = false;
|
||||
try {
|
||||
random(1);
|
||||
randomAvailable = true;
|
||||
} catch (e) {}
|
||||
|
||||
// Default fallback, if any
|
||||
randomFallback = /*? if (ISAAC) { */function(len) {
|
||||
for (var a=[], i=0; i<len; ++i)
|
||||
a[i] = ((0.5 + isaac() * 2.3283064365386963e-10) * 256) | 0;
|
||||
return a;
|
||||
};/*? } else { */null;/*? }*/
|
||||
|
||||
/**
|
||||
* Sets the pseudo random number generator to use as a fallback if neither node's `crypto` module nor the Web Crypto
|
||||
* API is available. Please note: It is highly important that the PRNG used is cryptographically secure and that it
|
||||
* is seeded properly!
|
||||
* @param {?function(number):!Array.<number>} random Function taking the number of bytes to generate as its
|
||||
* sole argument, returning the corresponding array of cryptographically secure random byte values.
|
||||
* @see http://nodejs.org/api/crypto.html
|
||||
* @see http://www.w3.org/TR/WebCryptoAPI/
|
||||
*/
|
||||
bcrypt.setRandomFallback = function(random) {
|
||||
randomFallback = random;
|
||||
};
|
||||
|
||||
/**
|
||||
* Synchronously generates a salt.
|
||||
* @param {number=} rounds Number of rounds to use, defaults to 10 if omitted
|
||||
* @param {number=} seed_length Not supported.
|
||||
* @returns {string} Resulting salt
|
||||
* @throws {Error} If a random fallback is required but not set
|
||||
* @expose
|
||||
*/
|
||||
bcrypt.genSaltSync = function(rounds, seed_length) {
|
||||
rounds = rounds || GENSALT_DEFAULT_LOG2_ROUNDS;
|
||||
if (typeof rounds !== 'number')
|
||||
throw Error("Illegal arguments: "+(typeof rounds)+", "+(typeof seed_length));
|
||||
if (rounds < 4)
|
||||
rounds = 4;
|
||||
else if (rounds > 31)
|
||||
rounds = 31;
|
||||
var salt = [];
|
||||
salt.push("$2a$");
|
||||
if (rounds < 10)
|
||||
salt.push("0");
|
||||
salt.push(rounds.toString());
|
||||
salt.push('$');
|
||||
salt.push(base64_encode(random(BCRYPT_SALT_LEN), BCRYPT_SALT_LEN)); // May throw
|
||||
return salt.join('');
|
||||
};
|
||||
|
||||
/**
|
||||
* Asynchronously generates a salt.
|
||||
* @param {(number|function(Error, string=))=} rounds Number of rounds to use, defaults to 10 if omitted
|
||||
* @param {(number|function(Error, string=))=} seed_length Not supported.
|
||||
* @param {function(Error, string=)=} callback Callback receiving the error, if any, and the resulting salt
|
||||
* @returns {!Promise} If `callback` has been omitted
|
||||
* @throws {Error} If `callback` is present but not a function
|
||||
* @expose
|
||||
*/
|
||||
bcrypt.genSalt = function(rounds, seed_length, callback) {
|
||||
if (typeof seed_length === 'function')
|
||||
callback = seed_length,
|
||||
seed_length = undefined; // Not supported.
|
||||
if (typeof rounds === 'function')
|
||||
callback = rounds,
|
||||
rounds = undefined;
|
||||
if (typeof rounds === 'undefined')
|
||||
rounds = GENSALT_DEFAULT_LOG2_ROUNDS;
|
||||
else if (typeof rounds !== 'number')
|
||||
throw Error("illegal arguments: "+(typeof rounds));
|
||||
|
||||
function _async(callback) {
|
||||
nextTick(function() { // Pretty thin, but salting is fast enough
|
||||
try {
|
||||
callback(null, bcrypt.genSaltSync(rounds));
|
||||
} catch (err) {
|
||||
callback(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
if (typeof callback !== 'function')
|
||||
throw Error("Illegal callback: "+typeof(callback));
|
||||
_async(callback);
|
||||
} else
|
||||
return new Promise(function(resolve, reject) {
|
||||
_async(function(err, res) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve(res);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Synchronously generates a hash for the given string.
|
||||
* @param {string} s String to hash
|
||||
* @param {(number|string)=} salt Salt length to generate or salt to use, default to 10
|
||||
* @returns {string} Resulting hash
|
||||
* @expose
|
||||
*/
|
||||
bcrypt.hashSync = function(s, salt) {
|
||||
if (typeof salt === 'undefined')
|
||||
salt = GENSALT_DEFAULT_LOG2_ROUNDS;
|
||||
if (typeof salt === 'number')
|
||||
salt = bcrypt.genSaltSync(salt);
|
||||
if (typeof s !== 'string' || typeof salt !== 'string')
|
||||
throw Error("Illegal arguments: "+(typeof s)+', '+(typeof salt));
|
||||
return _hash(s, salt);
|
||||
};
|
||||
|
||||
/**
|
||||
* Asynchronously generates a hash for the given string.
|
||||
* @param {string} s String to hash
|
||||
* @param {number|string} salt Salt length to generate or salt to use
|
||||
* @param {function(Error, string=)=} callback Callback receiving the error, if any, and the resulting hash
|
||||
* @param {function(number)=} progressCallback Callback successively called with the percentage of rounds completed
|
||||
* (0.0 - 1.0), maximally once per `MAX_EXECUTION_TIME = 100` ms.
|
||||
* @returns {!Promise} If `callback` has been omitted
|
||||
* @throws {Error} If `callback` is present but not a function
|
||||
* @expose
|
||||
*/
|
||||
bcrypt.hash = function(s, salt, callback, progressCallback) {
|
||||
|
||||
function _async(callback) {
|
||||
if (typeof s === 'string' && typeof salt === 'number')
|
||||
bcrypt.genSalt(salt, function(err, salt) {
|
||||
_hash(s, salt, callback, progressCallback);
|
||||
});
|
||||
else if (typeof s === 'string' && typeof salt === 'string')
|
||||
_hash(s, salt, callback, progressCallback);
|
||||
else
|
||||
nextTick(callback.bind(this, Error("Illegal arguments: "+(typeof s)+', '+(typeof salt))));
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
if (typeof callback !== 'function')
|
||||
throw Error("Illegal callback: "+typeof(callback));
|
||||
_async(callback);
|
||||
} else
|
||||
return new Promise(function(resolve, reject) {
|
||||
_async(function(err, res) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve(res);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Compares two strings of the same length in constant time.
|
||||
* @param {string} known Must be of the correct length
|
||||
* @param {string} unknown Must be the same length as `known`
|
||||
* @returns {boolean}
|
||||
* @inner
|
||||
*/
|
||||
function safeStringCompare(known, unknown) {
|
||||
var right = 0,
|
||||
wrong = 0;
|
||||
for (var i=0, k=known.length; i<k; ++i) {
|
||||
if (known.charCodeAt(i) === unknown.charCodeAt(i))
|
||||
++right;
|
||||
else
|
||||
++wrong;
|
||||
}
|
||||
// Prevent removal of unused variables (never true, actually)
|
||||
if (right < 0)
|
||||
return false;
|
||||
return wrong === 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronously tests a string against a hash.
|
||||
* @param {string} s String to compare
|
||||
* @param {string} hash Hash to test against
|
||||
* @returns {boolean} true if matching, otherwise false
|
||||
* @throws {Error} If an argument is illegal
|
||||
* @expose
|
||||
*/
|
||||
bcrypt.compareSync = function(s, hash) {
|
||||
if (typeof s !== "string" || typeof hash !== "string")
|
||||
throw Error("Illegal arguments: "+(typeof s)+', '+(typeof hash));
|
||||
if (hash.length !== 60)
|
||||
return false;
|
||||
return safeStringCompare(bcrypt.hashSync(s, hash.substr(0, hash.length-31)), hash);
|
||||
};
|
||||
|
||||
/**
|
||||
* Asynchronously compares the given data against the given hash.
|
||||
* @param {string} s Data to compare
|
||||
* @param {string} hash Data to be compared to
|
||||
* @param {function(Error, boolean)=} callback Callback receiving the error, if any, otherwise the result
|
||||
* @param {function(number)=} progressCallback Callback successively called with the percentage of rounds completed
|
||||
* (0.0 - 1.0), maximally once per `MAX_EXECUTION_TIME = 100` ms.
|
||||
* @returns {!Promise} If `callback` has been omitted
|
||||
* @throws {Error} If `callback` is present but not a function
|
||||
* @expose
|
||||
*/
|
||||
bcrypt.compare = function(s, hash, callback, progressCallback) {
|
||||
|
||||
function _async(callback) {
|
||||
if (typeof s !== "string" || typeof hash !== "string") {
|
||||
nextTick(callback.bind(this, Error("Illegal arguments: "+(typeof s)+', '+(typeof hash))));
|
||||
return;
|
||||
}
|
||||
if (hash.length !== 60) {
|
||||
nextTick(callback.bind(this, null, false));
|
||||
return;
|
||||
}
|
||||
bcrypt.hash(s, hash.substr(0, 29), function(err, comp) {
|
||||
if (err)
|
||||
callback(err);
|
||||
else
|
||||
callback(null, safeStringCompare(comp, hash));
|
||||
}, progressCallback);
|
||||
}
|
||||
|
||||
if (callback) {
|
||||
if (typeof callback !== 'function')
|
||||
throw Error("Illegal callback: "+typeof(callback));
|
||||
_async(callback);
|
||||
} else
|
||||
return new Promise(function(resolve, reject) {
|
||||
_async(function(err, res) {
|
||||
if (err) {
|
||||
reject(err);
|
||||
return;
|
||||
}
|
||||
resolve(res);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the number of rounds used to encrypt the specified hash.
|
||||
* @param {string} hash Hash to extract the used number of rounds from
|
||||
* @returns {number} Number of rounds used
|
||||
* @throws {Error} If `hash` is not a string
|
||||
* @expose
|
||||
*/
|
||||
bcrypt.getRounds = function(hash) {
|
||||
if (typeof hash !== "string")
|
||||
throw Error("Illegal arguments: "+(typeof hash));
|
||||
return parseInt(hash.split("$")[2], 10);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the salt portion from a hash. Does not validate the hash.
|
||||
* @param {string} hash Hash to extract the salt from
|
||||
* @returns {string} Extracted salt part
|
||||
* @throws {Error} If `hash` is not a string or otherwise invalid
|
||||
* @expose
|
||||
*/
|
||||
bcrypt.getSalt = function(hash) {
|
||||
if (typeof hash !== 'string')
|
||||
throw Error("Illegal arguments: "+(typeof hash));
|
||||
if (hash.length !== 60)
|
||||
throw Error("Illegal hash length: "+hash.length+" != 60");
|
||||
return hash.substring(0, 29);
|
||||
};
|
||||
|
||||
//? include("bcrypt/util.js");
|
||||
|
||||
//? include("bcrypt/impl.js");
|
||||
|
||||
/**
|
||||
* Encodes a byte array to base64 with up to len bytes of input, using the custom bcrypt alphabet.
|
||||
* @function
|
||||
* @param {!Array.<number>} b Byte array
|
||||
* @param {number} len Maximum input length
|
||||
* @returns {string}
|
||||
* @expose
|
||||
*/
|
||||
bcrypt.encodeBase64 = base64_encode;
|
||||
|
||||
/**
|
||||
* Decodes a base64 encoded string to up to len bytes of output, using the custom bcrypt alphabet.
|
||||
* @function
|
||||
* @param {string} s String to decode
|
||||
* @param {number} len Maximum output length
|
||||
* @returns {!Array.<number>}
|
||||
* @expose
|
||||
*/
|
||||
bcrypt.decodeBase64 = base64_decode;
|
|
@ -0,0 +1,669 @@
|
|||
/**
|
||||
* @type {number}
|
||||
* @const
|
||||
* @inner
|
||||
*/
|
||||
var BCRYPT_SALT_LEN = 16;
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
* @const
|
||||
* @inner
|
||||
*/
|
||||
var GENSALT_DEFAULT_LOG2_ROUNDS = 10;
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
* @const
|
||||
* @inner
|
||||
*/
|
||||
var BLOWFISH_NUM_ROUNDS = 16;
|
||||
|
||||
/**
|
||||
* @type {number}
|
||||
* @const
|
||||
* @inner
|
||||
*/
|
||||
var MAX_EXECUTION_TIME = 100;
|
||||
|
||||
/**
|
||||
* @type {Array.<number>}
|
||||
* @const
|
||||
* @inner
|
||||
*/
|
||||
var P_ORIG = [
|
||||
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822,
|
||||
0x299f31d0, 0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377,
|
||||
0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5,
|
||||
0xb5470917, 0x9216d5d9, 0x8979fb1b
|
||||
];
|
||||
|
||||
/**
|
||||
* @type {Array.<number>}
|
||||
* @const
|
||||
* @inner
|
||||
*/
|
||||
var S_ORIG = [
|
||||
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed,
|
||||
0x6a267e96, 0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7,
|
||||
0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3,
|
||||
0xf4933d7e, 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
|
||||
0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, 0xc5d1b023,
|
||||
0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e,
|
||||
0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda,
|
||||
0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
|
||||
0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af,
|
||||
0x7c72e993, 0xb3ee1411, 0x636fbc2a, 0x2ba9c55d, 0x741831f6,
|
||||
0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381,
|
||||
0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
|
||||
0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d,
|
||||
0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5,
|
||||
0x0f6d6ff3, 0x83f44239, 0x2e0b4482, 0xa4842004, 0x69c8f04a,
|
||||
0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
|
||||
0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c,
|
||||
0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176,
|
||||
0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3,
|
||||
0x3b8b5ebe, 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
|
||||
0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, 0x37d0d724,
|
||||
0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b,
|
||||
0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b, 0x976ce0bd,
|
||||
0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
|
||||
0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f,
|
||||
0x9b30952c, 0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd,
|
||||
0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39,
|
||||
0xb9d3fbdb, 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
|
||||
0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, 0x3c7516df,
|
||||
0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760,
|
||||
0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e,
|
||||
0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
|
||||
0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98,
|
||||
0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, 0x9a53e479, 0xb6f84565,
|
||||
0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341,
|
||||
0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
|
||||
0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0,
|
||||
0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64,
|
||||
0x8888b812, 0x900df01c, 0x4fad5ea0, 0x688fc31c, 0xd1cff191,
|
||||
0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
|
||||
0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0,
|
||||
0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705,
|
||||
0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5,
|
||||
0xfb9d35cf, 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
|
||||
0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, 0x2464369b,
|
||||
0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f,
|
||||
0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9, 0x11c81968,
|
||||
0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
|
||||
0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5,
|
||||
0x571be91f, 0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6,
|
||||
0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799,
|
||||
0x6e85076a, 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
|
||||
0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, 0xecaa8c71,
|
||||
0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29,
|
||||
0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65, 0x6b8fe4d6,
|
||||
0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
|
||||
0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f,
|
||||
0x3ebaefc9, 0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286,
|
||||
0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec,
|
||||
0x5716f2b8, 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
|
||||
0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, 0xd19113f9,
|
||||
0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc,
|
||||
0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e,
|
||||
0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
|
||||
0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290,
|
||||
0x24977c79, 0x5679b072, 0xbcaf89af, 0xde9a771f, 0xd9930810,
|
||||
0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6,
|
||||
0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
|
||||
0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847,
|
||||
0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451,
|
||||
0x50940002, 0x133ae4dd, 0x71dff89e, 0x10314e55, 0x81ac77d6,
|
||||
0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
|
||||
0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570,
|
||||
0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa,
|
||||
0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978,
|
||||
0x9c10b36a, 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
|
||||
0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, 0x5223a708,
|
||||
0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883,
|
||||
0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5, 0x65582185,
|
||||
0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
|
||||
0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830,
|
||||
0xeb61bd96, 0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239,
|
||||
0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab,
|
||||
0xb2f3846e, 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
|
||||
0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, 0x9b540b19,
|
||||
0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77,
|
||||
0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1,
|
||||
0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
|
||||
0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef,
|
||||
0x34c6ffea, 0xfe28ed61, 0xee7c3c73, 0x5d4a14d9, 0xe864b7e3,
|
||||
0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15,
|
||||
0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
|
||||
0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2,
|
||||
0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492,
|
||||
0x47848a0b, 0x5692b285, 0x095bbf00, 0xad19489d, 0x1462b174,
|
||||
0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
|
||||
0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759,
|
||||
0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e,
|
||||
0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc,
|
||||
0x800bcadc, 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
|
||||
0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, 0xc5c43465,
|
||||
0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a,
|
||||
0xe6e39f2b, 0xdb83adf7, 0xe93d5a68, 0x948140f7, 0xf64c261c,
|
||||
0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
|
||||
0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e,
|
||||
0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af,
|
||||
0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0,
|
||||
0x31cb8504, 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
|
||||
0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, 0x68dc1462,
|
||||
0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c,
|
||||
0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec, 0xce78a399,
|
||||
0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
|
||||
0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74,
|
||||
0xdd5b4332, 0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397,
|
||||
0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7,
|
||||
0xd096954b, 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
|
||||
0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, 0xfdf8e802,
|
||||
0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22,
|
||||
0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4,
|
||||
0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
|
||||
0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2,
|
||||
0x02e1329e, 0xaf664fd1, 0xcad18115, 0x6b2395e0, 0x333e92e1,
|
||||
0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c,
|
||||
0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
|
||||
0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341,
|
||||
0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8,
|
||||
0x991be14c, 0xdb6e6b0d, 0xc67b5510, 0x6d672c37, 0x2765d43b,
|
||||
0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
|
||||
0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88,
|
||||
0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979,
|
||||
0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc,
|
||||
0x782ef11c, 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
|
||||
0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, 0x44421659,
|
||||
0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f,
|
||||
0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086, 0x60787bf8,
|
||||
0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
|
||||
0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be,
|
||||
0xbde8ae24, 0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2,
|
||||
0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255,
|
||||
0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
|
||||
0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, 0xb90bace1,
|
||||
0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09,
|
||||
0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025,
|
||||
0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
|
||||
0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01,
|
||||
0xa70683fa, 0xa002b5c4, 0x0de6d027, 0x9af88c27, 0x773f8641,
|
||||
0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa,
|
||||
0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
|
||||
0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409,
|
||||
0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9,
|
||||
0x1ac15bb4, 0xd39eb8fc, 0xed545578, 0x08fca5b5, 0xd83d7cd3,
|
||||
0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
|
||||
0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234,
|
||||
0x92638212, 0x670efa8e, 0x406000e0, 0x3a39ce37, 0xd3faf5cf,
|
||||
0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740,
|
||||
0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
|
||||
0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f,
|
||||
0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d,
|
||||
0xd5730a1d, 0x4cd04dc6, 0x2939bbdb, 0xa9ba4650, 0xac9526e8,
|
||||
0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
|
||||
0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba,
|
||||
0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1,
|
||||
0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69,
|
||||
0x77fa0a59, 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
|
||||
0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a,
|
||||
0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b,
|
||||
0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6, 0x47b0acfd,
|
||||
0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
|
||||
0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4,
|
||||
0x88f46dba, 0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2,
|
||||
0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb,
|
||||
0x26dcf319, 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
|
||||
0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, 0x4de81751,
|
||||
0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce,
|
||||
0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369,
|
||||
0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
|
||||
0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd,
|
||||
0x1b588d40, 0xccd2017f, 0x6bb4e3bb, 0xdda26a7e, 0x3a59ff45,
|
||||
0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae,
|
||||
0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
|
||||
0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08,
|
||||
0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d,
|
||||
0x06b89fb4, 0xce6ea048, 0x6f3f3b82, 0x3520ab82, 0x011a1d4b,
|
||||
0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
|
||||
0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e,
|
||||
0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a,
|
||||
0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c,
|
||||
0xe0b12b4f, 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
|
||||
0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, 0xfae59361,
|
||||
0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c,
|
||||
0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be,
|
||||
0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
|
||||
0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d,
|
||||
0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891,
|
||||
0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5,
|
||||
0xf6fb2299, 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
|
||||
0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, 0xde966292,
|
||||
0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a,
|
||||
0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2,
|
||||
0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
|
||||
0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c,
|
||||
0xf746ce76, 0x77afa1c5, 0x20756060, 0x85cbfe4e, 0x8ae88dd8,
|
||||
0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4,
|
||||
0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
|
||||
0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6
|
||||
];
|
||||
|
||||
/**
|
||||
* @type {Array.<number>}
|
||||
* @const
|
||||
* @inner
|
||||
*/
|
||||
var C_ORIG = [
|
||||
0x4f727068, 0x65616e42, 0x65686f6c, 0x64657253, 0x63727944,
|
||||
0x6f756274
|
||||
];
|
||||
|
||||
/**
|
||||
* @param {Array.<number>} lr
|
||||
* @param {number} off
|
||||
* @param {Array.<number>} P
|
||||
* @param {Array.<number>} S
|
||||
* @returns {Array.<number>}
|
||||
* @inner
|
||||
*/
|
||||
function _encipher(lr, off, P, S) { // This is our bottleneck: 1714/1905 ticks / 90% - see profile.txt
|
||||
var n,
|
||||
l = lr[off],
|
||||
r = lr[off + 1];
|
||||
|
||||
l ^= P[0];
|
||||
|
||||
/*
|
||||
for (var i=0, k=BLOWFISH_NUM_ROUNDS-2; i<=k;)
|
||||
// Feistel substitution on left word
|
||||
n = S[l >>> 24],
|
||||
n += S[0x100 | ((l >> 16) & 0xff)],
|
||||
n ^= S[0x200 | ((l >> 8) & 0xff)],
|
||||
n += S[0x300 | (l & 0xff)],
|
||||
r ^= n ^ P[++i],
|
||||
// Feistel substitution on right word
|
||||
n = S[r >>> 24],
|
||||
n += S[0x100 | ((r >> 16) & 0xff)],
|
||||
n ^= S[0x200 | ((r >> 8) & 0xff)],
|
||||
n += S[0x300 | (r & 0xff)],
|
||||
l ^= n ^ P[++i];
|
||||
*/
|
||||
|
||||
//The following is an unrolled version of the above loop.
|
||||
//Iteration 0
|
||||
n = S[l >>> 24];
|
||||
n += S[0x100 | ((l >> 16) & 0xff)];
|
||||
n ^= S[0x200 | ((l >> 8) & 0xff)];
|
||||
n += S[0x300 | (l & 0xff)];
|
||||
r ^= n ^ P[1];
|
||||
n = S[r >>> 24];
|
||||
n += S[0x100 | ((r >> 16) & 0xff)];
|
||||
n ^= S[0x200 | ((r >> 8) & 0xff)];
|
||||
n += S[0x300 | (r & 0xff)];
|
||||
l ^= n ^ P[2];
|
||||
//Iteration 1
|
||||
n = S[l >>> 24];
|
||||
n += S[0x100 | ((l >> 16) & 0xff)];
|
||||
n ^= S[0x200 | ((l >> 8) & 0xff)];
|
||||
n += S[0x300 | (l & 0xff)];
|
||||
r ^= n ^ P[3];
|
||||
n = S[r >>> 24];
|
||||
n += S[0x100 | ((r >> 16) & 0xff)];
|
||||
n ^= S[0x200 | ((r >> 8) & 0xff)];
|
||||
n += S[0x300 | (r & 0xff)];
|
||||
l ^= n ^ P[4];
|
||||
//Iteration 2
|
||||
n = S[l >>> 24];
|
||||
n += S[0x100 | ((l >> 16) & 0xff)];
|
||||
n ^= S[0x200 | ((l >> 8) & 0xff)];
|
||||
n += S[0x300 | (l & 0xff)];
|
||||
r ^= n ^ P[5];
|
||||
n = S[r >>> 24];
|
||||
n += S[0x100 | ((r >> 16) & 0xff)];
|
||||
n ^= S[0x200 | ((r >> 8) & 0xff)];
|
||||
n += S[0x300 | (r & 0xff)];
|
||||
l ^= n ^ P[6];
|
||||
//Iteration 3
|
||||
n = S[l >>> 24];
|
||||
n += S[0x100 | ((l >> 16) & 0xff)];
|
||||
n ^= S[0x200 | ((l >> 8) & 0xff)];
|
||||
n += S[0x300 | (l & 0xff)];
|
||||
r ^= n ^ P[7];
|
||||
n = S[r >>> 24];
|
||||
n += S[0x100 | ((r >> 16) & 0xff)];
|
||||
n ^= S[0x200 | ((r >> 8) & 0xff)];
|
||||
n += S[0x300 | (r & 0xff)];
|
||||
l ^= n ^ P[8];
|
||||
//Iteration 4
|
||||
n = S[l >>> 24];
|
||||
n += S[0x100 | ((l >> 16) & 0xff)];
|
||||
n ^= S[0x200 | ((l >> 8) & 0xff)];
|
||||
n += S[0x300 | (l & 0xff)];
|
||||
r ^= n ^ P[9];
|
||||
n = S[r >>> 24];
|
||||
n += S[0x100 | ((r >> 16) & 0xff)];
|
||||
n ^= S[0x200 | ((r >> 8) & 0xff)];
|
||||
n += S[0x300 | (r & 0xff)];
|
||||
l ^= n ^ P[10];
|
||||
//Iteration 5
|
||||
n = S[l >>> 24];
|
||||
n += S[0x100 | ((l >> 16) & 0xff)];
|
||||
n ^= S[0x200 | ((l >> 8) & 0xff)];
|
||||
n += S[0x300 | (l & 0xff)];
|
||||
r ^= n ^ P[11];
|
||||
n = S[r >>> 24];
|
||||
n += S[0x100 | ((r >> 16) & 0xff)];
|
||||
n ^= S[0x200 | ((r >> 8) & 0xff)];
|
||||
n += S[0x300 | (r & 0xff)];
|
||||
l ^= n ^ P[12];
|
||||
//Iteration 6
|
||||
n = S[l >>> 24];
|
||||
n += S[0x100 | ((l >> 16) & 0xff)];
|
||||
n ^= S[0x200 | ((l >> 8) & 0xff)];
|
||||
n += S[0x300 | (l & 0xff)];
|
||||
r ^= n ^ P[13];
|
||||
n = S[r >>> 24];
|
||||
n += S[0x100 | ((r >> 16) & 0xff)];
|
||||
n ^= S[0x200 | ((r >> 8) & 0xff)];
|
||||
n += S[0x300 | (r & 0xff)];
|
||||
l ^= n ^ P[14];
|
||||
//Iteration 7
|
||||
n = S[l >>> 24];
|
||||
n += S[0x100 | ((l >> 16) & 0xff)];
|
||||
n ^= S[0x200 | ((l >> 8) & 0xff)];
|
||||
n += S[0x300 | (l & 0xff)];
|
||||
r ^= n ^ P[15];
|
||||
n = S[r >>> 24];
|
||||
n += S[0x100 | ((r >> 16) & 0xff)];
|
||||
n ^= S[0x200 | ((r >> 8) & 0xff)];
|
||||
n += S[0x300 | (r & 0xff)];
|
||||
l ^= n ^ P[16];
|
||||
|
||||
lr[off] = r ^ P[BLOWFISH_NUM_ROUNDS + 1];
|
||||
lr[off + 1] = l;
|
||||
return lr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array.<number>} data
|
||||
* @param {number} offp
|
||||
* @returns {{key: number, offp: number}}
|
||||
* @inner
|
||||
*/
|
||||
function _streamtoword(data, offp) {
|
||||
for (var i = 0, word = 0; i < 4; ++i)
|
||||
word = (word << 8) | (data[offp] & 0xff),
|
||||
offp = (offp + 1) % data.length;
|
||||
return { key: word, offp: offp };
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array.<number>} key
|
||||
* @param {Array.<number>} P
|
||||
* @param {Array.<number>} S
|
||||
* @inner
|
||||
*/
|
||||
function _key(key, P, S) {
|
||||
var offset = 0,
|
||||
lr = [0, 0],
|
||||
plen = P.length,
|
||||
slen = S.length,
|
||||
sw;
|
||||
for (var i = 0; i < plen; i++)
|
||||
sw = _streamtoword(key, offset),
|
||||
offset = sw.offp,
|
||||
P[i] = P[i] ^ sw.key;
|
||||
for (i = 0; i < plen; i += 2)
|
||||
lr = _encipher(lr, 0, P, S),
|
||||
P[i] = lr[0],
|
||||
P[i + 1] = lr[1];
|
||||
for (i = 0; i < slen; i += 2)
|
||||
lr = _encipher(lr, 0, P, S),
|
||||
S[i] = lr[0],
|
||||
S[i + 1] = lr[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Expensive key schedule Blowfish.
|
||||
* @param {Array.<number>} data
|
||||
* @param {Array.<number>} key
|
||||
* @param {Array.<number>} P
|
||||
* @param {Array.<number>} S
|
||||
* @inner
|
||||
*/
|
||||
function _ekskey(data, key, P, S) {
|
||||
var offp = 0,
|
||||
lr = [0, 0],
|
||||
plen = P.length,
|
||||
slen = S.length,
|
||||
sw;
|
||||
for (var i = 0; i < plen; i++)
|
||||
sw = _streamtoword(key, offp),
|
||||
offp = sw.offp,
|
||||
P[i] = P[i] ^ sw.key;
|
||||
offp = 0;
|
||||
for (i = 0; i < plen; i += 2)
|
||||
sw = _streamtoword(data, offp),
|
||||
offp = sw.offp,
|
||||
lr[0] ^= sw.key,
|
||||
sw = _streamtoword(data, offp),
|
||||
offp = sw.offp,
|
||||
lr[1] ^= sw.key,
|
||||
lr = _encipher(lr, 0, P, S),
|
||||
P[i] = lr[0],
|
||||
P[i + 1] = lr[1];
|
||||
for (i = 0; i < slen; i += 2)
|
||||
sw = _streamtoword(data, offp),
|
||||
offp = sw.offp,
|
||||
lr[0] ^= sw.key,
|
||||
sw = _streamtoword(data, offp),
|
||||
offp = sw.offp,
|
||||
lr[1] ^= sw.key,
|
||||
lr = _encipher(lr, 0, P, S),
|
||||
S[i] = lr[0],
|
||||
S[i + 1] = lr[1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Internaly crypts a string.
|
||||
* @param {Array.<number>} b Bytes to crypt
|
||||
* @param {Array.<number>} salt Salt bytes to use
|
||||
* @param {number} rounds Number of rounds
|
||||
* @param {function(Error, Array.<number>=)=} callback Callback receiving the error, if any, and the resulting bytes. If
|
||||
* omitted, the operation will be performed synchronously.
|
||||
* @param {function(number)=} progressCallback Callback called with the current progress
|
||||
* @returns {!Array.<number>|undefined} Resulting bytes if callback has been omitted, otherwise `undefined`
|
||||
* @inner
|
||||
*/
|
||||
function _crypt(b, salt, rounds, callback, progressCallback) {
|
||||
var cdata = C_ORIG.slice(),
|
||||
clen = cdata.length,
|
||||
err;
|
||||
|
||||
// Validate
|
||||
if (rounds < 4 || rounds > 31) {
|
||||
err = Error("Illegal number of rounds (4-31): "+rounds);
|
||||
if (callback) {
|
||||
nextTick(callback.bind(this, err));
|
||||
return;
|
||||
} else
|
||||
throw err;
|
||||
}
|
||||
if (salt.length !== BCRYPT_SALT_LEN) {
|
||||
err =Error("Illegal salt length: "+salt.length+" != "+BCRYPT_SALT_LEN);
|
||||
if (callback) {
|
||||
nextTick(callback.bind(this, err));
|
||||
return;
|
||||
} else
|
||||
throw err;
|
||||
}
|
||||
rounds = (1 << rounds) >>> 0;
|
||||
|
||||
var P, S, i = 0, j;
|
||||
|
||||
//Use typed arrays when available - huge speedup!
|
||||
if (Int32Array) {
|
||||
P = new Int32Array(P_ORIG);
|
||||
S = new Int32Array(S_ORIG);
|
||||
} else {
|
||||
P = P_ORIG.slice();
|
||||
S = S_ORIG.slice();
|
||||
}
|
||||
|
||||
_ekskey(salt, b, P, S);
|
||||
|
||||
/**
|
||||
* Calcualtes the next round.
|
||||
* @returns {Array.<number>|undefined} Resulting array if callback has been omitted, otherwise `undefined`
|
||||
* @inner
|
||||
*/
|
||||
function next() {
|
||||
if (progressCallback)
|
||||
progressCallback(i / rounds);
|
||||
if (i < rounds) {
|
||||
var start = Date.now();
|
||||
for (; i < rounds;) {
|
||||
i = i + 1;
|
||||
_key(b, P, S);
|
||||
_key(salt, P, S);
|
||||
if (Date.now() - start > MAX_EXECUTION_TIME)
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < 64; i++)
|
||||
for (j = 0; j < (clen >> 1); j++)
|
||||
_encipher(cdata, j << 1, P, S);
|
||||
var ret = [];
|
||||
for (i = 0; i < clen; i++)
|
||||
ret.push(((cdata[i] >> 24) & 0xff) >>> 0),
|
||||
ret.push(((cdata[i] >> 16) & 0xff) >>> 0),
|
||||
ret.push(((cdata[i] >> 8) & 0xff) >>> 0),
|
||||
ret.push((cdata[i] & 0xff) >>> 0);
|
||||
if (callback) {
|
||||
callback(null, ret);
|
||||
return;
|
||||
} else
|
||||
return ret;
|
||||
}
|
||||
if (callback)
|
||||
nextTick(next);
|
||||
}
|
||||
|
||||
// Async
|
||||
if (typeof callback !== 'undefined') {
|
||||
next();
|
||||
|
||||
// Sync
|
||||
} else {
|
||||
var res;
|
||||
while (true)
|
||||
if (typeof(res = next()) !== 'undefined')
|
||||
return res || [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Internally hashes a string.
|
||||
* @param {string} s String to hash
|
||||
* @param {?string} salt Salt to use, actually never null
|
||||
* @param {function(Error, string=)=} callback Callback receiving the error, if any, and the resulting hash. If omitted,
|
||||
* hashing is perormed synchronously.
|
||||
* @param {function(number)=} progressCallback Callback called with the current progress
|
||||
* @returns {string|undefined} Resulting hash if callback has been omitted, otherwise `undefined`
|
||||
* @inner
|
||||
*/
|
||||
function _hash(s, salt, callback, progressCallback) {
|
||||
var err;
|
||||
if (typeof s !== 'string' || typeof salt !== 'string') {
|
||||
err = Error("Invalid string / salt: Not a string");
|
||||
if (callback) {
|
||||
nextTick(callback.bind(this, err));
|
||||
return;
|
||||
}
|
||||
else
|
||||
throw err;
|
||||
}
|
||||
|
||||
// Validate the salt
|
||||
var minor, offset;
|
||||
if (salt.charAt(0) !== '$' || salt.charAt(1) !== '2') {
|
||||
err = Error("Invalid salt version: "+salt.substring(0,2));
|
||||
if (callback) {
|
||||
nextTick(callback.bind(this, err));
|
||||
return;
|
||||
}
|
||||
else
|
||||
throw err;
|
||||
}
|
||||
if (salt.charAt(2) === '$')
|
||||
minor = String.fromCharCode(0),
|
||||
offset = 3;
|
||||
else {
|
||||
minor = salt.charAt(2);
|
||||
if ((minor !== 'a' && minor !== 'b' && minor !== 'y') || salt.charAt(3) !== '$') {
|
||||
err = Error("Invalid salt revision: "+salt.substring(2,4));
|
||||
if (callback) {
|
||||
nextTick(callback.bind(this, err));
|
||||
return;
|
||||
} else
|
||||
throw err;
|
||||
}
|
||||
offset = 4;
|
||||
}
|
||||
|
||||
// Extract number of rounds
|
||||
if (salt.charAt(offset + 2) > '$') {
|
||||
err = Error("Missing salt rounds");
|
||||
if (callback) {
|
||||
nextTick(callback.bind(this, err));
|
||||
return;
|
||||
} else
|
||||
throw err;
|
||||
}
|
||||
var r1 = parseInt(salt.substring(offset, offset + 1), 10) * 10,
|
||||
r2 = parseInt(salt.substring(offset + 1, offset + 2), 10),
|
||||
rounds = r1 + r2,
|
||||
real_salt = salt.substring(offset + 3, offset + 25);
|
||||
s += minor >= 'a' ? "\x00" : "";
|
||||
|
||||
var passwordb = stringToBytes(s),
|
||||
saltb = base64_decode(real_salt, BCRYPT_SALT_LEN);
|
||||
|
||||
/**
|
||||
* Finishes hashing.
|
||||
* @param {Array.<number>} bytes Byte array
|
||||
* @returns {string}
|
||||
* @inner
|
||||
*/
|
||||
function finish(bytes) {
|
||||
var res = [];
|
||||
res.push("$2");
|
||||
if (minor >= 'a')
|
||||
res.push(minor);
|
||||
res.push("$");
|
||||
if (rounds < 10)
|
||||
res.push("0");
|
||||
res.push(rounds.toString());
|
||||
res.push("$");
|
||||
res.push(base64_encode(saltb, saltb.length));
|
||||
res.push(base64_encode(bytes, C_ORIG.length * 4 - 1));
|
||||
return res.join('');
|
||||
}
|
||||
|
||||
// Sync
|
||||
if (typeof callback == 'undefined')
|
||||
return finish(_crypt(passwordb, saltb, rounds));
|
||||
|
||||
// Async
|
||||
else {
|
||||
_crypt(passwordb, saltb, rounds, function(err, bytes) {
|
||||
if (err)
|
||||
callback(err, null);
|
||||
else
|
||||
callback(null, finish(bytes));
|
||||
}, progressCallback);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
Because of [reasonable security doubts](https://github.com/dcodeIO/bcrypt.js/issues/16), these files, which used to be
|
||||
a part of bcrypt-isaac.js, are no longer used but are kept here for reference only.
|
||||
|
||||
What is required instead is a proper way to collect entropy sources (using an intermediate stream cipher) which is then
|
||||
used to seed the CSPRNG. Pick one and use `bcrypt.setRandomFallback` instead.
|
|
@ -0,0 +1,133 @@
|
|||
/* basic entropy accumulator */
|
||||
var accum = (function() {
|
||||
|
||||
var pool, // randomness pool
|
||||
time, // start timestamp
|
||||
last; // last step timestamp
|
||||
|
||||
/* initialize with default pool */
|
||||
function init() {
|
||||
pool = [];
|
||||
time = new Date().getTime();
|
||||
last = time;
|
||||
// use Math.random
|
||||
pool.push((Math.random() * 0xffffffff)|0);
|
||||
// use current time
|
||||
pool.push(time|0);
|
||||
}
|
||||
|
||||
/* perform one step */
|
||||
function step() {
|
||||
if (!to)
|
||||
return;
|
||||
if (pool.length >= 255) { // stop at 255 values (1 more is added on fetch)
|
||||
stop();
|
||||
return;
|
||||
}
|
||||
var now = new Date().getTime();
|
||||
// use actual time difference
|
||||
pool.push(now-last);
|
||||
// always compute, occasionally use Math.random
|
||||
var rnd = (Math.random() * 0xffffffff)|0;
|
||||
if (now % 2)
|
||||
pool[pool.length-1] += rnd;
|
||||
last = now;
|
||||
to = setTimeout(step, 100+Math.random()*512); // use hypothetical time difference
|
||||
}
|
||||
|
||||
var to = null;
|
||||
|
||||
/* starts accumulating */
|
||||
function start() {
|
||||
if (to) return;
|
||||
to = setTimeout(step, 100+Math.random()*512);
|
||||
if (console.log)
|
||||
console.log("bcrypt-isaac: collecting entropy...");
|
||||
// install collectors
|
||||
if (typeof window !== 'undefined' && window && window.addEventListener)
|
||||
window.addEventListener("load", loadCollector, false),
|
||||
window.addEventListener("mousemove", mouseCollector, false),
|
||||
window.addEventListener("touchmove", touchCollector, false);
|
||||
else if (typeof document !== 'undefined' && document && document.attachEvent)
|
||||
document.attachEvent("onload", loadCollector),
|
||||
document.attachEvent("onmousemove", mouseCollector);
|
||||
}
|
||||
|
||||
/* stops accumulating */
|
||||
function stop() {
|
||||
if (!to) return;
|
||||
clearTimeout(to); to = null;
|
||||
// uninstall collectors
|
||||
if (typeof window !== 'undefined' && window && window.removeEventListener)
|
||||
window.removeEventListener("load", loadCollector, false),
|
||||
window.removeEventListener("mousemove", mouseCollector, false),
|
||||
window.removeEventListener("touchmove", touchCollector, false);
|
||||
else if (typeof document !== 'undefined' && document && document.detachEvent)
|
||||
document.detachEvent("onload", loadCollector),
|
||||
document.detachEvent("onmousemove", mouseCollector);
|
||||
}
|
||||
|
||||
/* fetches the randomness pool */
|
||||
function fetch() {
|
||||
// add overall time difference
|
||||
pool.push((new Date().getTime()-time)|0);
|
||||
var res = pool;
|
||||
init();
|
||||
if (console.log)
|
||||
console.log("bcrypt-isaac: using "+res.length+"/256 samples of entropy");
|
||||
// console.log(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* adds the current time to the top of the pool */
|
||||
function addTime() {
|
||||
pool[pool.length-1] += new Date().getTime() - time;
|
||||
}
|
||||
|
||||
/* page load collector */
|
||||
function loadCollector() {
|
||||
if (!to || pool.length >= 255)
|
||||
return;
|
||||
pool.push(0);
|
||||
addTime();
|
||||
}
|
||||
|
||||
/* mouse events collector */
|
||||
function mouseCollector(ev) {
|
||||
if (!to || pool.length >= 255)
|
||||
return;
|
||||
try {
|
||||
var x = ev.x || ev.clientX || ev.offsetX || 0,
|
||||
y = ev.y || ev.clientY || ev.offsetY || 0;
|
||||
if (x != 0 || y != 0)
|
||||
pool[pool.length-1] += ((x-mouseCollector.last[0]) ^ (y-mouseCollector.last[1])),
|
||||
addTime(),
|
||||
mouseCollector.last = [x,y];
|
||||
} catch (e) {}
|
||||
}
|
||||
mouseCollector.last = [0,0];
|
||||
|
||||
/* touch events collector */
|
||||
function touchCollector(ev) {
|
||||
if (!to || pool.length >= 255)
|
||||
return;
|
||||
try {
|
||||
var touch = ev.touches[0] || ev.changedTouches[0];
|
||||
var x = touch.pageX || touch.clientX || 0,
|
||||
y = touch.pageY || touch.clientY || 0;
|
||||
if (x != 0 || y != 0)
|
||||
pool[pool.length-1] += (x-touchCollector.last[0]) ^ (y-touchCollector.last[1]),
|
||||
addTime(),
|
||||
touchCollector.last = [x,y];
|
||||
} catch (e) {}
|
||||
}
|
||||
touchCollector.last = [0,0];
|
||||
|
||||
init();
|
||||
return {
|
||||
"start": start,
|
||||
"stop": stop,
|
||||
"fetch": fetch
|
||||
}
|
||||
|
||||
})();
|
|
@ -0,0 +1,140 @@
|
|||
/*
|
||||
isaac.js Copyright (c) 2012 Yves-Marie K. Rinquin
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
/* isaac module pattern */
|
||||
var isaac = (function(){
|
||||
|
||||
/* internal states */
|
||||
var m = Array(256), // internal memory
|
||||
acc = 0, // accumulator
|
||||
brs = 0, // last result
|
||||
cnt = 0, // counter
|
||||
r = Array(256), // result array
|
||||
gnt = 0, // generation counter
|
||||
isd = false; // initially seeded
|
||||
|
||||
|
||||
/* 32-bit integer safe adder */
|
||||
function add(x, y) {
|
||||
var lsb = (x & 0xffff) + (y & 0xffff),
|
||||
msb = (x >>> 16) + (y >>> 16) + (lsb >>> 16);
|
||||
return (msb << 16) | (lsb & 0xffff);
|
||||
}
|
||||
|
||||
/* initialisation */
|
||||
function reset() {
|
||||
acc = brs = cnt = 0;
|
||||
for (var i = 0; i < 256; ++i)
|
||||
m[i] = r[i] = 0;
|
||||
gnt = 0;
|
||||
}
|
||||
|
||||
/* seeding function */
|
||||
function seed(s) {
|
||||
var a, b, c, d, e, f, g, h, i;
|
||||
|
||||
/* seeding the seeds of love */
|
||||
a = b = c = d = e = f = g = h = 0x9e3779b9; /* the golden ratio */
|
||||
|
||||
if (s && typeof(s) === 'number')
|
||||
s = [s];
|
||||
|
||||
if (s instanceof Array) {
|
||||
reset();
|
||||
for (i = 0; i < s.length; ++i)
|
||||
r[i & 0xff] += typeof(s[i]) === 'number' ? s[i] : 0;
|
||||
}
|
||||
|
||||
/* private: seed mixer */
|
||||
function seed_mix() {
|
||||
a ^= b << 11; d = add(d, a); b = add(b, c);
|
||||
b ^= c >>> 2; e = add(e, b); c = add(c, d);
|
||||
c ^= d << 8; f = add(f, c); d = add(d, e);
|
||||
d ^= e >>> 16; g = add(g, d); e = add(e, f);
|
||||
e ^= f << 10; h = add(h, e); f = add(f, g);
|
||||
f ^= g >>> 4; a = add(a, f); g = add(g, h);
|
||||
g ^= h << 8; b = add(b, g); h = add(h, a);
|
||||
h ^= a >>> 9; c = add(c, h); a = add(a, b);
|
||||
}
|
||||
|
||||
for (i = 0; i < 4; i++) /* scramble it */
|
||||
seed_mix();
|
||||
|
||||
for (i = 0; i < 256; i += 8) {
|
||||
if (s) /* use all the information in the seed */
|
||||
a = add(a, r[i + 0]), b = add(b, r[i + 1]),
|
||||
c = add(c, r[i + 2]), d = add(d, r[i + 3]),
|
||||
e = add(e, r[i + 4]), f = add(f, r[i + 5]),
|
||||
g = add(g, r[i + 6]), h = add(h, r[i + 7]);
|
||||
seed_mix();
|
||||
/* fill in m[] with messy stuff */
|
||||
m[i + 0] = a; m[i + 1] = b; m[i + 2] = c; m[i + 3] = d;
|
||||
m[i + 4] = e; m[i + 5] = f; m[i + 6] = g; m[i + 7] = h;
|
||||
}
|
||||
if (s)
|
||||
/* do a second pass to make all of the seed affect all of m[] */
|
||||
for (i = 0; i < 256; i += 8)
|
||||
a = add(a, m[i + 0]), b = add(b, m[i + 1]),
|
||||
c = add(c, m[i + 2]), d = add(d, m[i + 3]),
|
||||
e = add(e, m[i + 4]), f = add(f, m[i + 5]),
|
||||
g = add(g, m[i + 6]), h = add(h, m[i + 7]),
|
||||
seed_mix(),
|
||||
/* fill in m[] with messy stuff (again) */
|
||||
m[i + 0] = a, m[i + 1] = b, m[i + 2] = c, m[i + 3] = d,
|
||||
m[i + 4] = e, m[i + 5] = f, m[i + 6] = g, m[i + 7] = h;
|
||||
prng(); /* fill in the first set of results */
|
||||
gnt = 256; /* prepare to use the first set of results */;
|
||||
}
|
||||
|
||||
/* isaac generator, n = number of run */
|
||||
function prng(n) {
|
||||
var i, x, y;
|
||||
n = n && typeof(n) === 'number' ? Math.abs(Math.floor(n)) : 1;
|
||||
while (n--) {
|
||||
cnt = add(cnt, 1);
|
||||
brs = add(brs, cnt);
|
||||
for(i = 0; i < 256; i++) {
|
||||
switch(i & 3) {
|
||||
case 0: acc ^= acc << 13; break;
|
||||
case 1: acc ^= acc >>> 6; break;
|
||||
case 2: acc ^= acc << 2; break;
|
||||
case 3: acc ^= acc >>> 16; break;
|
||||
}
|
||||
acc = add(m[(i + 128) & 0xff], acc); x = m[i];
|
||||
m[i] = y = add(m[(x >>> 2) & 0xff], add(acc, brs));
|
||||
r[i] = brs = add(m[(y >>> 10) & 0xff], x);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* return a random number between */
|
||||
return function() {
|
||||
if (!isd) // seed from accumulator
|
||||
isd = true,
|
||||
accum.stop(),
|
||||
seed(accum.fetch());
|
||||
if (!gnt--)
|
||||
prng(), gnt = 255;
|
||||
return r[gnt];
|
||||
};
|
||||
})();
|
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* Continues with the callback on the next tick.
|
||||
* @function
|
||||
* @param {function(...[*])} callback Callback to execute
|
||||
* @inner
|
||||
*/
|
||||
var nextTick = typeof process !== 'undefined' && process && typeof process.nextTick === 'function'
|
||||
? (typeof setImmediate === 'function' ? setImmediate : process.nextTick)
|
||||
: setTimeout;
|
||||
|
||||
/**
|
||||
* Converts a JavaScript string to UTF8 bytes.
|
||||
* @param {string} str String
|
||||
* @returns {!Array.<number>} UTF8 bytes
|
||||
* @inner
|
||||
*/
|
||||
function stringToBytes(str) {
|
||||
var out = [],
|
||||
i = 0;
|
||||
utfx.encodeUTF16toUTF8(function() {
|
||||
if (i >= str.length) return null;
|
||||
return str.charCodeAt(i++);
|
||||
}, function(b) {
|
||||
out.push(b);
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
//? include("util/base64.js");
|
||||
|
||||
//? include("../../node_modules/utfx/dist/utfx-embeddable.js");
|
||||
|
||||
Date.now = Date.now || function() { return +new Date; };
|
|
@ -0,0 +1,115 @@
|
|||
// A base64 implementation for the bcrypt algorithm. This is partly non-standard.
|
||||
|
||||
/**
|
||||
* bcrypt's own non-standard base64 dictionary.
|
||||
* @type {!Array.<string>}
|
||||
* @const
|
||||
* @inner
|
||||
**/
|
||||
var BASE64_CODE = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789".split('');
|
||||
|
||||
/**
|
||||
* @type {!Array.<number>}
|
||||
* @const
|
||||
* @inner
|
||||
**/
|
||||
var BASE64_INDEX = [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||||
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0,
|
||||
1, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, -1, -1, -1, -1, -1, -1,
|
||||
-1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||
20, 21, 22, 23, 24, 25, 26, 27, -1, -1, -1, -1, -1, -1, 28, 29, 30,
|
||||
31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
|
||||
48, 49, 50, 51, 52, 53, -1, -1, -1, -1, -1];
|
||||
|
||||
/**
|
||||
* @type {!function(...number):string}
|
||||
* @inner
|
||||
*/
|
||||
var stringFromCharCode = String.fromCharCode;
|
||||
|
||||
/**
|
||||
* Encodes a byte array to base64 with up to len bytes of input.
|
||||
* @param {!Array.<number>} b Byte array
|
||||
* @param {number} len Maximum input length
|
||||
* @returns {string}
|
||||
* @inner
|
||||
*/
|
||||
function base64_encode(b, len) {
|
||||
var off = 0,
|
||||
rs = [],
|
||||
c1, c2;
|
||||
if (len <= 0 || len > b.length)
|
||||
throw Error("Illegal len: "+len);
|
||||
while (off < len) {
|
||||
c1 = b[off++] & 0xff;
|
||||
rs.push(BASE64_CODE[(c1 >> 2) & 0x3f]);
|
||||
c1 = (c1 & 0x03) << 4;
|
||||
if (off >= len) {
|
||||
rs.push(BASE64_CODE[c1 & 0x3f]);
|
||||
break;
|
||||
}
|
||||
c2 = b[off++] & 0xff;
|
||||
c1 |= (c2 >> 4) & 0x0f;
|
||||
rs.push(BASE64_CODE[c1 & 0x3f]);
|
||||
c1 = (c2 & 0x0f) << 2;
|
||||
if (off >= len) {
|
||||
rs.push(BASE64_CODE[c1 & 0x3f]);
|
||||
break;
|
||||
}
|
||||
c2 = b[off++] & 0xff;
|
||||
c1 |= (c2 >> 6) & 0x03;
|
||||
rs.push(BASE64_CODE[c1 & 0x3f]);
|
||||
rs.push(BASE64_CODE[c2 & 0x3f]);
|
||||
}
|
||||
return rs.join('');
|
||||
}
|
||||
|
||||
/**
|
||||
* Decodes a base64 encoded string to up to len bytes of output.
|
||||
* @param {string} s String to decode
|
||||
* @param {number} len Maximum output length
|
||||
* @returns {!Array.<number>}
|
||||
* @inner
|
||||
*/
|
||||
function base64_decode(s, len) {
|
||||
var off = 0,
|
||||
slen = s.length,
|
||||
olen = 0,
|
||||
rs = [],
|
||||
c1, c2, c3, c4, o, code;
|
||||
if (len <= 0)
|
||||
throw Error("Illegal len: "+len);
|
||||
while (off < slen - 1 && olen < len) {
|
||||
code = s.charCodeAt(off++);
|
||||
c1 = code < BASE64_INDEX.length ? BASE64_INDEX[code] : -1;
|
||||
code = s.charCodeAt(off++);
|
||||
c2 = code < BASE64_INDEX.length ? BASE64_INDEX[code] : -1;
|
||||
if (c1 == -1 || c2 == -1)
|
||||
break;
|
||||
o = (c1 << 2) >>> 0;
|
||||
o |= (c2 & 0x30) >> 4;
|
||||
rs.push(stringFromCharCode(o));
|
||||
if (++olen >= len || off >= slen)
|
||||
break;
|
||||
code = s.charCodeAt(off++);
|
||||
c3 = code < BASE64_INDEX.length ? BASE64_INDEX[code] : -1;
|
||||
if (c3 == -1)
|
||||
break;
|
||||
o = ((c2 & 0x0f) << 4) >>> 0;
|
||||
o |= (c3 & 0x3c) >> 2;
|
||||
rs.push(stringFromCharCode(o));
|
||||
if (++olen >= len || off >= slen)
|
||||
break;
|
||||
code = s.charCodeAt(off++);
|
||||
c4 = code < BASE64_INDEX.length ? BASE64_INDEX[code] : -1;
|
||||
o = ((c3 & 0x03) << 6) >>> 0;
|
||||
o |= c4;
|
||||
rs.push(stringFromCharCode(o));
|
||||
++olen;
|
||||
}
|
||||
var res = [];
|
||||
for (off = 0; off<olen; off++)
|
||||
res.push(rs[off].charCodeAt(0));
|
||||
return res;
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
"name": "bcryptjs",
|
||||
"description": "Optimized bcrypt in plain JavaScript with zero dependencies.",
|
||||
"version": /*?== VERSION */,
|
||||
"main": "dist/bcrypt.min.js",
|
||||
"license": "New-BSD",
|
||||
"homepage": "http://dcode.io/",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/dcodeIO/bcrypt.js.git"
|
||||
},
|
||||
"keywords": ["bcrypt", "password", "auth", "authentication", "encryption", "crypt", "crypto"],
|
||||
"dependencies": {},
|
||||
"devDependencies": {},
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
//? if (typeof ISAAC === 'undefined') ISAAC = false;
|
||||
/*
|
||||
Copyright (c) 2012 Nevins Bartolomeo <nevins.bartolomeo@gmail.com>
|
||||
Copyright (c) 2012 Shane Girish <shaneGirish@gmail.com>
|
||||
Copyright (c) 2014 Daniel Wirtz <dcode@dcode.io>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @license bcrypt.js (c) 2013 Daniel Wirtz <dcode@dcode.io>
|
||||
* Released under the Apache License, Version 2.0
|
||||
* see: https://github.com/dcodeIO/bcrypt.js for details
|
||||
*/
|
||||
(function(global, factory) {
|
||||
|
||||
/* AMD */ if (typeof define === 'function' && define["amd"])
|
||||
define([], factory);
|
||||
/* CommonJS */ else if (typeof require === 'function' && typeof module === "object" && module && module["exports"])
|
||||
module["exports"] = factory();
|
||||
/* Global */ else
|
||||
(global["dcodeIO"] = global["dcodeIO"] || {})["bcrypt"] = factory();
|
||||
|
||||
}(this, function() {
|
||||
"use strict";
|
||||
|
||||
//? include("bcrypt.js");
|
||||
|
||||
return bcrypt;
|
||||
}));
|
|
@ -0,0 +1,150 @@
|
|||
Sentences that contain all letters commonly used in a language
|
||||
--------------------------------------------------------------
|
||||
|
||||
Markus Kuhn <http://www.cl.cam.ac.uk/~mgk25/> -- 2012-04-11
|
||||
|
||||
This is an example of a plain-text file encoded in UTF-8.
|
||||
|
||||
|
||||
Danish (da)
|
||||
---------
|
||||
|
||||
Quizdeltagerne spiste jordbær med fløde, mens cirkusklovnen
|
||||
Wolther spillede på xylofon.
|
||||
(= Quiz contestants were eating strawbery with cream while Wolther
|
||||
the circus clown played on xylophone.)
|
||||
|
||||
German (de)
|
||||
-----------
|
||||
|
||||
Falsches Üben von Xylophonmusik quält jeden größeren Zwerg
|
||||
(= Wrongful practicing of xylophone music tortures every larger dwarf)
|
||||
|
||||
Zwölf Boxkämpfer jagten Eva quer über den Sylter Deich
|
||||
(= Twelve boxing fighters hunted Eva across the dike of Sylt)
|
||||
|
||||
Heizölrückstoßabdämpfung
|
||||
(= fuel oil recoil absorber)
|
||||
(jqvwxy missing, but all non-ASCII letters in one word)
|
||||
|
||||
Greek (el)
|
||||
----------
|
||||
|
||||
Γαζέες καὶ μυρτιὲς δὲν θὰ βρῶ πιὰ στὸ χρυσαφὶ ξέφωτο
|
||||
(= No more shall I see acacias or myrtles in the golden clearing)
|
||||
|
||||
Ξεσκεπάζω τὴν ψυχοφθόρα βδελυγμία
|
||||
(= I uncover the soul-destroying abhorrence)
|
||||
|
||||
English (en)
|
||||
------------
|
||||
|
||||
The quick brown fox jumps over the lazy dog
|
||||
|
||||
Spanish (es)
|
||||
------------
|
||||
|
||||
El pingüino Wenceslao hizo kilómetros bajo exhaustiva lluvia y
|
||||
frío, añoraba a su querido cachorro.
|
||||
(Contains every letter and every accent, but not every combination
|
||||
of vowel + acute.)
|
||||
|
||||
French (fr)
|
||||
-----------
|
||||
|
||||
Portez ce vieux whisky au juge blond qui fume sur son île intérieure, à
|
||||
côté de l'alcôve ovoïde, où les bûches se consument dans l'âtre, ce
|
||||
qui lui permet de penser à la cænogenèse de l'être dont il est question
|
||||
dans la cause ambiguë entendue à Moÿ, dans un capharnaüm qui,
|
||||
pense-t-il, diminue çà et là la qualité de son œuvre.
|
||||
|
||||
l'île exiguë
|
||||
Où l'obèse jury mûr
|
||||
Fête l'haï volapük,
|
||||
Âne ex aéquo au whist,
|
||||
Ôtez ce vœu déçu.
|
||||
|
||||
Le cœur déçu mais l'âme plutôt naïve, Louÿs rêva de crapaüter en
|
||||
canoë au delà des îles, près du mälström où brûlent les novæ.
|
||||
|
||||
Irish Gaelic (ga)
|
||||
-----------------
|
||||
|
||||
D'fhuascail Íosa, Úrmhac na hÓighe Beannaithe, pór Éava agus Ádhaimh
|
||||
|
||||
Hungarian (hu)
|
||||
--------------
|
||||
|
||||
Árvíztűrő tükörfúrógép
|
||||
(= flood-proof mirror-drilling machine, only all non-ASCII letters)
|
||||
|
||||
Icelandic (is)
|
||||
--------------
|
||||
|
||||
Kæmi ný öxi hér ykist þjófum nú bæði víl og ádrepa
|
||||
|
||||
Sævör grét áðan því úlpan var ónýt
|
||||
(some ASCII letters missing)
|
||||
|
||||
Japanese (jp)
|
||||
-------------
|
||||
|
||||
Hiragana: (Iroha)
|
||||
|
||||
いろはにほへとちりぬるを
|
||||
わかよたれそつねならむ
|
||||
うゐのおくやまけふこえて
|
||||
あさきゆめみしゑひもせす
|
||||
|
||||
Katakana:
|
||||
|
||||
イロハニホヘト チリヌルヲ ワカヨタレソ ツネナラム
|
||||
ウヰノオクヤマ ケフコエテ アサキユメミシ ヱヒモセスン
|
||||
|
||||
Hebrew (iw)
|
||||
-----------
|
||||
|
||||
? דג סקרן שט בים מאוכזב ולפתע מצא לו חברה איך הקליטה
|
||||
|
||||
Polish (pl)
|
||||
-----------
|
||||
|
||||
Pchnąć w tę łódź jeża lub ośm skrzyń fig
|
||||
(= To push a hedgehog or eight bins of figs in this boat)
|
||||
|
||||
Russian (ru)
|
||||
------------
|
||||
|
||||
В чащах юга жил бы цитрус? Да, но фальшивый экземпляр!
|
||||
(= Would a citrus live in the bushes of south? Yes, but only a fake one!)
|
||||
|
||||
Съешь же ещё этих мягких французских булок да выпей чаю
|
||||
(= Eat some more of these fresh French loafs and have some tea)
|
||||
|
||||
Thai (th)
|
||||
---------
|
||||
|
||||
[--------------------------|------------------------]
|
||||
๏ เป็นมนุษย์สุดประเสริฐเลิศคุณค่า กว่าบรรดาฝูงสัตว์เดรัจฉาน
|
||||
จงฝ่าฟันพัฒนาวิชาการ อย่าล้างผลาญฤๅเข่นฆ่าบีฑาใคร
|
||||
ไม่ถือโทษโกรธแช่งซัดฮึดฮัดด่า หัดอภัยเหมือนกีฬาอัชฌาสัย
|
||||
ปฏิบัติประพฤติกฎกำหนดใจ พูดจาให้จ๊ะๆ จ๋าๆ น่าฟังเอย ฯ
|
||||
|
||||
[The copyright for the Thai example is owned by The Computer
|
||||
Association of Thailand under the Royal Patronage of His Majesty the
|
||||
King.]
|
||||
|
||||
Turkish (tr)
|
||||
------------
|
||||
|
||||
Pijamalı hasta, yağız şoföre çabucak güvendi.
|
||||
(=Patient with pajamas, trusted swarthy driver quickly)
|
||||
|
||||
|
||||
Special thanks to the people from all over the world who contributed
|
||||
these sentences since 1999.
|
||||
|
||||
A much larger collection of such pangrams is now available at
|
||||
|
||||
http://en.wikipedia.org/wiki/List_of_pangrams
|
||||
|
|
@ -0,0 +1,197 @@
|
|||
var path = require("path"),
|
||||
fs = require("fs"),
|
||||
binding = require("bcrypt"),
|
||||
bcrypt = require(path.join(__dirname, '..', 'index.js'))/*,
|
||||
isaac = eval(
|
||||
fs.readFileSync(path.join(__dirname, "..", "src", "bcrypt", "prng", "accum.js"))+
|
||||
fs.readFileSync(path.join(__dirname, "..", "src", "bcrypt", "prng", "isaac.js"))+
|
||||
" accum.start();"+
|
||||
" isaac"
|
||||
)*/;
|
||||
|
||||
module.exports = {
|
||||
|
||||
"encodeBase64": function(test) {
|
||||
var str = bcrypt.encodeBase64([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10], 16);
|
||||
test.strictEqual(str, "..CA.uOD/eaGAOmJB.yMBu");
|
||||
test.done();
|
||||
},
|
||||
|
||||
"decodeBase64": function(test) {
|
||||
var bytes = bcrypt.decodeBase64("..CA.uOD/eaGAOmJB.yMBv.", 16);
|
||||
test.deepEqual(bytes, [0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F]);
|
||||
test.done();
|
||||
},
|
||||
|
||||
"genSaltSync": function(test) {
|
||||
var salt = bcrypt.genSaltSync(10);
|
||||
test.ok(salt);
|
||||
test.ok(typeof salt == 'string');
|
||||
test.ok(salt.length > 0);
|
||||
test.done();
|
||||
},
|
||||
|
||||
"genSalt": function(test) {
|
||||
bcrypt.genSalt(10, function(err, salt) {
|
||||
test.ok(salt);
|
||||
test.ok(typeof salt == 'string');
|
||||
test.ok(salt.length > 0);
|
||||
test.done();
|
||||
});
|
||||
},
|
||||
|
||||
"hashSync": function(test) {
|
||||
test.doesNotThrow(function() {
|
||||
bcrypt.hashSync("hello", 10);
|
||||
});
|
||||
test.notEqual(bcrypt.hashSync("hello", 10), bcrypt.hashSync("hello", 10));
|
||||
test.done();
|
||||
},
|
||||
|
||||
"hash": function(test) {
|
||||
bcrypt.hash("hello", 10, function(err, hash) {
|
||||
test.notOk(err);
|
||||
test.ok(hash);
|
||||
test.done();
|
||||
});
|
||||
},
|
||||
|
||||
"compareSync": function(test) {
|
||||
var salt1 = bcrypt.genSaltSync(),
|
||||
hash1 = bcrypt.hashSync("hello", salt1); // $2a$
|
||||
var salt2 = bcrypt.genSaltSync().replace(/\$2a\$/, "$2y$"),
|
||||
hash2 = bcrypt.hashSync("world", salt2);
|
||||
var salt3 = bcrypt.genSaltSync().replace(/\$2a\$/, "$2b$"),
|
||||
hash3 = bcrypt.hashSync("hello world", salt3);
|
||||
|
||||
test.strictEqual(hash1.substring(0,4), "$2a$");
|
||||
test.ok(bcrypt.compareSync("hello", hash1));
|
||||
test.notOk(bcrypt.compareSync("hello", hash2));
|
||||
test.notOk(bcrypt.compareSync("hello", hash3));
|
||||
|
||||
test.strictEqual(hash2.substring(0,4), "$2y$");
|
||||
test.ok(bcrypt.compareSync("world", hash2));
|
||||
test.notOk(bcrypt.compareSync("world", hash1));
|
||||
test.notOk(bcrypt.compareSync("world", hash3));
|
||||
|
||||
test.strictEqual(hash3.substring(0,4), "$2b$");
|
||||
test.ok(bcrypt.compareSync("hello world", hash3));
|
||||
test.notOk(bcrypt.compareSync("hello world", hash1));
|
||||
test.notOk(bcrypt.compareSync("hello world", hash2));
|
||||
|
||||
test.done();
|
||||
},
|
||||
|
||||
"compare": function(test) {
|
||||
var salt1 = bcrypt.genSaltSync(),
|
||||
hash1 = bcrypt.hashSync("hello", salt1); // $2a$
|
||||
var salt2 = bcrypt.genSaltSync();
|
||||
salt2 = salt2.substring(0,2)+'y'+salt2.substring(3); // $2y$
|
||||
var hash2 = bcrypt.hashSync("world", salt2);
|
||||
bcrypt.compare("hello", hash1, function(err, same) {
|
||||
test.notOk(err);
|
||||
test.ok(same);
|
||||
bcrypt.compare("hello", hash2, function(err, same) {
|
||||
test.notOk(err);
|
||||
test.notOk(same);
|
||||
bcrypt.compare("world", hash2, function(err, same) {
|
||||
test.notOk(err);
|
||||
test.ok(same);
|
||||
bcrypt.compare("world", hash1, function(err, same) {
|
||||
test.notOk(err);
|
||||
test.notOk(same);
|
||||
test.done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
"getSalt": function(test) {
|
||||
var hash1 = bcrypt.hashSync("hello", bcrypt.genSaltSync());
|
||||
var salt = bcrypt.getSalt(hash1);
|
||||
var hash2 = bcrypt.hashSync("hello", salt);
|
||||
test.equal(hash1, hash2);
|
||||
test.done();
|
||||
},
|
||||
|
||||
"getRounds": function(test) {
|
||||
var hash1 = bcrypt.hashSync("hello", bcrypt.genSaltSync());
|
||||
test.equal(bcrypt.getRounds(hash1), 10);
|
||||
test.done();
|
||||
},
|
||||
|
||||
"progress": function(test) {
|
||||
bcrypt.genSalt(12, function(err, salt) {
|
||||
test.ok(!err);
|
||||
var progress = [];
|
||||
bcrypt.hash("hello world", salt, function(err, hash) {
|
||||
test.ok(!err);
|
||||
test.ok(typeof hash === 'string');
|
||||
test.ok(progress.length >= 2);
|
||||
test.strictEqual(progress[0], 0);
|
||||
test.strictEqual(progress[progress.length-1], 1);
|
||||
test.done();
|
||||
}, function(n) {
|
||||
progress.push(n);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
"promise": function(test) {
|
||||
bcrypt.genSalt(10)
|
||||
.then(function(salt) {
|
||||
bcrypt.hash("hello", salt)
|
||||
.then(function(hash) {
|
||||
test.ok(hash);
|
||||
bcrypt.compare("hello", hash)
|
||||
.then(function(result) {
|
||||
test.ok(result);
|
||||
bcrypt.genSalt(/* no args */)
|
||||
.then(function(salt) {
|
||||
test.ok(salt);
|
||||
test.done();
|
||||
}, function(err) {
|
||||
test.fail(err, null, "promise rejected");
|
||||
});
|
||||
}, function(err) {
|
||||
test.fail(err, null, "promise rejected");
|
||||
});
|
||||
}, function(err) {
|
||||
test.fail(err, null, 'promise rejected');
|
||||
});
|
||||
}, function(err) {
|
||||
test.fail(err, null, "promise rejected");
|
||||
});
|
||||
},
|
||||
|
||||
"compat": {
|
||||
"quickbrown": function(test) {
|
||||
var pass = fs.readFileSync(path.join(__dirname, "quickbrown.txt"))+"",
|
||||
salt = bcrypt.genSaltSync(),
|
||||
hash1 = binding.hashSync(pass, salt),
|
||||
hash2 = bcrypt.hashSync(pass, salt);
|
||||
test.equal(hash1, hash2);
|
||||
test.done();
|
||||
},
|
||||
|
||||
"roundsOOB": function(test) {
|
||||
var salt1 = bcrypt.genSaltSync(0), // $10$ like not set
|
||||
salt2 = binding.genSaltSync(0);
|
||||
test.strictEqual(salt1.substring(0, 7), "$2a$10$");
|
||||
test.strictEqual(salt2.substring(0, 7), "$2a$10$");
|
||||
|
||||
salt1 = bcrypt.genSaltSync(3); // $04$ is lower cap
|
||||
salt2 = bcrypt.genSaltSync(3);
|
||||
test.strictEqual(salt1.substring(0, 7), "$2a$04$");
|
||||
test.strictEqual(salt2.substring(0, 7), "$2a$04$");
|
||||
|
||||
salt1 = bcrypt.genSaltSync(32); // $31$ is upper cap
|
||||
salt2 = bcrypt.genSaltSync(32);
|
||||
test.strictEqual(salt1.substring(0, 7), "$2a$31$");
|
||||
test.strictEqual(salt2.substring(0, 7), "$2a$31$");
|
||||
|
||||
test.done();
|
||||
}
|
||||
}
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- 4
|
||||
- 6
|
||||
- 8
|
||||
- 10
|
||||
- 11
|
|
@ -0,0 +1,19 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -0,0 +1,48 @@
|
|||
# buffer-writer
|
||||
|
||||
[![Build Status](https://secure.travis-ci.org/brianc/node-buffer-writer.png?branch=master)](http://travis-ci.org/brianc/node-buffer-writer)
|
||||
|
||||
Fast & efficient buffer writer used to keep memory usage low by internally recycling a single large buffer.
|
||||
|
||||
Used as the binary protocol writer in [node-postgres](https://github.com/brianc/node-postgres)
|
||||
|
||||
Since postgres requires big endian encoding, this only writes big endian numbers for now, but can & probably will easily be extended to write little endian as well.
|
||||
|
||||
I'll admit this has a few postgres specific things I might need to take out in the future, such as `addHeader`
|
||||
|
||||
## api
|
||||
|
||||
`var writer = new (require('buffer-writer')());`
|
||||
|
||||
### writer.addInt32(num)
|
||||
|
||||
Writes a 4-byte big endian binary encoded number to the end of the buffer.
|
||||
|
||||
### writer.addInt16(num)
|
||||
|
||||
Writes a 2-byte big endian binary encoded number to the end of the buffer.
|
||||
|
||||
### writer.addCString(string)
|
||||
|
||||
Writes a string to the buffer `utf8` encoded and adds a null character (`\0`) at the end.
|
||||
|
||||
### var buffer = writer.addHeader(char)
|
||||
|
||||
Writes the 5 byte PostgreSQL required header to the beginning of the buffer. (1 byte for character, 1 BE Int32 for length of the buffer)
|
||||
|
||||
### var buffer = writer.join()
|
||||
|
||||
Collects all data in the writer and joins it into a single, new buffer.
|
||||
|
||||
### var buffer = writer.flush(char)
|
||||
|
||||
Writes the 5 byte postgres required message header, collects all data in the writer and joins it into a single, new buffer, and then resets the writer.
|
||||
|
||||
## thoughts
|
||||
|
||||
This is kind of node-postgres specific. If you're interested in using this for a more general purpose thing, lemme know.
|
||||
I would love to work with you on getting this more reusable for your needs.
|
||||
|
||||
## license
|
||||
|
||||
MIT
|
|
@ -0,0 +1,129 @@
|
|||
//binary data writer tuned for creating
|
||||
//postgres message packets as effeciently as possible by reusing the
|
||||
//same buffer to avoid memcpy and limit memory allocations
|
||||
var Writer = module.exports = function (size) {
|
||||
this.size = size || 1024;
|
||||
this.buffer = Buffer.alloc(this.size + 5);
|
||||
this.offset = 5;
|
||||
this.headerPosition = 0;
|
||||
};
|
||||
|
||||
//resizes internal buffer if not enough size left
|
||||
Writer.prototype._ensure = function (size) {
|
||||
var remaining = this.buffer.length - this.offset;
|
||||
if (remaining < size) {
|
||||
var oldBuffer = this.buffer;
|
||||
// exponential growth factor of around ~ 1.5
|
||||
// https://stackoverflow.com/questions/2269063/buffer-growth-strategy
|
||||
var newSize = oldBuffer.length + (oldBuffer.length >> 1) + size;
|
||||
this.buffer = Buffer.alloc(newSize);
|
||||
oldBuffer.copy(this.buffer);
|
||||
}
|
||||
};
|
||||
|
||||
Writer.prototype.addInt32 = function (num) {
|
||||
this._ensure(4);
|
||||
this.buffer[this.offset++] = (num >>> 24 & 0xFF);
|
||||
this.buffer[this.offset++] = (num >>> 16 & 0xFF);
|
||||
this.buffer[this.offset++] = (num >>> 8 & 0xFF);
|
||||
this.buffer[this.offset++] = (num >>> 0 & 0xFF);
|
||||
return this;
|
||||
};
|
||||
|
||||
Writer.prototype.addInt16 = function (num) {
|
||||
this._ensure(2);
|
||||
this.buffer[this.offset++] = (num >>> 8 & 0xFF);
|
||||
this.buffer[this.offset++] = (num >>> 0 & 0xFF);
|
||||
return this;
|
||||
};
|
||||
|
||||
//for versions of node requiring 'length' as 3rd argument to buffer.write
|
||||
var writeString = function (buffer, string, offset, len) {
|
||||
buffer.write(string, offset, len);
|
||||
};
|
||||
|
||||
//overwrite function for older versions of node
|
||||
if (Buffer.prototype.write.length === 3) {
|
||||
writeString = function (buffer, string, offset, len) {
|
||||
buffer.write(string, offset);
|
||||
};
|
||||
}
|
||||
|
||||
Writer.prototype.addCString = function (string) {
|
||||
//just write a 0 for empty or null strings
|
||||
if (!string) {
|
||||
this._ensure(1);
|
||||
} else {
|
||||
var len = Buffer.byteLength(string);
|
||||
this._ensure(len + 1); //+1 for null terminator
|
||||
writeString(this.buffer, string, this.offset, len);
|
||||
this.offset += len;
|
||||
}
|
||||
|
||||
this.buffer[this.offset++] = 0; // null terminator
|
||||
return this;
|
||||
};
|
||||
|
||||
Writer.prototype.addChar = function (c) {
|
||||
this._ensure(1);
|
||||
writeString(this.buffer, c, this.offset, 1);
|
||||
this.offset++;
|
||||
return this;
|
||||
};
|
||||
|
||||
Writer.prototype.addString = function (string) {
|
||||
string = string || "";
|
||||
var len = Buffer.byteLength(string);
|
||||
this._ensure(len);
|
||||
this.buffer.write(string, this.offset);
|
||||
this.offset += len;
|
||||
return this;
|
||||
};
|
||||
|
||||
Writer.prototype.getByteLength = function () {
|
||||
return this.offset - 5;
|
||||
};
|
||||
|
||||
Writer.prototype.add = function (otherBuffer) {
|
||||
this._ensure(otherBuffer.length);
|
||||
otherBuffer.copy(this.buffer, this.offset);
|
||||
this.offset += otherBuffer.length;
|
||||
return this;
|
||||
};
|
||||
|
||||
Writer.prototype.clear = function () {
|
||||
this.offset = 5;
|
||||
this.headerPosition = 0;
|
||||
this.lastEnd = 0;
|
||||
};
|
||||
|
||||
//appends a header block to all the written data since the last
|
||||
//subsequent header or to the beginning if there is only one data block
|
||||
Writer.prototype.addHeader = function (code, last) {
|
||||
var origOffset = this.offset;
|
||||
this.offset = this.headerPosition;
|
||||
this.buffer[this.offset++] = code;
|
||||
//length is everything in this packet minus the code
|
||||
this.addInt32(origOffset - (this.headerPosition + 1));
|
||||
//set next header position
|
||||
this.headerPosition = origOffset;
|
||||
//make space for next header
|
||||
this.offset = origOffset;
|
||||
if (!last) {
|
||||
this._ensure(5);
|
||||
this.offset += 5;
|
||||
}
|
||||
};
|
||||
|
||||
Writer.prototype.join = function (code) {
|
||||
if (code) {
|
||||
this.addHeader(code, true);
|
||||
}
|
||||
return this.buffer.slice(code ? 0 : 5, this.offset);
|
||||
};
|
||||
|
||||
Writer.prototype.flush = function (code) {
|
||||
var result = this.join(code);
|
||||
this.clear();
|
||||
return result;
|
||||
};
|
|
@ -0,0 +1,26 @@
|
|||
{
|
||||
"name": "buffer-writer",
|
||||
"version": "2.0.0",
|
||||
"description": "a fast, efficient buffer writer",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "mocha --throw-deprecation"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/brianc/node-buffer-writer.git"
|
||||
},
|
||||
"keywords": [
|
||||
"buffer",
|
||||
"writer",
|
||||
"builder"
|
||||
],
|
||||
"author": "Brian M. Carlson",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"mocha": "5.2.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
--ui tdd
|
|
@ -0,0 +1,218 @@
|
|||
var Writer = require(__dirname + "/../");
|
||||
|
||||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
assert.equalBuffers = function (actual, expected) {
|
||||
var spit = function (actual, expected) {
|
||||
console.log("");
|
||||
console.log("actual " + util.inspect(actual));
|
||||
console.log("expect " + util.inspect(expected));
|
||||
console.log("");
|
||||
};
|
||||
if (actual.length != expected.length) {
|
||||
spit(actual, expected);
|
||||
assert.strictEqual(actual.length, expected.length);
|
||||
}
|
||||
for (var i = 0; i < actual.length; i++) {
|
||||
if (actual[i] != expected[i]) {
|
||||
spit(actual, expected);
|
||||
}
|
||||
assert.strictEqual(actual[i], expected[i]);
|
||||
}
|
||||
};
|
||||
|
||||
suite('adding int32', function () {
|
||||
var testAddingInt32 = function (int, expectedBuffer) {
|
||||
test('writes ' + int, function () {
|
||||
var subject = new Writer();
|
||||
var result = subject.addInt32(int).join();
|
||||
assert.equalBuffers(result, expectedBuffer);
|
||||
});
|
||||
};
|
||||
|
||||
testAddingInt32(0, [0, 0, 0, 0]);
|
||||
testAddingInt32(1, [0, 0, 0, 1]);
|
||||
testAddingInt32(256, [0, 0, 1, 0]);
|
||||
test('writes largest int32', function () {
|
||||
//todo need to find largest int32 when I have internet access
|
||||
return false;
|
||||
});
|
||||
|
||||
test('writing multiple int32s', function () {
|
||||
var subject = new Writer();
|
||||
var result = subject.addInt32(1).addInt32(10).addInt32(0).join();
|
||||
assert.equalBuffers(result, [0, 0, 0, 1, 0, 0, 0, 0x0a, 0, 0, 0, 0]);
|
||||
});
|
||||
|
||||
suite('having to resize the buffer', function () {
|
||||
test('after resize correct result returned', function () {
|
||||
var subject = new Writer(10);
|
||||
subject.addInt32(1).addInt32(1).addInt32(1);
|
||||
assert.equalBuffers(subject.join(), [0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
suite('int16', function () {
|
||||
test('writes 0', function () {
|
||||
var subject = new Writer();
|
||||
var result = subject.addInt16(0).join();
|
||||
assert.equalBuffers(result, [0, 0]);
|
||||
});
|
||||
|
||||
test('writes 400', function () {
|
||||
var subject = new Writer();
|
||||
var result = subject.addInt16(400).join();
|
||||
assert.equalBuffers(result, [1, 0x90]);
|
||||
});
|
||||
|
||||
test('writes many', function () {
|
||||
var subject = new Writer();
|
||||
var result = subject.addInt16(0).addInt16(1).addInt16(2).join();
|
||||
assert.equalBuffers(result, [0, 0, 0, 1, 0, 2]);
|
||||
});
|
||||
|
||||
test('resizes if internal buffer fills up', function () {
|
||||
var subject = new Writer(3);
|
||||
var result = subject.addInt16(2).addInt16(3).join();
|
||||
assert.equalBuffers(result, [0, 2, 0, 3]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
suite('cString', function () {
|
||||
test('writes empty cstring', function () {
|
||||
var subject = new Writer();
|
||||
var result = subject.addCString().join();
|
||||
assert.equalBuffers(result, [0]);
|
||||
});
|
||||
|
||||
test('writes two empty cstrings', function () {
|
||||
var subject = new Writer();
|
||||
var result = subject.addCString("").addCString("").join();
|
||||
assert.equalBuffers(result, [0, 0]);
|
||||
});
|
||||
|
||||
|
||||
test('writes non-empty cstring', function () {
|
||||
var subject = new Writer();
|
||||
var result = subject.addCString("!!!").join();
|
||||
assert.equalBuffers(result, [33, 33, 33, 0]);
|
||||
});
|
||||
|
||||
test('resizes if reached end', function () {
|
||||
var subject = new Writer(3);
|
||||
var result = subject.addCString("!!!").join();
|
||||
assert.equalBuffers(result, [33, 33, 33, 0]);
|
||||
});
|
||||
|
||||
test('writes multiple cstrings', function () {
|
||||
var subject = new Writer();
|
||||
var result = subject.addCString("!").addCString("!").join();
|
||||
assert.equalBuffers(result, [33, 0, 33, 0]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
test('writes char', function () {
|
||||
var subject = new Writer(2);
|
||||
var result = subject.addChar('a').addChar('b').addChar('c').join();
|
||||
assert.equalBuffers(result, [0x61, 0x62, 0x63]);
|
||||
});
|
||||
|
||||
test('gets correct byte length', function () {
|
||||
var subject = new Writer(5);
|
||||
assert.strictEqual(subject.getByteLength(), 0);
|
||||
subject.addInt32(0);
|
||||
assert.strictEqual(subject.getByteLength(), 4);
|
||||
subject.addCString("!");
|
||||
assert.strictEqual(subject.getByteLength(), 6);
|
||||
});
|
||||
|
||||
test('can add arbitrary buffer to the end', function () {
|
||||
var subject = new Writer(4);
|
||||
subject.addCString("!!!")
|
||||
var result = subject.add(Buffer.from("@@@")).join();
|
||||
assert.equalBuffers(result, [33, 33, 33, 0, 0x40, 0x40, 0x40]);
|
||||
});
|
||||
|
||||
suite('can write normal string', function () {
|
||||
var subject = new Writer(4);
|
||||
var result = subject.addString("!").join();
|
||||
assert.equalBuffers(result, [33]);
|
||||
test('can write cString too', function () {
|
||||
var result = subject.addCString("!").join();
|
||||
assert.equalBuffers(result, [33, 33, 0]);
|
||||
});
|
||||
test('can resize', function () {
|
||||
var result = subject.addString("!!").join();
|
||||
assert.equalBuffers(result, [33, 33, 0, 33, 33]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
suite('clearing', function () {
|
||||
var subject = new Writer();
|
||||
subject.addCString("@!!#!#");
|
||||
subject.addInt32(10401);
|
||||
test('clears', function () {
|
||||
subject.clear();
|
||||
assert.equalBuffers(subject.join(), []);
|
||||
});
|
||||
test('writing more', function () {
|
||||
var joinedResult = subject.addCString("!").addInt32(9).addInt16(2).join();
|
||||
assert.equalBuffers(joinedResult, [33, 0, 0, 0, 0, 9, 0, 2]);
|
||||
});
|
||||
test('returns result', function () {
|
||||
var flushedResult = subject.flush();
|
||||
assert.equalBuffers(flushedResult, [33, 0, 0, 0, 0, 9, 0, 2])
|
||||
});
|
||||
test('clears the writer', function () {
|
||||
assert.equalBuffers(subject.join(), [])
|
||||
assert.equalBuffers(subject.flush(), [])
|
||||
});
|
||||
});
|
||||
|
||||
test("resizing to much larger", function () {
|
||||
var subject = new Writer(2);
|
||||
var string = "!!!!!!!!";
|
||||
var result = subject.addCString(string).flush();
|
||||
assert.equalBuffers(result, [33, 33, 33, 33, 33, 33, 33, 33, 0]);
|
||||
});
|
||||
|
||||
suite("flush", function () {
|
||||
test('added as a hex code to a full writer', function () {
|
||||
var subject = new Writer(2);
|
||||
var result = subject.addCString("!").flush(0x50);
|
||||
assert.equalBuffers(result, [0x50, 0, 0, 0, 6, 33, 0]);
|
||||
});
|
||||
|
||||
test('added as a hex code to a non-full writer', function () {
|
||||
var subject = new Writer(10).addCString("!");
|
||||
var joinedResult = subject.join(0x50);
|
||||
var result = subject.flush(0x50);
|
||||
assert.equalBuffers(result, [0x50, 0, 0, 0, 6, 33, 0]);
|
||||
});
|
||||
|
||||
test('added as a hex code to a buffer which requires resizing', function () {
|
||||
var result = new Writer(2).addCString("!!!!!!!!").flush(0x50);
|
||||
assert.equalBuffers(result, [0x50, 0, 0, 0, 0x0D, 33, 33, 33, 33, 33, 33, 33, 33, 0]);
|
||||
});
|
||||
});
|
||||
|
||||
suite("header", function () {
|
||||
test('adding two packets with headers', function () {
|
||||
var subject = new Writer(10).addCString("!");
|
||||
subject.addHeader(0x50);
|
||||
subject.addCString("!!");
|
||||
subject.addHeader(0x40);
|
||||
subject.addCString("!");
|
||||
var result = subject.flush(0x10);
|
||||
assert.equalBuffers(result, [0x50, 0, 0, 0, 6, 33, 0, 0x40, 0, 0, 0, 7, 33, 33, 0, 0x10, 0, 0, 0, 6, 33, 0]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,431 @@
|
|||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
|
||||
|
||||
## [Unreleased](https://github.com/motdotla/dotenv/compare/v16.3.1...master)
|
||||
|
||||
## [16.3.1](https://github.com/motdotla/dotenv/compare/v16.3.0...v16.3.1) (2023-06-17)
|
||||
|
||||
### Added
|
||||
|
||||
- Add missing type definitions for `processEnv` and `DOTENV_KEY` options. [#756](https://github.com/motdotla/dotenv/pull/756)
|
||||
|
||||
## [16.3.0](https://github.com/motdotla/dotenv/compare/v16.2.0...v16.3.0) (2023-06-16)
|
||||
|
||||
### Added
|
||||
|
||||
- Optionally pass `DOTENV_KEY` to options rather than relying on `process.env.DOTENV_KEY`. Defaults to `process.env.DOTENV_KEY` [#754](https://github.com/motdotla/dotenv/pull/754)
|
||||
|
||||
## [16.2.0](https://github.com/motdotla/dotenv/compare/v16.1.4...v16.2.0) (2023-06-15)
|
||||
|
||||
### Added
|
||||
|
||||
- Optionally write to your own target object rather than `process.env`. Defaults to `process.env`. [#753](https://github.com/motdotla/dotenv/pull/753)
|
||||
- Add import type URL to types file [#751](https://github.com/motdotla/dotenv/pull/751)
|
||||
|
||||
## [16.1.4](https://github.com/motdotla/dotenv/compare/v16.1.3...v16.1.4) (2023-06-04)
|
||||
|
||||
### Added
|
||||
|
||||
- Added `.github/` to `.npmignore` [#747](https://github.com/motdotla/dotenv/pull/747)
|
||||
|
||||
## [16.1.3](https://github.com/motdotla/dotenv/compare/v16.1.2...v16.1.3) (2023-05-31)
|
||||
|
||||
### Removed
|
||||
|
||||
- Removed `browser` keys for `path`, `os`, and `crypto` in package.json. These were set to false incorrectly as of 16.1. Instead, if using dotenv on the front-end make sure to include polyfills for `path`, `os`, and `crypto`. [node-polyfill-webpack-plugin](https://github.com/Richienb/node-polyfill-webpack-plugin) provides these.
|
||||
|
||||
## [16.1.2](https://github.com/motdotla/dotenv/compare/v16.1.1...v16.1.2) (2023-05-31)
|
||||
|
||||
### Changed
|
||||
|
||||
- Exposed private function `_configDotenv` as `configDotenv`. [#744](https://github.com/motdotla/dotenv/pull/744)
|
||||
|
||||
## [16.1.1](https://github.com/motdotla/dotenv/compare/v16.1.0...v16.1.1) (2023-05-30)
|
||||
|
||||
### Added
|
||||
|
||||
- Added type definition for `decrypt` function
|
||||
|
||||
### Changed
|
||||
|
||||
- Fixed `{crypto: false}` in `packageJson.browser`
|
||||
|
||||
## [16.1.0](https://github.com/motdotla/dotenv/compare/v16.0.3...v16.1.0) (2023-05-30)
|
||||
|
||||
### Added
|
||||
|
||||
- Add `populate` convenience method [#733](https://github.com/motdotla/dotenv/pull/733)
|
||||
- Accept URL as path option [#720](https://github.com/motdotla/dotenv/pull/720)
|
||||
- Add dotenv to `npm fund` command
|
||||
- Spanish language README [#698](https://github.com/motdotla/dotenv/pull/698)
|
||||
- Add `.env.vault` support. 🎉 ([#730](https://github.com/motdotla/dotenv/pull/730))
|
||||
|
||||
ℹ️ `.env.vault` extends the `.env` file format standard with a localized encrypted vault file. Package it securely with your production code deploys. It's cloud agnostic so that you can deploy your secrets anywhere – without [risky third-party integrations](https://techcrunch.com/2023/01/05/circleci-breach/). [read more](https://github.com/motdotla/dotenv#-deploying)
|
||||
|
||||
### Changed
|
||||
|
||||
- Fixed "cannot resolve 'fs'" error on tools like Replit [#693](https://github.com/motdotla/dotenv/pull/693)
|
||||
|
||||
## [16.0.3](https://github.com/motdotla/dotenv/compare/v16.0.2...v16.0.3) (2022-09-29)
|
||||
|
||||
### Changed
|
||||
|
||||
- Added library version to debug logs ([#682](https://github.com/motdotla/dotenv/pull/682))
|
||||
|
||||
## [16.0.2](https://github.com/motdotla/dotenv/compare/v16.0.1...v16.0.2) (2022-08-30)
|
||||
|
||||
### Added
|
||||
|
||||
- Export `env-options.js` and `cli-options.js` in package.json for use with downstream [dotenv-expand](https://github.com/motdotla/dotenv-expand) module
|
||||
|
||||
## [16.0.1](https://github.com/motdotla/dotenv/compare/v16.0.0...v16.0.1) (2022-05-10)
|
||||
|
||||
### Changed
|
||||
|
||||
- Minor README clarifications
|
||||
- Development ONLY: updated devDependencies as recommended for development only security risks ([#658](https://github.com/motdotla/dotenv/pull/658))
|
||||
|
||||
## [16.0.0](https://github.com/motdotla/dotenv/compare/v15.0.1...v16.0.0) (2022-02-02)
|
||||
|
||||
### Added
|
||||
|
||||
- _Breaking:_ Backtick support 🎉 ([#615](https://github.com/motdotla/dotenv/pull/615))
|
||||
|
||||
If you had values containing the backtick character, please quote those values with either single or double quotes.
|
||||
|
||||
## [15.0.1](https://github.com/motdotla/dotenv/compare/v15.0.0...v15.0.1) (2022-02-02)
|
||||
|
||||
### Changed
|
||||
|
||||
- Properly parse empty single or double quoted values 🐞 ([#614](https://github.com/motdotla/dotenv/pull/614))
|
||||
|
||||
## [15.0.0](https://github.com/motdotla/dotenv/compare/v14.3.2...v15.0.0) (2022-01-31)
|
||||
|
||||
`v15.0.0` is a major new release with some important breaking changes.
|
||||
|
||||
### Added
|
||||
|
||||
- _Breaking:_ Multiline parsing support (just works. no need for the flag.)
|
||||
|
||||
### Changed
|
||||
|
||||
- _Breaking:_ `#` marks the beginning of a comment (UNLESS the value is wrapped in quotes. Please update your `.env` files to wrap in quotes any values containing `#`. For example: `SECRET_HASH="something-with-a-#-hash"`).
|
||||
|
||||
..Understandably, (as some teams have noted) this is tedious to do across the entire team. To make it less tedious, we recommend using [dotenv cli](https://github.com/dotenv-org/cli) going forward. It's an optional plugin that will keep your `.env` files in sync between machines, environments, or team members.
|
||||
|
||||
### Removed
|
||||
|
||||
- _Breaking:_ Remove multiline option (just works out of the box now. no need for the flag.)
|
||||
|
||||
## [14.3.2](https://github.com/motdotla/dotenv/compare/v14.3.1...v14.3.2) (2022-01-25)
|
||||
|
||||
### Changed
|
||||
|
||||
- Preserve backwards compatibility on values containing `#` 🐞 ([#603](https://github.com/motdotla/dotenv/pull/603))
|
||||
|
||||
## [14.3.1](https://github.com/motdotla/dotenv/compare/v14.3.0...v14.3.1) (2022-01-25)
|
||||
|
||||
### Changed
|
||||
|
||||
- Preserve backwards compatibility on exports by re-introducing the prior in-place exports 🐞 ([#606](https://github.com/motdotla/dotenv/pull/606))
|
||||
|
||||
## [14.3.0](https://github.com/motdotla/dotenv/compare/v14.2.0...v14.3.0) (2022-01-24)
|
||||
|
||||
### Added
|
||||
|
||||
- Add `multiline` option 🎉 ([#486](https://github.com/motdotla/dotenv/pull/486))
|
||||
|
||||
## [14.2.0](https://github.com/motdotla/dotenv/compare/v14.1.1...v14.2.0) (2022-01-17)
|
||||
|
||||
### Added
|
||||
|
||||
- Add `dotenv_config_override` cli option
|
||||
- Add `DOTENV_CONFIG_OVERRIDE` command line env option
|
||||
|
||||
## [14.1.1](https://github.com/motdotla/dotenv/compare/v14.1.0...v14.1.1) (2022-01-17)
|
||||
|
||||
### Added
|
||||
|
||||
- Add React gotcha to FAQ on README
|
||||
|
||||
## [14.1.0](https://github.com/motdotla/dotenv/compare/v14.0.1...v14.1.0) (2022-01-17)
|
||||
|
||||
### Added
|
||||
|
||||
- Add `override` option 🎉 ([#595](https://github.com/motdotla/dotenv/pull/595))
|
||||
|
||||
## [14.0.1](https://github.com/motdotla/dotenv/compare/v14.0.0...v14.0.1) (2022-01-16)
|
||||
|
||||
### Added
|
||||
|
||||
- Log error on failure to load `.env` file ([#594](https://github.com/motdotla/dotenv/pull/594))
|
||||
|
||||
## [14.0.0](https://github.com/motdotla/dotenv/compare/v13.0.1...v14.0.0) (2022-01-16)
|
||||
|
||||
### Added
|
||||
|
||||
- _Breaking:_ Support inline comments for the parser 🎉 ([#568](https://github.com/motdotla/dotenv/pull/568))
|
||||
|
||||
## [13.0.1](https://github.com/motdotla/dotenv/compare/v13.0.0...v13.0.1) (2022-01-16)
|
||||
|
||||
### Changed
|
||||
|
||||
* Hide comments and newlines from debug output ([#404](https://github.com/motdotla/dotenv/pull/404))
|
||||
|
||||
## [13.0.0](https://github.com/motdotla/dotenv/compare/v12.0.4...v13.0.0) (2022-01-16)
|
||||
|
||||
### Added
|
||||
|
||||
* _Breaking:_ Add type file for `config.js` ([#539](https://github.com/motdotla/dotenv/pull/539))
|
||||
|
||||
## [12.0.4](https://github.com/motdotla/dotenv/compare/v12.0.3...v12.0.4) (2022-01-16)
|
||||
|
||||
### Changed
|
||||
|
||||
* README updates
|
||||
* Minor order adjustment to package json format
|
||||
|
||||
## [12.0.3](https://github.com/motdotla/dotenv/compare/v12.0.2...v12.0.3) (2022-01-15)
|
||||
|
||||
### Changed
|
||||
|
||||
* Simplified jsdoc for consistency across editors
|
||||
|
||||
## [12.0.2](https://github.com/motdotla/dotenv/compare/v12.0.1...v12.0.2) (2022-01-15)
|
||||
|
||||
### Changed
|
||||
|
||||
* Improve embedded jsdoc type documentation
|
||||
|
||||
## [12.0.1](https://github.com/motdotla/dotenv/compare/v12.0.0...v12.0.1) (2022-01-15)
|
||||
|
||||
### Changed
|
||||
|
||||
* README updates and clarifications
|
||||
|
||||
## [12.0.0](https://github.com/motdotla/dotenv/compare/v11.0.0...v12.0.0) (2022-01-15)
|
||||
|
||||
### Removed
|
||||
|
||||
- _Breaking:_ drop support for Flow static type checker ([#584](https://github.com/motdotla/dotenv/pull/584))
|
||||
|
||||
### Changed
|
||||
|
||||
- Move types/index.d.ts to lib/main.d.ts ([#585](https://github.com/motdotla/dotenv/pull/585))
|
||||
- Typescript cleanup ([#587](https://github.com/motdotla/dotenv/pull/587))
|
||||
- Explicit typescript inclusion in package.json ([#566](https://github.com/motdotla/dotenv/pull/566))
|
||||
|
||||
## [11.0.0](https://github.com/motdotla/dotenv/compare/v10.0.0...v11.0.0) (2022-01-11)
|
||||
|
||||
### Changed
|
||||
|
||||
- _Breaking:_ drop support for Node v10 ([#558](https://github.com/motdotla/dotenv/pull/558))
|
||||
- Patch debug option ([#550](https://github.com/motdotla/dotenv/pull/550))
|
||||
|
||||
## [10.0.0](https://github.com/motdotla/dotenv/compare/v9.0.2...v10.0.0) (2021-05-20)
|
||||
|
||||
### Added
|
||||
|
||||
- Add generic support to parse function
|
||||
- Allow for import "dotenv/config.js"
|
||||
- Add support to resolve home directory in path via ~
|
||||
|
||||
## [9.0.2](https://github.com/motdotla/dotenv/compare/v9.0.1...v9.0.2) (2021-05-10)
|
||||
|
||||
### Changed
|
||||
|
||||
- Support windows newlines with debug mode
|
||||
|
||||
## [9.0.1](https://github.com/motdotla/dotenv/compare/v9.0.0...v9.0.1) (2021-05-08)
|
||||
|
||||
### Changed
|
||||
|
||||
- Updates to README
|
||||
|
||||
## [9.0.0](https://github.com/motdotla/dotenv/compare/v8.6.0...v9.0.0) (2021-05-05)
|
||||
|
||||
### Changed
|
||||
|
||||
- _Breaking:_ drop support for Node v8
|
||||
|
||||
## [8.6.0](https://github.com/motdotla/dotenv/compare/v8.5.1...v8.6.0) (2021-05-05)
|
||||
|
||||
### Added
|
||||
|
||||
- define package.json in exports
|
||||
|
||||
## [8.5.1](https://github.com/motdotla/dotenv/compare/v8.5.0...v8.5.1) (2021-05-05)
|
||||
|
||||
### Changed
|
||||
|
||||
- updated dev dependencies via npm audit
|
||||
|
||||
## [8.5.0](https://github.com/motdotla/dotenv/compare/v8.4.0...v8.5.0) (2021-05-05)
|
||||
|
||||
### Added
|
||||
|
||||
- allow for `import "dotenv/config"`
|
||||
|
||||
## [8.4.0](https://github.com/motdotla/dotenv/compare/v8.3.0...v8.4.0) (2021-05-05)
|
||||
|
||||
### Changed
|
||||
|
||||
- point to exact types file to work with VS Code
|
||||
|
||||
## [8.3.0](https://github.com/motdotla/dotenv/compare/v8.2.0...v8.3.0) (2021-05-05)
|
||||
|
||||
### Changed
|
||||
|
||||
- _Breaking:_ drop support for Node v8 (mistake to be released as minor bump. later bumped to 9.0.0. see above.)
|
||||
|
||||
## [8.2.0](https://github.com/motdotla/dotenv/compare/v8.1.0...v8.2.0) (2019-10-16)
|
||||
|
||||
### Added
|
||||
|
||||
- TypeScript types
|
||||
|
||||
## [8.1.0](https://github.com/motdotla/dotenv/compare/v8.0.0...v8.1.0) (2019-08-18)
|
||||
|
||||
### Changed
|
||||
|
||||
- _Breaking:_ drop support for Node v6 ([#392](https://github.com/motdotla/dotenv/issues/392))
|
||||
|
||||
# [8.0.0](https://github.com/motdotla/dotenv/compare/v7.0.0...v8.0.0) (2019-05-02)
|
||||
|
||||
### Changed
|
||||
|
||||
- _Breaking:_ drop support for Node v6 ([#302](https://github.com/motdotla/dotenv/issues/392))
|
||||
|
||||
## [7.0.0] - 2019-03-12
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fix removing unbalanced quotes ([#376](https://github.com/motdotla/dotenv/pull/376))
|
||||
|
||||
### Removed
|
||||
|
||||
- Removed `load` alias for `config` for consistency throughout code and documentation.
|
||||
|
||||
## [6.2.0] - 2018-12-03
|
||||
|
||||
### Added
|
||||
|
||||
- Support preload configuration via environment variables ([#351](https://github.com/motdotla/dotenv/issues/351))
|
||||
|
||||
## [6.1.0] - 2018-10-08
|
||||
|
||||
### Added
|
||||
|
||||
- `debug` option for `config` and `parse` methods will turn on logging
|
||||
|
||||
## [6.0.0] - 2018-06-02
|
||||
|
||||
### Changed
|
||||
|
||||
- _Breaking:_ drop support for Node v4 ([#304](https://github.com/motdotla/dotenv/pull/304))
|
||||
|
||||
## [5.0.0] - 2018-01-29
|
||||
|
||||
### Added
|
||||
|
||||
- Testing against Node v8 and v9
|
||||
- Documentation on trim behavior of values
|
||||
- Documentation on how to use with `import`
|
||||
|
||||
### Changed
|
||||
|
||||
- _Breaking_: default `path` is now `path.resolve(process.cwd(), '.env')`
|
||||
- _Breaking_: does not write over keys already in `process.env` if the key has a falsy value
|
||||
- using `const` and `let` instead of `var`
|
||||
|
||||
### Removed
|
||||
|
||||
- Testing against Node v7
|
||||
|
||||
## [4.0.0] - 2016-12-23
|
||||
|
||||
### Changed
|
||||
|
||||
- Return Object with parsed content or error instead of false ([#165](https://github.com/motdotla/dotenv/pull/165)).
|
||||
|
||||
### Removed
|
||||
|
||||
- `verbose` option removed in favor of returning result.
|
||||
|
||||
## [3.0.0] - 2016-12-20
|
||||
|
||||
### Added
|
||||
|
||||
- `verbose` option will log any error messages. Off by default.
|
||||
- parses email addresses correctly
|
||||
- allow importing config method directly in ES6
|
||||
|
||||
### Changed
|
||||
|
||||
- Suppress error messages by default ([#154](https://github.com/motdotla/dotenv/pull/154))
|
||||
- Ignoring more files for NPM to make package download smaller
|
||||
|
||||
### Fixed
|
||||
|
||||
- False positive test due to case-sensitive variable ([#124](https://github.com/motdotla/dotenv/pull/124))
|
||||
|
||||
### Removed
|
||||
|
||||
- `silent` option removed in favor of `verbose`
|
||||
|
||||
## [2.0.0] - 2016-01-20
|
||||
|
||||
### Added
|
||||
|
||||
- CHANGELOG to ["make it easier for users and contributors to see precisely what notable changes have been made between each release"](http://keepachangelog.com/). Linked to from README
|
||||
- LICENSE to be more explicit about what was defined in `package.json`. Linked to from README
|
||||
- Testing nodejs v4 on travis-ci
|
||||
- added examples of how to use dotenv in different ways
|
||||
- return parsed object on success rather than boolean true
|
||||
|
||||
### Changed
|
||||
|
||||
- README has shorter description not referencing ruby gem since we don't have or want feature parity
|
||||
|
||||
### Removed
|
||||
|
||||
- Variable expansion and escaping so environment variables are encouraged to be fully orthogonal
|
||||
|
||||
## [1.2.0] - 2015-06-20
|
||||
|
||||
### Added
|
||||
|
||||
- Preload hook to require dotenv without including it in your code
|
||||
|
||||
### Changed
|
||||
|
||||
- clarified license to be "BSD-2-Clause" in `package.json`
|
||||
|
||||
### Fixed
|
||||
|
||||
- retain spaces in string vars
|
||||
|
||||
## [1.1.0] - 2015-03-31
|
||||
|
||||
### Added
|
||||
|
||||
- Silent option to silence `console.log` when `.env` missing
|
||||
|
||||
## [1.0.0] - 2015-03-13
|
||||
|
||||
### Removed
|
||||
|
||||
- support for multiple `.env` files. should always use one `.env` file for the current environment
|
||||
|
||||
[7.0.0]: https://github.com/motdotla/dotenv/compare/v6.2.0...v7.0.0
|
||||
[6.2.0]: https://github.com/motdotla/dotenv/compare/v6.1.0...v6.2.0
|
||||
[6.1.0]: https://github.com/motdotla/dotenv/compare/v6.0.0...v6.1.0
|
||||
[6.0.0]: https://github.com/motdotla/dotenv/compare/v5.0.0...v6.0.0
|
||||
[5.0.0]: https://github.com/motdotla/dotenv/compare/v4.0.0...v5.0.0
|
||||
[4.0.0]: https://github.com/motdotla/dotenv/compare/v3.0.0...v4.0.0
|
||||
[3.0.0]: https://github.com/motdotla/dotenv/compare/v2.0.0...v3.0.0
|
||||
[2.0.0]: https://github.com/motdotla/dotenv/compare/v1.2.0...v2.0.0
|
||||
[1.2.0]: https://github.com/motdotla/dotenv/compare/v1.1.0...v1.2.0
|
||||
[1.1.0]: https://github.com/motdotla/dotenv/compare/v1.0.0...v1.1.0
|
||||
[1.0.0]: https://github.com/motdotla/dotenv/compare/v0.4.0...v1.0.0
|
|
@ -0,0 +1,23 @@
|
|||
Copyright (c) 2015, Scott Motte
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,442 @@
|
|||
<div align="center">
|
||||
|
||||
<p>
|
||||
<sup>
|
||||
<a href="https://github.com/sponsors/motdotla">Dotenv es apoyado por la comunidad.</a>
|
||||
</sup>
|
||||
</p>
|
||||
<sup>Gracias espaciales a:</sup>
|
||||
<br>
|
||||
<br>
|
||||
<a href="https://www.warp.dev/?utm_source=github&utm_medium=referral&utm_campaign=dotenv_p_20220831">
|
||||
<div>
|
||||
<img src="https://res.cloudinary.com/dotenv-org/image/upload/v1661980709/warp_hi8oqj.png" width="230" alt="Warp">
|
||||
</div>
|
||||
<b>Warp es una rápida e impresionante terminal basada en Rust, reinventado para funcionar como una aplicación moderna.</b>
|
||||
<div>
|
||||
<sup>Haga más en la CLI con edición de texto real, resultado básado en bloques, y busqueda de comandos de IA.</sup>
|
||||
</div>
|
||||
</a>
|
||||
<br>
|
||||
<a href="https://retool.com/?utm_source=sponsor&utm_campaign=dotenv">
|
||||
<div>
|
||||
<img src="https://res.cloudinary.com/dotenv-org/image/upload/c_scale,w_300/v1664466968/logo-full-black_vidfqf.png" width="270" alt="Retool">
|
||||
</div>
|
||||
<b>Retool ayuda a los desarrolladores a crear software interno personalizado, como aplicaciones CRUD y paneles de administración, realmente rápido.</b>
|
||||
<div>
|
||||
<sup>Construya Interfaces de Usuario de forma visual con componentes flexibles, conéctese a cualquier fuente de datos, y escriba lógica de negocio en JavaScript.</sup>
|
||||
</div>
|
||||
</a>
|
||||
<br>
|
||||
<a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=dotenv&utm_source=github">
|
||||
<div>
|
||||
<img src="https://res.cloudinary.com/dotenv-org/image/upload/c_scale,w_400/v1665605496/68747470733a2f2f73696e647265736f726875732e636f6d2f6173736574732f7468616e6b732f776f726b6f732d6c6f676f2d77686974652d62672e737667_zdmsbu.svg" width="270" alt="WorkOS">
|
||||
</div>
|
||||
<b>Su Apliación, Lista para la Empresa.</b>
|
||||
<div>
|
||||
<sup>Agrega Inicio de Sesión Único, Autenticación Multi-Factor, y mucho más, en minutos en lugar de meses.</sup>
|
||||
</div>
|
||||
</a>
|
||||
<hr>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
|
||||
</div>
|
||||
|
||||
# dotenv [![NPM version](https://img.shields.io/npm/v/dotenv.svg?style=flat-square)](https://www.npmjs.com/package/dotenv)
|
||||
|
||||
<img src="https://raw.githubusercontent.com/motdotla/dotenv/master/dotenv.svg" alt="dotenv" align="right" width="200" />
|
||||
|
||||
Dotenv es un módulo de dependencia cero que carga las variables de entorno desde un archivo `.env` en [`process.env`](https://nodejs.org/docs/latest/api/process.html#process_process_env). El almacenamiento de la configuración del entorno separado del código está basado en la metodología [The Twelve-Factor App](http://12factor.net/config).
|
||||
|
||||
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/feross/standard)
|
||||
[![LICENSE](https://img.shields.io/github/license/motdotla/dotenv.svg)](LICENSE)
|
||||
|
||||
## Instalación
|
||||
|
||||
```bash
|
||||
# instalación local (recomendado)
|
||||
npm install dotenv --save
|
||||
```
|
||||
|
||||
O installación con yarn? `yarn add dotenv`
|
||||
|
||||
## Uso
|
||||
|
||||
Cree un archivo `.env` en la raíz de su proyecto:
|
||||
|
||||
```dosini
|
||||
S3_BUCKET="YOURS3BUCKET"
|
||||
SECRET_KEY="YOURSECRETKEYGOESHERE"
|
||||
```
|
||||
|
||||
Tan prónto como sea posible en su aplicación, importe y configure dotenv:
|
||||
|
||||
```javascript
|
||||
require('dotenv').config()
|
||||
console.log(process.env) // elimine esto después que haya confirmado que esta funcionando
|
||||
```
|
||||
|
||||
.. o usa ES6?
|
||||
|
||||
```javascript
|
||||
import * as dotenv from 'dotenv' // vea en https://github.com/motdotla/dotenv#como-uso-dotenv-con-import
|
||||
// REVISAR LINK DE REFERENCIA DE IMPORTACIÓN
|
||||
dotenv.config()
|
||||
import express from 'express'
|
||||
```
|
||||
|
||||
Eso es todo. `process.env` ahora tiene las claves y los valores que definiste en tu archivo `.env`:
|
||||
|
||||
```javascript
|
||||
require('dotenv').config()
|
||||
|
||||
...
|
||||
|
||||
s3.getBucketCors({Bucket: process.env.S3_BUCKET}, function(err, data) {})
|
||||
```
|
||||
|
||||
### Valores multilínea
|
||||
|
||||
Si necesita variables de varias líneas, por ejemplo, claves privadas, ahora se admiten en la versión (`>= v15.0.0`) con saltos de línea:
|
||||
|
||||
```dosini
|
||||
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
|
||||
...
|
||||
Kh9NV...
|
||||
...
|
||||
-----END RSA PRIVATE KEY-----"
|
||||
```
|
||||
|
||||
Alternativamente, puede usar comillas dobles y usar el carácter `\n`:
|
||||
|
||||
```dosini
|
||||
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nKh9NV...\n-----END RSA PRIVATE KEY-----\n"
|
||||
```
|
||||
|
||||
### Comentarios
|
||||
|
||||
Los comentarios pueden ser agregados en tu archivo o en la misma línea:
|
||||
|
||||
```dosini
|
||||
# This is a comment
|
||||
SECRET_KEY=YOURSECRETKEYGOESHERE # comment
|
||||
SECRET_HASH="something-with-a-#-hash"
|
||||
```
|
||||
|
||||
Los comentarios comienzan donde existe un `#`, entonces, si su valor contiene un `#`, enciérrelo entre comillas. Este es un cambio importante desde la versión `>= v15.0.0` en adelante.
|
||||
|
||||
### Análisis
|
||||
|
||||
El motor que analiza el contenido de su archivo que contiene variables de entorno está disponible para su uso. Este Acepta una Cadena o un Búfer y devolverá un Objeto con las claves y los valores analizados.
|
||||
|
||||
```javascript
|
||||
const dotenv = require('dotenv')
|
||||
const buf = Buffer.from('BASICO=basico')
|
||||
const config = dotenv.parse(buf) // devolverá un objeto
|
||||
console.log(typeof config, config) // objeto { BASICO : 'basico' }
|
||||
```
|
||||
|
||||
### Precarga
|
||||
|
||||
Puede usar el `--require` (`-r`) [opción de línea de comando](https://nodejs.org/api/cli.html#-r---require-module) para precargar dotenv. Al hacer esto, no necesita requerir ni cargar dotnev en el código de su aplicación.
|
||||
|
||||
```bash
|
||||
$ node -r dotenv/config tu_script.js
|
||||
```
|
||||
|
||||
Las opciones de configuración a continuación se admiten como argumentos de línea de comandos en el formato `dotenv_config_<option>=value`
|
||||
|
||||
```bash
|
||||
$ node -r dotenv/config tu_script.js dotenv_config_path=/custom/path/to/.env dotenv_config_debug=true
|
||||
```
|
||||
|
||||
Además, puede usar variables de entorno para establecer opciones de configuración. Los argumentos de línea de comandos precederán a estos.
|
||||
|
||||
```bash
|
||||
$ DOTENV_CONFIG_<OPTION>=value node -r dotenv/config tu_script.js
|
||||
```
|
||||
|
||||
```bash
|
||||
$ DOTENV_CONFIG_ENCODING=latin1 DOTENV_CONFIG_DEBUG=true node -r dotenv/config tu_script.js dotenv_config_path=/custom/path/to/.env
|
||||
```
|
||||
|
||||
### Expansión Variable
|
||||
|
||||
Necesitaras agregar el valor de otro variable en una de sus variables? Usa [dotenv-expand](https://github.com/motdotla/dotenv-expand).
|
||||
|
||||
### Sincronizando
|
||||
|
||||
Necesitas mentener sincronizados los archivos `.env` entre maquinas, entornos, o miembros del equipo? Usa
|
||||
[dotenv-vault](https://github.com/dotenv-org/dotenv-vault).
|
||||
|
||||
## Ejemplos
|
||||
|
||||
Vea [ejemplos](https://github.com/dotenv-org/examples) sobre el uso de dotenv con varios frameworks, lenguajes y configuraciones.
|
||||
|
||||
* [nodejs](https://github.com/dotenv-org/examples/tree/master/dotenv-nodejs)
|
||||
* [nodejs (depurar en)](https://github.com/dotenv-org/examples/tree/master/dotenv-nodejs-debug)
|
||||
* [nodejs (anular en)](https://github.com/dotenv-org/examples/tree/master/dotenv-nodejs-override)
|
||||
* [esm](https://github.com/dotenv-org/examples/tree/master/dotenv-esm)
|
||||
* [esm (precarga)](https://github.com/dotenv-org/examples/tree/master/dotenv-esm-preload)
|
||||
* [typescript](https://github.com/dotenv-org/examples/tree/master/dotenv-typescript)
|
||||
* [typescript parse](https://github.com/dotenv-org/examples/tree/master/dotenv-typescript-parse)
|
||||
* [typescript config](https://github.com/dotenv-org/examples/tree/master/dotenv-typescript-config)
|
||||
* [webpack](https://github.com/dotenv-org/examples/tree/master/dotenv-webpack)
|
||||
* [webpack (plugin)](https://github.com/dotenv-org/examples/tree/master/dotenv-webpack2)
|
||||
* [react](https://github.com/dotenv-org/examples/tree/master/dotenv-react)
|
||||
* [react (typescript)](https://github.com/dotenv-org/examples/tree/master/dotenv-react-typescript)
|
||||
* [express](https://github.com/dotenv-org/examples/tree/master/dotenv-express)
|
||||
* [nestjs](https://github.com/dotenv-org/examples/tree/master/dotenv-nestjs)
|
||||
|
||||
## Documentación
|
||||
|
||||
Dotenv expone dos funciones:
|
||||
|
||||
* `configuración`
|
||||
* `analizar`
|
||||
|
||||
### Configuración
|
||||
|
||||
`Configuración` leerá su archivo `.env`, analizará el contenido, lo asignará a [`process.env`](https://nodejs.org/docs/latest/api/process.html#process_process_env),
|
||||
y devolverá un Objeto con una clave `parsed` que contiene el contenido cargado o una clave `error` si falla.
|
||||
|
||||
```js
|
||||
const result = dotenv.config()
|
||||
|
||||
if (result.error) {
|
||||
throw result.error
|
||||
}
|
||||
|
||||
console.log(result.parsed)
|
||||
```
|
||||
|
||||
Adicionalmente, puede pasar opciones a `configuracion`.
|
||||
|
||||
#### Opciones
|
||||
|
||||
##### Ruta
|
||||
|
||||
Por defecto: `path.resolve(process.cwd(), '.env')`
|
||||
|
||||
Especifique una ruta personalizada si el archivo que contiene las variables de entorno se encuentra localizado en otro lugar.
|
||||
|
||||
```js
|
||||
require('dotenv').config({ path: '/personalizado/ruta/a/.env' })
|
||||
```
|
||||
|
||||
##### Codificación
|
||||
|
||||
Por defecto: `utf8`
|
||||
|
||||
Especifique la codificación del archivo que contiene las variables de entorno.
|
||||
|
||||
```js
|
||||
require('dotenv').config({ encoding: 'latin1' })
|
||||
```
|
||||
|
||||
##### Depurar
|
||||
|
||||
Por defecto: `false`
|
||||
|
||||
Active el registro de ayuda para depurar por qué ciertas claves o valores no se inician como lo esperabas.
|
||||
|
||||
```js
|
||||
require('dotenv').config({ debug: process.env.DEBUG })
|
||||
```
|
||||
|
||||
##### Anular
|
||||
|
||||
Por defecto: `false`
|
||||
|
||||
Anule cualquier variable de entorno que ya se haya configurada en su maquina con los valores de su archivo .env.
|
||||
|
||||
```js
|
||||
require('dotenv').config({ override: true })
|
||||
```
|
||||
|
||||
### Analizar
|
||||
|
||||
El motor que analiza el contenido del archivo que contiene las variables de entorno está disponible para su uso. Acepta una Cadena o un Búfer y retornará un objecto con los valores analizados.
|
||||
|
||||
```js
|
||||
const dotenv = require('dotenv')
|
||||
const buf = Buffer.from('BASICO=basico')
|
||||
const config = dotenv.parse(buf) // devolverá un objeto
|
||||
console.log(typeof config, config) // objeto { BASICO : 'basico' }
|
||||
```
|
||||
|
||||
#### Opciones
|
||||
|
||||
##### Depurar
|
||||
|
||||
Por defecto: `false`
|
||||
|
||||
Active el registro de ayuda para depurar por qué ciertas claves o valores no se inician como lo esperabas.
|
||||
|
||||
```js
|
||||
const dotenv = require('dotenv')
|
||||
const buf = Buffer.from('hola mundo')
|
||||
const opt = { debug: true }
|
||||
const config = dotenv.parse(buf, opt)
|
||||
// espere por un mensaje de depuración porque el búfer no esta listo KEY=VAL
|
||||
```
|
||||
|
||||
## FAQ
|
||||
|
||||
### ¿Por qué el archivo `.env` no carga mis variables de entorno correctamente?
|
||||
|
||||
Lo más probable es que su archivo `.env` no esté en el lugar correcto. [Vea este stack overflow](https://stackoverflow.com/questions/42335016/dotenv-file-is-not-loading-environment-variables).
|
||||
|
||||
Active el modo de depuración y vuelva a intentarlo...
|
||||
|
||||
```js
|
||||
require('dotenv').config({ debug: true })
|
||||
```
|
||||
|
||||
Recibirá un error apropiado en su consola.
|
||||
|
||||
### ¿Debo confirmar mi archivo `.env`?
|
||||
|
||||
No. Recomendamos **enfáticamente** no enviar su archivo `.env` a la versión de control. Solo debe incluir los valores especificos del entorno, como la base de datos, contraseñas o claves API.
|
||||
|
||||
### ¿Debería tener multiples archivos `.env`?
|
||||
|
||||
No. Recomendamos **enfáticamente** no tener un archivo `.env` "principal" y un archivo `.env` de "entorno" como `.env.test`. Su configuración debe variar entre implementaciones y no debe compartir valores entre entornos.
|
||||
|
||||
> En una Aplicación de Doce Factores, las variables de entorno son controles diferenciados, cada uno totalmente independiente a otras variables de entorno. Nunca se agrupan como "entornos", sino que se gestionan de manera independiente para cada despliegue. Este es un modelo que se escala sin problemas a medida que la aplicación se expande de forma natural en más despliegues a lo largo de su vida.
|
||||
>
|
||||
> – [La Apliación de los Doce Factores](https://12factor.net/es/)
|
||||
|
||||
### ¿Qué reglas sigue el motor de análisis?
|
||||
|
||||
El motor de análisis actualmente admite las siguientes reglas:
|
||||
|
||||
- `BASICO=basico` se convierte en `{BASICO: 'basico'}`
|
||||
- las líneas vacías se saltan
|
||||
- las líneas que comienzan con `#` se tratan como comentarios
|
||||
- `#` marca el comienzo de un comentario (a menos que el valor esté entre comillas)
|
||||
- valores vacíos se convierten en cadenas vacías (`VACIO=` se convierte en `{VACIO: ''}`)
|
||||
- las comillas internas se mantienen (piensa en JSON) (`JSON={"foo": "bar"}` se convierte en `{JSON:"{\"foo\": \"bar\"}"`)
|
||||
- los espacios en blanco se eliminan de ambos extremos de los valores no citanos (aprende más en [`trim`](https://developer.mozilla.org/es/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)) (`FOO= algo ` se convierte en `{FOO: 'algo'}`)
|
||||
- los valores entre comillas simples y dobles se escapan (`CITA_SIMPLE='citado'` se convierte en `{CITA_SIMPLE: "citado"}`)
|
||||
- los valores entre comillas simples y dobles mantienen los espacios en blanco en ambos extremos (`FOO=" algo "` se convierte en `{FOO: ' algo '}`)
|
||||
- los valores entre comillas dobles expanden nuevas líneas (`MULTILINEA="nueva\nlínea"` se convierte en
|
||||
|
||||
```
|
||||
{MULTILINEA: 'nueva
|
||||
línea'}
|
||||
```
|
||||
|
||||
- se admite la comilla simple invertida (`` SIGNO_ACENTO=`Esto tiene comillas 'simples' y "dobles" en su interior.` ``)
|
||||
|
||||
### ¿Qué sucede con las variables de entorno que ya estaban configuradas?
|
||||
|
||||
Por defecto, nunca modificaremos ninguna variable de entorno que ya haya sido establecida. En particular, si hay una variable en su archivo `.env` que colisiona con una que ya existe en su entorno, entonces esa variable se omitirá.
|
||||
|
||||
Si por el contrario, quieres anular `process.env` utiliza la opción `override`.
|
||||
|
||||
```javascript
|
||||
require('dotenv').config({ override: true })
|
||||
```
|
||||
|
||||
### ¿Por qué mis variables de entorno no aparecen para React?
|
||||
|
||||
Su código React se ejecuta en Webpack, donde el módulo `fs` o incluso el propio `process` global no son accesibles fuera-de-la-caja. El módulo `process.env` sólo puede ser inyectado a través de la configuración de Webpack.
|
||||
|
||||
Si estás usando [`react-scripts`](https://www.npmjs.com/package/react-scripts), el cual se distribuye a través de [`create-react-app`](https://create-react-app.dev/), tiene dotenv incorporado pero con una singularidad. Escriba sus variables de entorno con `REACT_APP_`. Vea [este stack overflow](https://stackoverflow.com/questions/42182577/is-it-possible-to-use-dotenv-in-a-react-project) para más detalles.
|
||||
|
||||
Si estás utilizando otros frameworks (por ejemplo, Next.js, Gatsby...), debes consultar su documentación para saber cómo injectar variables de entorno en el cliente.
|
||||
|
||||
### ¿Puedo personalizar/escribir plugins para dotenv?
|
||||
|
||||
Sí! `dotenv.config()` devuelve un objeto que representa el archivo `.env` analizado. Esto te da todo lo que necesitas para poder establecer valores en `process.env`. Por ejemplo:
|
||||
|
||||
```js
|
||||
const dotenv = require('dotenv')
|
||||
const variableExpansion = require('dotenv-expand')
|
||||
const miEnv = dotenv.config()
|
||||
variableExpansion(miEnv)
|
||||
```
|
||||
|
||||
### Cómo uso dotnev con `import`?
|
||||
|
||||
Simplemente..
|
||||
|
||||
```javascript
|
||||
// index.mjs (ESM)
|
||||
import * as dotenv from 'dotenv' // vea https://github.com/motdotla/dotenv#como-uso-dotenv-con-import
|
||||
dotenv.config()
|
||||
import express from 'express'
|
||||
```
|
||||
|
||||
Un poco de historia...
|
||||
|
||||
> Cuando se ejecuta un módulo que contiene una sentencia `import`, los módulos que importa serán cargados primero, y luego se ejecuta cada bloque del módulo en un recorrido en profundidad del gráfico de dependencias, evitando los ciclos al saltarse todo lo que ya se ha ejecutado.
|
||||
>
|
||||
> – [ES6 en Profundidad: Módulos](https://hacks.mozilla.org/2015/08/es6-in-depth-modules/)
|
||||
|
||||
¿Qué significa esto en lenguaje sencillo? Significa que se podrías pensar que lo siguiente funcionaría pero no lo hará.
|
||||
|
||||
```js
|
||||
// notificarError.mjs
|
||||
import { Cliente } from 'mejor-servicio-para-notificar-error'
|
||||
|
||||
export default new Client(process.env.CLAVE_API)
|
||||
|
||||
// index.mjs
|
||||
import dotenv from 'dotenv'
|
||||
dotenv.config()
|
||||
|
||||
import notificarError from './notificarError.mjs'
|
||||
notificarError.report(new Error('ejemplo documentado'))
|
||||
```
|
||||
|
||||
`process.env.CLAVE_API` será vacio.
|
||||
|
||||
En su lugar, el código anterior debe ser escrito como...
|
||||
|
||||
```js
|
||||
// notificarError.mjs
|
||||
import { Cliente } from 'mejor-servicio-para-notificar-errores'
|
||||
|
||||
export default new Client(process.env.CLAVE_API)
|
||||
|
||||
// index.mjs
|
||||
import * as dotenv from 'dotenv'
|
||||
dotenv.config()
|
||||
|
||||
import notificarError from './notificarError.mjs'
|
||||
notificarError.report(new Error('ejemplo documentado'))
|
||||
```
|
||||
|
||||
¿Esto tiene algo de sentido? Esto es poco poco intuitivo, pero es como funciona la importación de módulos en ES6. Aquí hay un ejemplo [ejemplo práctico de esta trampa](https://github.com/dotenv-org/examples/tree/master/dotenv-es6-import-pitfall).
|
||||
|
||||
Existen dos arternativas a este planteamiento:
|
||||
|
||||
1. Precarga dotenv: `node --require dotenv/config index.js` (_Nota: no es necesario usar `import` dotenv con este método_)
|
||||
2. Cree un archivo separado que ejecutará `config` primero como se describe en [este comentario #133](https://github.com/motdotla/dotenv/issues/133#issuecomment-255298822)
|
||||
|
||||
### ¿Qué pasa con la expansión de variable?
|
||||
|
||||
Prueba [dotenv-expand](https://github.com/motdotla/dotenv-expand)
|
||||
|
||||
### ¿Qué pasa con la sincronización y la seguridad de los archivos .env?
|
||||
|
||||
Vea [dotenv-vault](https://github.com/dotenv-org/dotenv-vault)
|
||||
|
||||
## Guía de contribución
|
||||
|
||||
Vea [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||
|
||||
## REGISTRO DE CAMBIOS
|
||||
|
||||
Vea [CHANGELOG.md](CHANGELOG.md)
|
||||
|
||||
## ¿Quiénes utilizan dotenv?
|
||||
|
||||
[Estos módulos npm dependen de él.](https://www.npmjs.com/browse/depended/dotenv)
|
||||
|
||||
Los proyectos que lo amplían suelen utilizar la [palabra clave "dotenv" en npm](https://www.npmjs.com/search?q=keywords:dotenv).
|
|
@ -0,0 +1,633 @@
|
|||
<div align="center">
|
||||
|
||||
<p>
|
||||
<sup>
|
||||
<a href="https://github.com/sponsors/motdotla">Dotenv is supported by the community.</a>
|
||||
</sup>
|
||||
</p>
|
||||
<sup>Special thanks to:</sup>
|
||||
<br>
|
||||
<br>
|
||||
<a href="https://www.warp.dev/?utm_source=github&utm_medium=referral&utm_campaign=dotenv_p_20220831">
|
||||
<div>
|
||||
<img src="https://res.cloudinary.com/dotenv-org/image/upload/v1661980709/warp_hi8oqj.png" width="230" alt="Warp">
|
||||
</div>
|
||||
<b>Warp is a blazingly fast, Rust-based terminal reimagined to work like a modern app.</b>
|
||||
<div>
|
||||
<sup>Get more done in the CLI with real text editing, block-based output, and AI command search.</sup>
|
||||
</div>
|
||||
</a>
|
||||
<br>
|
||||
<a href="https://retool.com/?utm_source=sponsor&utm_campaign=dotenv">
|
||||
<div>
|
||||
<img src="https://res.cloudinary.com/dotenv-org/image/upload/c_scale,w_300/v1664466968/logo-full-black_vidfqf.png" width="270" alt="Retool">
|
||||
</div>
|
||||
<b>Retool helps developers build custom internal software, like CRUD apps and admin panels, really fast.</b>
|
||||
<div>
|
||||
<sup>Build UIs visually with flexible components, connect to any data source, and write business logic in JavaScript.</sup>
|
||||
</div>
|
||||
</a>
|
||||
<br>
|
||||
<a href="https://workos.com/?utm_campaign=github_repo&utm_medium=referral&utm_content=dotenv&utm_source=github">
|
||||
<div>
|
||||
<img src="https://res.cloudinary.com/dotenv-org/image/upload/c_scale,w_400/v1665605496/68747470733a2f2f73696e647265736f726875732e636f6d2f6173736574732f7468616e6b732f776f726b6f732d6c6f676f2d77686974652d62672e737667_zdmsbu.svg" width="270" alt="WorkOS">
|
||||
</div>
|
||||
<b>Your App, Enterprise Ready.</b>
|
||||
<div>
|
||||
<sup>Add Single Sign-On, Multi-Factor Auth, and more, in minutes instead of months.</sup>
|
||||
</div>
|
||||
</a>
|
||||
<hr>
|
||||
</div>
|
||||
|
||||
# dotenv [![NPM version](https://img.shields.io/npm/v/dotenv.svg?style=flat-square)](https://www.npmjs.com/package/dotenv)
|
||||
|
||||
<img src="https://raw.githubusercontent.com/motdotla/dotenv/master/dotenv.svg" alt="dotenv" align="right" width="200" />
|
||||
|
||||
Dotenv is a zero-dependency module that loads environment variables from a `.env` file into [`process.env`](https://nodejs.org/docs/latest/api/process.html#process_process_env). Storing configuration in the environment separate from code is based on [The Twelve-Factor App](http://12factor.net/config) methodology.
|
||||
|
||||
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square)](https://github.com/feross/standard)
|
||||
[![LICENSE](https://img.shields.io/github/license/motdotla/dotenv.svg)](LICENSE)
|
||||
[![dotenv-vault](https://badge.dotenv.org/works-with.svg?r=1)](https://www.dotenv.org/r/github.com/dotenv-org/dotenv-vault?r=1)
|
||||
|
||||
* [🌱 Install](#-install)
|
||||
* [🏗️ Usage (.env)](#%EF%B8%8F-usage)
|
||||
* [🚀 Deploying (.env.vault) 🆕](#-deploying)
|
||||
* [🌴 Multiple Environments 🆕](#-manage-multiple-environments)
|
||||
* [📚 Examples](#-examples)
|
||||
* [📖 Docs](#-documentation)
|
||||
* [❓ FAQ](#-faq)
|
||||
* [⏱️ Changelog](./CHANGELOG.md)
|
||||
|
||||
## 🌱 Install
|
||||
|
||||
```bash
|
||||
# install locally (recommended)
|
||||
npm install dotenv --save
|
||||
```
|
||||
|
||||
Or installing with yarn? `yarn add dotenv`
|
||||
|
||||
## 🏗️ Usage
|
||||
|
||||
<a href="https://www.youtube.com/watch?v=YtkZR0NFd1g">
|
||||
<div align="right">
|
||||
<img src="https://img.youtube.com/vi/YtkZR0NFd1g/hqdefault.jpg" alt="how to use dotenv video tutorial" align="right" width="330" />
|
||||
<img src="https://simpleicons.vercel.app/youtube/ff0000" alt="youtube/@dotenvorg" align="right" width="24" />
|
||||
</div>
|
||||
</a>
|
||||
|
||||
Create a `.env` file in the root of your project:
|
||||
|
||||
```dosini
|
||||
S3_BUCKET="YOURS3BUCKET"
|
||||
SECRET_KEY="YOURSECRETKEYGOESHERE"
|
||||
```
|
||||
|
||||
As early as possible in your application, import and configure dotenv:
|
||||
|
||||
```javascript
|
||||
require('dotenv').config()
|
||||
console.log(process.env) // remove this after you've confirmed it is working
|
||||
```
|
||||
|
||||
.. [or using ES6?](#how-do-i-use-dotenv-with-import)
|
||||
|
||||
```javascript
|
||||
import 'dotenv/config'
|
||||
```
|
||||
|
||||
That's it. `process.env` now has the keys and values you defined in your `.env` file:
|
||||
|
||||
```javascript
|
||||
require('dotenv').config()
|
||||
|
||||
...
|
||||
|
||||
s3.getBucketCors({Bucket: process.env.S3_BUCKET}, function(err, data) {})
|
||||
```
|
||||
|
||||
### Multiline values
|
||||
|
||||
If you need multiline variables, for example private keys, those are now supported (`>= v15.0.0`) with line breaks:
|
||||
|
||||
```dosini
|
||||
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----
|
||||
...
|
||||
Kh9NV...
|
||||
...
|
||||
-----END RSA PRIVATE KEY-----"
|
||||
```
|
||||
|
||||
Alternatively, you can double quote strings and use the `\n` character:
|
||||
|
||||
```dosini
|
||||
PRIVATE_KEY="-----BEGIN RSA PRIVATE KEY-----\nKh9NV...\n-----END RSA PRIVATE KEY-----\n"
|
||||
```
|
||||
|
||||
### Comments
|
||||
|
||||
Comments may be added to your file on their own line or inline:
|
||||
|
||||
```dosini
|
||||
# This is a comment
|
||||
SECRET_KEY=YOURSECRETKEYGOESHERE # comment
|
||||
SECRET_HASH="something-with-a-#-hash"
|
||||
```
|
||||
|
||||
Comments begin where a `#` exists, so if your value contains a `#` please wrap it in quotes. This is a breaking change from `>= v15.0.0` and on.
|
||||
|
||||
### Parsing
|
||||
|
||||
The engine which parses the contents of your file containing environment variables is available to use. It accepts a String or Buffer and will return an Object with the parsed keys and values.
|
||||
|
||||
```javascript
|
||||
const dotenv = require('dotenv')
|
||||
const buf = Buffer.from('BASIC=basic')
|
||||
const config = dotenv.parse(buf) // will return an object
|
||||
console.log(typeof config, config) // object { BASIC : 'basic' }
|
||||
```
|
||||
|
||||
### Preload
|
||||
|
||||
You can use the `--require` (`-r`) [command line option](https://nodejs.org/api/cli.html#-r---require-module) to preload dotenv. By doing this, you do not need to require and load dotenv in your application code.
|
||||
|
||||
```bash
|
||||
$ node -r dotenv/config your_script.js
|
||||
```
|
||||
|
||||
The configuration options below are supported as command line arguments in the format `dotenv_config_<option>=value`
|
||||
|
||||
```bash
|
||||
$ node -r dotenv/config your_script.js dotenv_config_path=/custom/path/to/.env dotenv_config_debug=true
|
||||
```
|
||||
|
||||
Additionally, you can use environment variables to set configuration options. Command line arguments will precede these.
|
||||
|
||||
```bash
|
||||
$ DOTENV_CONFIG_<OPTION>=value node -r dotenv/config your_script.js
|
||||
```
|
||||
|
||||
```bash
|
||||
$ DOTENV_CONFIG_ENCODING=latin1 DOTENV_CONFIG_DEBUG=true node -r dotenv/config your_script.js dotenv_config_path=/custom/path/to/.env
|
||||
```
|
||||
|
||||
### Variable Expansion
|
||||
|
||||
You need to add the value of another variable in one of your variables? Use [dotenv-expand](https://github.com/motdotla/dotenv-expand).
|
||||
|
||||
### Syncing
|
||||
|
||||
You need to keep `.env` files in sync between machines, environments, or team members? Use [dotenv-vault](https://github.com/dotenv-org/dotenv-vault).
|
||||
|
||||
### Deploying
|
||||
|
||||
You need to deploy your secrets in a cloud-agnostic manner? Use a `.env.vault` file.
|
||||
|
||||
### Multiple Environments
|
||||
|
||||
You need to manage your secrets across different environments and apply them as needed? Use a `.env.vault` file with a `DOTENV_KEY`.
|
||||
|
||||
## 🚀 Deploying
|
||||
|
||||
*Note: Requires dotenv >= 16.1.0*
|
||||
|
||||
Encrypt your `.env.vault` file.
|
||||
|
||||
```bash
|
||||
$ npx dotenv-vault build
|
||||
```
|
||||
|
||||
Fetch your production `DOTENV_KEY`.
|
||||
|
||||
```bash
|
||||
$ npx dotenv-vault keys production
|
||||
```
|
||||
|
||||
Set `DOTENV_KEY` on your server.
|
||||
|
||||
```bash
|
||||
# heroku example
|
||||
heroku config:set DOTENV_KEY=dotenv://:key_1234…@dotenv.org/vault/.env.vault?environment=production
|
||||
```
|
||||
|
||||
That's it! On deploy, your `.env.vault` file will be decrypted and its secrets injected as environment variables – just in time.
|
||||
|
||||
*ℹ️ A note from [Mot](https://github.com/motdotla): Until recently, we did not have an opinion on how and where to store your secrets in production. We now strongly recommend generating a `.env.vault` file. It's the best way to prevent your secrets from being scattered across multiple servers and cloud providers – protecting you from breaches like the [CircleCI breach](https://techcrunch.com/2023/01/05/circleci-breach/). Also it unlocks interoperability WITHOUT native third-party integrations. Third-party integrations are [increasingly risky](https://coderpad.io/blog/development/heroku-github-breach/) to our industry. They may be the 'du jour' of today, but we imagine a better future.*
|
||||
|
||||
<a href="https://github.com/dotenv-org/dotenv-vault#dotenv-vault-">Learn more at dotenv-vault: Deploying</a>
|
||||
|
||||
## 🌴 Manage Multiple Environments
|
||||
|
||||
Edit your production environment variables.
|
||||
|
||||
```bash
|
||||
$ npx dotenv-vault open production
|
||||
```
|
||||
|
||||
Regenerate your `.env.vault` file.
|
||||
|
||||
```bash
|
||||
$ npx dotenv-vault build
|
||||
```
|
||||
|
||||
*ℹ️ 🔐 Vault Managed vs 💻 Locally Managed: The above example, for brevity's sake, used the 🔐 Vault Managed solution to manage your `.env.vault` file. You can instead use the 💻 Locally Managed solution. [Read more here](https://github.com/dotenv-org/dotenv-vault#how-do-i-use--locally-managed-dotenv-vault). Our vision is that other platforms and orchestration tools adopt the `.env.vault` standard as they did the `.env` standard. We don't expect to be the only ones providing tooling to manage and generate `.env.vault` files.*
|
||||
|
||||
<a href="https://github.com/dotenv-org/dotenv-vault#-manage-multiple-environments">Learn more at dotenv-vault: Manage Multiple Environments</a>
|
||||
|
||||
## 📚 Examples
|
||||
|
||||
See [examples](https://github.com/dotenv-org/examples) of using dotenv with various frameworks, languages, and configurations.
|
||||
|
||||
* [nodejs](https://github.com/dotenv-org/examples/tree/master/dotenv-nodejs)
|
||||
* [nodejs (debug on)](https://github.com/dotenv-org/examples/tree/master/dotenv-nodejs-debug)
|
||||
* [nodejs (override on)](https://github.com/dotenv-org/examples/tree/master/dotenv-nodejs-override)
|
||||
* [nodejs (processEnv override)](https://github.com/dotenv-org/examples/tree/master/dotenv-custom-target)
|
||||
* [nodejs (DOTENV_KEY override)](https://github.com/dotenv-org/examples/tree/master/dotenv-vault-custom-target)
|
||||
* [esm](https://github.com/dotenv-org/examples/tree/master/dotenv-esm)
|
||||
* [esm (preload)](https://github.com/dotenv-org/examples/tree/master/dotenv-esm-preload)
|
||||
* [typescript](https://github.com/dotenv-org/examples/tree/master/dotenv-typescript)
|
||||
* [typescript parse](https://github.com/dotenv-org/examples/tree/master/dotenv-typescript-parse)
|
||||
* [typescript config](https://github.com/dotenv-org/examples/tree/master/dotenv-typescript-config)
|
||||
* [webpack](https://github.com/dotenv-org/examples/tree/master/dotenv-webpack)
|
||||
* [webpack (plugin)](https://github.com/dotenv-org/examples/tree/master/dotenv-webpack2)
|
||||
* [react](https://github.com/dotenv-org/examples/tree/master/dotenv-react)
|
||||
* [react (typescript)](https://github.com/dotenv-org/examples/tree/master/dotenv-react-typescript)
|
||||
* [express](https://github.com/dotenv-org/examples/tree/master/dotenv-express)
|
||||
* [nestjs](https://github.com/dotenv-org/examples/tree/master/dotenv-nestjs)
|
||||
* [fastify](https://github.com/dotenv-org/examples/tree/master/dotenv-fastify)
|
||||
|
||||
## 📖 Documentation
|
||||
|
||||
Dotenv exposes four functions:
|
||||
|
||||
* `config`
|
||||
* `parse`
|
||||
* `populate`
|
||||
* `decrypt`
|
||||
|
||||
### Config
|
||||
|
||||
`config` will read your `.env` file, parse the contents, assign it to
|
||||
[`process.env`](https://nodejs.org/docs/latest/api/process.html#process_process_env),
|
||||
and return an Object with a `parsed` key containing the loaded content or an `error` key if it failed.
|
||||
|
||||
```js
|
||||
const result = dotenv.config()
|
||||
|
||||
if (result.error) {
|
||||
throw result.error
|
||||
}
|
||||
|
||||
console.log(result.parsed)
|
||||
```
|
||||
|
||||
You can additionally, pass options to `config`.
|
||||
|
||||
#### Options
|
||||
|
||||
##### path
|
||||
|
||||
Default: `path.resolve(process.cwd(), '.env')`
|
||||
|
||||
Specify a custom path if your file containing environment variables is located elsewhere.
|
||||
|
||||
```js
|
||||
require('dotenv').config({ path: '/custom/path/to/.env' })
|
||||
```
|
||||
|
||||
##### encoding
|
||||
|
||||
Default: `utf8`
|
||||
|
||||
Specify the encoding of your file containing environment variables.
|
||||
|
||||
```js
|
||||
require('dotenv').config({ encoding: 'latin1' })
|
||||
```
|
||||
|
||||
##### debug
|
||||
|
||||
Default: `false`
|
||||
|
||||
Turn on logging to help debug why certain keys or values are not being set as you expect.
|
||||
|
||||
```js
|
||||
require('dotenv').config({ debug: process.env.DEBUG })
|
||||
```
|
||||
|
||||
##### override
|
||||
|
||||
Default: `false`
|
||||
|
||||
Override any environment variables that have already been set on your machine with values from your .env file.
|
||||
|
||||
```js
|
||||
require('dotenv').config({ override: true })
|
||||
```
|
||||
|
||||
##### processEnv
|
||||
|
||||
Default: `process.env`
|
||||
|
||||
Specify an object to write your secrets to. Defaults to `process.env` environment variables.
|
||||
|
||||
```js
|
||||
const myObject = {}
|
||||
require('dotenv').config({ processEnv: myObject })
|
||||
|
||||
console.log(myObject) // values from .env or .env.vault live here now.
|
||||
console.log(process.env) // this was not changed or written to
|
||||
```
|
||||
|
||||
##### DOTENV_KEY
|
||||
|
||||
Default: `process.env.DOTENV_KEY`
|
||||
|
||||
Pass the `DOTENV_KEY` directly to config options. Defaults to looking for `process.env.DOTENV_KEY` environment variable. Note this only applies to decrypting `.env.vault` files. If passed as null or undefined, or not passed at all, dotenv falls back to its traditional job of parsing a `.env` file.
|
||||
|
||||
```js
|
||||
require('dotenv').config({ DOTENV_KEY: 'dotenv://:key_1234…@dotenv.org/vault/.env.vault?environment=production' })
|
||||
```
|
||||
|
||||
### Parse
|
||||
|
||||
The engine which parses the contents of your file containing environment
|
||||
variables is available to use. It accepts a String or Buffer and will return
|
||||
an Object with the parsed keys and values.
|
||||
|
||||
```js
|
||||
const dotenv = require('dotenv')
|
||||
const buf = Buffer.from('BASIC=basic')
|
||||
const config = dotenv.parse(buf) // will return an object
|
||||
console.log(typeof config, config) // object { BASIC : 'basic' }
|
||||
```
|
||||
|
||||
#### Options
|
||||
|
||||
##### debug
|
||||
|
||||
Default: `false`
|
||||
|
||||
Turn on logging to help debug why certain keys or values are not being set as you expect.
|
||||
|
||||
```js
|
||||
const dotenv = require('dotenv')
|
||||
const buf = Buffer.from('hello world')
|
||||
const opt = { debug: true }
|
||||
const config = dotenv.parse(buf, opt)
|
||||
// expect a debug message because the buffer is not in KEY=VAL form
|
||||
```
|
||||
|
||||
### Populate
|
||||
|
||||
The engine which populates the contents of your .env file to `process.env` is available for use. It accepts a target, a source, and options. This is useful for power users who want to supply their own objects.
|
||||
|
||||
For example, customizing the source:
|
||||
|
||||
```js
|
||||
const dotenv = require('dotenv')
|
||||
const parsed = { HELLO: 'world' }
|
||||
|
||||
dotenv.populate(process.env, parsed)
|
||||
|
||||
console.log(process.env.HELLO) // world
|
||||
```
|
||||
|
||||
For example, customizing the source AND target:
|
||||
|
||||
```js
|
||||
const dotenv = require('dotenv')
|
||||
const parsed = { HELLO: 'universe' }
|
||||
const target = { HELLO: 'world' } // empty object
|
||||
|
||||
dotenv.populate(target, parsed, { override: true, debug: true })
|
||||
|
||||
console.log(target) // { HELLO: 'universe' }
|
||||
```
|
||||
|
||||
#### options
|
||||
|
||||
##### Debug
|
||||
|
||||
Default: `false`
|
||||
|
||||
Turn on logging to help debug why certain keys or values are not being populated as you expect.
|
||||
|
||||
##### override
|
||||
|
||||
Default: `false`
|
||||
|
||||
Override any environment variables that have already been set.
|
||||
|
||||
### Decrypt
|
||||
|
||||
The engine which decrypts the ciphertext contents of your .env.vault file is available for use. It accepts a ciphertext and a decryption key. It uses AES-256-GCM encryption.
|
||||
|
||||
For example, decrypting a simple ciphertext:
|
||||
|
||||
```js
|
||||
const dotenv = require('dotenv')
|
||||
const ciphertext = 's7NYXa809k/bVSPwIAmJhPJmEGTtU0hG58hOZy7I0ix6y5HP8LsHBsZCYC/gw5DDFy5DgOcyd18R'
|
||||
const decryptionKey = 'ddcaa26504cd70a6fef9801901c3981538563a1767c297cb8416e8a38c62fe00'
|
||||
|
||||
const decrypted = dotenv.decrypt(ciphertext, decryptionKey)
|
||||
|
||||
console.log(decrypted) // # development@v6\nALPHA="zeta"
|
||||
```
|
||||
|
||||
## ❓ FAQ
|
||||
|
||||
### Why is the `.env` file not loading my environment variables successfully?
|
||||
|
||||
Most likely your `.env` file is not in the correct place. [See this stack overflow](https://stackoverflow.com/questions/42335016/dotenv-file-is-not-loading-environment-variables).
|
||||
|
||||
Turn on debug mode and try again..
|
||||
|
||||
```js
|
||||
require('dotenv').config({ debug: true })
|
||||
```
|
||||
|
||||
You will receive a helpful error outputted to your console.
|
||||
|
||||
### Should I commit my `.env` file?
|
||||
|
||||
No. We **strongly** recommend against committing your `.env` file to version
|
||||
control. It should only include environment-specific values such as database
|
||||
passwords or API keys. Your production database should have a different
|
||||
password than your development database.
|
||||
|
||||
### Should I have multiple `.env` files?
|
||||
|
||||
No. We **strongly** recommend against having a "main" `.env` file and an "environment" `.env` file like `.env.test`. Your config should vary between deploys, and you should not be sharing values between environments.
|
||||
|
||||
> In a twelve-factor app, env vars are granular controls, each fully orthogonal to other env vars. They are never grouped together as “environments”, but instead are independently managed for each deploy. This is a model that scales up smoothly as the app naturally expands into more deploys over its lifetime.
|
||||
>
|
||||
> – [The Twelve-Factor App](http://12factor.net/config)
|
||||
|
||||
### What rules does the parsing engine follow?
|
||||
|
||||
The parsing engine currently supports the following rules:
|
||||
|
||||
- `BASIC=basic` becomes `{BASIC: 'basic'}`
|
||||
- empty lines are skipped
|
||||
- lines beginning with `#` are treated as comments
|
||||
- `#` marks the beginning of a comment (unless when the value is wrapped in quotes)
|
||||
- empty values become empty strings (`EMPTY=` becomes `{EMPTY: ''}`)
|
||||
- inner quotes are maintained (think JSON) (`JSON={"foo": "bar"}` becomes `{JSON:"{\"foo\": \"bar\"}"`)
|
||||
- whitespace is removed from both ends of unquoted values (see more on [`trim`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/Trim)) (`FOO= some value ` becomes `{FOO: 'some value'}`)
|
||||
- single and double quoted values are escaped (`SINGLE_QUOTE='quoted'` becomes `{SINGLE_QUOTE: "quoted"}`)
|
||||
- single and double quoted values maintain whitespace from both ends (`FOO=" some value "` becomes `{FOO: ' some value '}`)
|
||||
- double quoted values expand new lines (`MULTILINE="new\nline"` becomes
|
||||
|
||||
```
|
||||
{MULTILINE: 'new
|
||||
line'}
|
||||
```
|
||||
|
||||
- backticks are supported (`` BACKTICK_KEY=`This has 'single' and "double" quotes inside of it.` ``)
|
||||
|
||||
### What happens to environment variables that were already set?
|
||||
|
||||
By default, we will never modify any environment variables that have already been set. In particular, if there is a variable in your `.env` file which collides with one that already exists in your environment, then that variable will be skipped.
|
||||
|
||||
If instead, you want to override `process.env` use the `override` option.
|
||||
|
||||
```javascript
|
||||
require('dotenv').config({ override: true })
|
||||
```
|
||||
|
||||
### How come my environment variables are not showing up for React?
|
||||
|
||||
Your React code is run in Webpack, where the `fs` module or even the `process` global itself are not accessible out-of-the-box. `process.env` can only be injected through Webpack configuration.
|
||||
|
||||
If you are using [`react-scripts`](https://www.npmjs.com/package/react-scripts), which is distributed through [`create-react-app`](https://create-react-app.dev/), it has dotenv built in but with a quirk. Preface your environment variables with `REACT_APP_`. See [this stack overflow](https://stackoverflow.com/questions/42182577/is-it-possible-to-use-dotenv-in-a-react-project) for more details.
|
||||
|
||||
If you are using other frameworks (e.g. Next.js, Gatsby...), you need to consult their documentation for how to inject environment variables into the client.
|
||||
|
||||
### Can I customize/write plugins for dotenv?
|
||||
|
||||
Yes! `dotenv.config()` returns an object representing the parsed `.env` file. This gives you everything you need to continue setting values on `process.env`. For example:
|
||||
|
||||
```js
|
||||
const dotenv = require('dotenv')
|
||||
const variableExpansion = require('dotenv-expand')
|
||||
const myEnv = dotenv.config()
|
||||
variableExpansion(myEnv)
|
||||
```
|
||||
|
||||
### How do I use dotenv with `import`?
|
||||
|
||||
Simply..
|
||||
|
||||
```javascript
|
||||
// index.mjs (ESM)
|
||||
import 'dotenv/config' // see https://github.com/motdotla/dotenv#how-do-i-use-dotenv-with-import
|
||||
import express from 'express'
|
||||
```
|
||||
|
||||
A little background..
|
||||
|
||||
> When you run a module containing an `import` declaration, the modules it imports are loaded first, then each module body is executed in a depth-first traversal of the dependency graph, avoiding cycles by skipping anything already executed.
|
||||
>
|
||||
> – [ES6 In Depth: Modules](https://hacks.mozilla.org/2015/08/es6-in-depth-modules/)
|
||||
|
||||
What does this mean in plain language? It means you would think the following would work but it won't.
|
||||
|
||||
`errorReporter.mjs`:
|
||||
```js
|
||||
import { Client } from 'best-error-reporting-service'
|
||||
|
||||
export default new Client(process.env.API_KEY)
|
||||
```
|
||||
`index.mjs`:
|
||||
```js
|
||||
// Note: this is INCORRECT and will not work
|
||||
import * as dotenv from 'dotenv'
|
||||
dotenv.config()
|
||||
|
||||
import errorReporter from './errorReporter.mjs'
|
||||
errorReporter.report(new Error('documented example'))
|
||||
```
|
||||
|
||||
`process.env.API_KEY` will be blank.
|
||||
|
||||
Instead, `index.mjs` should be written as..
|
||||
|
||||
```js
|
||||
import 'dotenv/config'
|
||||
|
||||
import errorReporter from './errorReporter.mjs'
|
||||
errorReporter.report(new Error('documented example'))
|
||||
```
|
||||
|
||||
Does that make sense? It's a bit unintuitive, but it is how importing of ES6 modules work. Here is a [working example of this pitfall](https://github.com/dotenv-org/examples/tree/master/dotenv-es6-import-pitfall).
|
||||
|
||||
There are two alternatives to this approach:
|
||||
|
||||
1. Preload dotenv: `node --require dotenv/config index.js` (_Note: you do not need to `import` dotenv with this approach_)
|
||||
2. Create a separate file that will execute `config` first as outlined in [this comment on #133](https://github.com/motdotla/dotenv/issues/133#issuecomment-255298822)
|
||||
|
||||
### Why am I getting the error `Module not found: Error: Can't resolve 'crypto|os|path'`?
|
||||
|
||||
You are using dotenv on the front-end and have not included a polyfill. Webpack < 5 used to include these for you. Do the following:
|
||||
|
||||
```bash
|
||||
npm install node-polyfill-webpack-plugin
|
||||
```
|
||||
|
||||
Configure your `webpack.config.js` to something like the following.
|
||||
|
||||
```js
|
||||
require('dotenv').config()
|
||||
|
||||
const path = require('path');
|
||||
const webpack = require('webpack')
|
||||
|
||||
const NodePolyfillPlugin = require('node-polyfill-webpack-plugin')
|
||||
|
||||
module.exports = {
|
||||
mode: 'development',
|
||||
entry: './src/index.ts',
|
||||
output: {
|
||||
filename: 'bundle.js',
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
},
|
||||
plugins: [
|
||||
new NodePolyfillPlugin(),
|
||||
new webpack.DefinePlugin({
|
||||
'process.env': {
|
||||
HELLO: JSON.stringify(process.env.HELLO)
|
||||
}
|
||||
}),
|
||||
]
|
||||
};
|
||||
```
|
||||
|
||||
Alternatively, just use [dotenv-webpack](https://github.com/mrsteele/dotenv-webpack) which does this and more behind the scenes for you.
|
||||
|
||||
### What about variable expansion?
|
||||
|
||||
Try [dotenv-expand](https://github.com/motdotla/dotenv-expand)
|
||||
|
||||
### What about syncing and securing .env files?
|
||||
|
||||
Use [dotenv-vault](https://github.com/dotenv-org/dotenv-vault)
|
||||
|
||||
### What is a `.env.vault` file?
|
||||
|
||||
A `.env.vault` file is an encrypted version of your development (and ci, staging, production, etc) environment variables. It is paired with a `DOTENV_KEY` to deploy your secrets more securely than scattering them across multiple platforms and tools. Use [dotenv-vault](https://github.com/dotenv-org/dotenv-vault) to manage and generate them.
|
||||
|
||||
## Contributing Guide
|
||||
|
||||
See [CONTRIBUTING.md](CONTRIBUTING.md)
|
||||
|
||||
## CHANGELOG
|
||||
|
||||
See [CHANGELOG.md](CHANGELOG.md)
|
||||
|
||||
## Who's using dotenv?
|
||||
|
||||
[These npm modules depend on it.](https://www.npmjs.com/browse/depended/dotenv)
|
||||
|
||||
Projects that expand it often use the [keyword "dotenv" on npm](https://www.npmjs.com/search?q=keywords:dotenv).
|
|
@ -0,0 +1 @@
|
|||
export {};
|
|
@ -0,0 +1,9 @@
|
|||
(function () {
|
||||
require('./lib/main').config(
|
||||
Object.assign(
|
||||
{},
|
||||
require('./lib/env-options'),
|
||||
require('./lib/cli-options')(process.argv)
|
||||
)
|
||||
)
|
||||
})()
|
|
@ -0,0 +1,11 @@
|
|||
const re = /^dotenv_config_(encoding|path|debug|override|DOTENV_KEY)=(.+)$/
|
||||
|
||||
module.exports = function optionMatcher (args) {
|
||||
return args.reduce(function (acc, cur) {
|
||||
const matches = cur.match(re)
|
||||
if (matches) {
|
||||
acc[matches[1]] = matches[2]
|
||||
}
|
||||
return acc
|
||||
}, {})
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// ../config.js accepts options via environment variables
|
||||
const options = {}
|
||||
|
||||
if (process.env.DOTENV_CONFIG_ENCODING != null) {
|
||||
options.encoding = process.env.DOTENV_CONFIG_ENCODING
|
||||
}
|
||||
|
||||
if (process.env.DOTENV_CONFIG_PATH != null) {
|
||||
options.path = process.env.DOTENV_CONFIG_PATH
|
||||
}
|
||||
|
||||
if (process.env.DOTENV_CONFIG_DEBUG != null) {
|
||||
options.debug = process.env.DOTENV_CONFIG_DEBUG
|
||||
}
|
||||
|
||||
if (process.env.DOTENV_CONFIG_OVERRIDE != null) {
|
||||
options.override = process.env.DOTENV_CONFIG_OVERRIDE
|
||||
}
|
||||
|
||||
if (process.env.DOTENV_CONFIG_DOTENV_KEY != null) {
|
||||
options.DOTENV_KEY = process.env.DOTENV_CONFIG_DOTENV_KEY
|
||||
}
|
||||
|
||||
module.exports = options
|
|
@ -0,0 +1,156 @@
|
|||
// TypeScript Version: 3.0
|
||||
/// <reference types="node" />
|
||||
import type { URL } from 'node:url';
|
||||
|
||||
export interface DotenvParseOutput {
|
||||
[name: string]: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string or buffer in the .env file format into an object.
|
||||
*
|
||||
* See https://docs.dotenv.org
|
||||
*
|
||||
* @param src - contents to be parsed. example: `'DB_HOST=localhost'`
|
||||
* @param options - additional options. example: `{ debug: true }`
|
||||
* @returns an object with keys and values based on `src`. example: `{ DB_HOST : 'localhost' }`
|
||||
*/
|
||||
export function parse<T extends DotenvParseOutput = DotenvParseOutput>(
|
||||
src: string | Buffer
|
||||
): T;
|
||||
|
||||
export interface DotenvConfigOptions {
|
||||
/**
|
||||
* Default: `path.resolve(process.cwd(), '.env')`
|
||||
*
|
||||
* Specify a custom path if your file containing environment variables is located elsewhere.
|
||||
*
|
||||
* example: `require('dotenv').config({ path: '/custom/path/to/.env' })`
|
||||
*/
|
||||
path?: string | URL;
|
||||
|
||||
/**
|
||||
* Default: `utf8`
|
||||
*
|
||||
* Specify the encoding of your file containing environment variables.
|
||||
*
|
||||
* example: `require('dotenv').config({ encoding: 'latin1' })`
|
||||
*/
|
||||
encoding?: string;
|
||||
|
||||
/**
|
||||
* Default: `false`
|
||||
*
|
||||
* Turn on logging to help debug why certain keys or values are not being set as you expect.
|
||||
*
|
||||
* example: `require('dotenv').config({ debug: process.env.DEBUG })`
|
||||
*/
|
||||
debug?: boolean;
|
||||
|
||||
/**
|
||||
* Default: `false`
|
||||
*
|
||||
* Override any environment variables that have already been set on your machine with values from your .env file.
|
||||
*
|
||||
* example: `require('dotenv').config({ override: true })`
|
||||
*/
|
||||
override?: boolean;
|
||||
|
||||
/**
|
||||
* Default: `process.env`
|
||||
*
|
||||
* Specify an object to write your secrets to. Defaults to process.env environment variables.
|
||||
*
|
||||
* example: `const processEnv = {}; require('dotenv').config({ processEnv: processEnv })`
|
||||
*/
|
||||
processEnv?: DotenvPopulateInput;
|
||||
|
||||
/**
|
||||
* Default: `undefined`
|
||||
*
|
||||
* Pass the DOTENV_KEY directly to config options. Defaults to looking for process.env.DOTENV_KEY environment variable. Note this only applies to decrypting .env.vault files. If passed as null or undefined, or not passed at all, dotenv falls back to its traditional job of parsing a .env file.
|
||||
*
|
||||
* example: `require('dotenv').config({ DOTENV_KEY: 'dotenv://:key_1234…@dotenv.org/vault/.env.vault?environment=production' })`
|
||||
*/
|
||||
DOTENV_KEY?: string;
|
||||
}
|
||||
|
||||
export interface DotenvConfigOutput {
|
||||
error?: Error;
|
||||
parsed?: DotenvParseOutput;
|
||||
}
|
||||
|
||||
export interface DotenvPopulateOptions {
|
||||
/**
|
||||
* Default: `false`
|
||||
*
|
||||
* Turn on logging to help debug why certain keys or values are not being set as you expect.
|
||||
*
|
||||
* example: `require('dotenv').config({ debug: process.env.DEBUG })`
|
||||
*/
|
||||
debug?: boolean;
|
||||
|
||||
/**
|
||||
* Default: `false`
|
||||
*
|
||||
* Override any environment variables that have already been set on your machine with values from your .env file.
|
||||
*
|
||||
* example: `require('dotenv').config({ override: true })`
|
||||
*/
|
||||
override?: boolean;
|
||||
}
|
||||
|
||||
export interface DotenvPopulateOutput {
|
||||
error?: Error;
|
||||
}
|
||||
|
||||
export interface DotenvPopulateInput {
|
||||
[name: string]: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads `.env` file contents into process.env by default. If `DOTENV_KEY` is present, it smartly attempts to load encrypted `.env.vault` file contents into process.env.
|
||||
*
|
||||
* See https://docs.dotenv.org
|
||||
*
|
||||
* @param options - additional options. example: `{ path: './custom/path', encoding: 'latin1', debug: true, override: false }`
|
||||
* @returns an object with a `parsed` key if successful or `error` key if an error occurred. example: { parsed: { KEY: 'value' } }
|
||||
*
|
||||
*/
|
||||
export function config(options?: DotenvConfigOptions): DotenvConfigOutput;
|
||||
|
||||
/**
|
||||
* Loads `.env` file contents into process.env.
|
||||
*
|
||||
* See https://docs.dotenv.org
|
||||
*
|
||||
* @param options - additional options. example: `{ path: './custom/path', encoding: 'latin1', debug: true, override: false }`
|
||||
* @returns an object with a `parsed` key if successful or `error` key if an error occurred. example: { parsed: { KEY: 'value' } }
|
||||
*
|
||||
*/
|
||||
export function configDotenv(options?: DotenvConfigOptions): DotenvConfigOutput;
|
||||
|
||||
/**
|
||||
* Loads `source` json contents into `target` like process.env.
|
||||
*
|
||||
* See https://docs.dotenv.org
|
||||
*
|
||||
* @param processEnv - the target JSON object. in most cases use process.env but you can also pass your own JSON object
|
||||
* @param parsed - the source JSON object
|
||||
* @param options - additional options. example: `{ debug: true, override: false }`
|
||||
* @returns {void}
|
||||
*
|
||||
*/
|
||||
export function populate(processEnv: DotenvPopulateInput, parsed: DotenvPopulateInput, options?: DotenvConfigOptions): DotenvPopulateOutput;
|
||||
|
||||
/**
|
||||
* Decrypt ciphertext
|
||||
*
|
||||
* See https://docs.dotenv.org
|
||||
*
|
||||
* @param encrypted - the encrypted ciphertext string
|
||||
* @param keyStr - the decryption key string
|
||||
* @returns {string}
|
||||
*
|
||||
*/
|
||||
export function decrypt(encrypted: string, keyStr: string): string;
|
|
@ -0,0 +1,314 @@
|
|||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const os = require('os')
|
||||
const crypto = require('crypto')
|
||||
const packageJson = require('../package.json')
|
||||
|
||||
const version = packageJson.version
|
||||
|
||||
const LINE = /(?:^|^)\s*(?:export\s+)?([\w.-]+)(?:\s*=\s*?|:\s+?)(\s*'(?:\\'|[^'])*'|\s*"(?:\\"|[^"])*"|\s*`(?:\\`|[^`])*`|[^#\r\n]+)?\s*(?:#.*)?(?:$|$)/mg
|
||||
|
||||
// Parse src into an Object
|
||||
function parse (src) {
|
||||
const obj = {}
|
||||
|
||||
// Convert buffer to string
|
||||
let lines = src.toString()
|
||||
|
||||
// Convert line breaks to same format
|
||||
lines = lines.replace(/\r\n?/mg, '\n')
|
||||
|
||||
let match
|
||||
while ((match = LINE.exec(lines)) != null) {
|
||||
const key = match[1]
|
||||
|
||||
// Default undefined or null to empty string
|
||||
let value = (match[2] || '')
|
||||
|
||||
// Remove whitespace
|
||||
value = value.trim()
|
||||
|
||||
// Check if double quoted
|
||||
const maybeQuote = value[0]
|
||||
|
||||
// Remove surrounding quotes
|
||||
value = value.replace(/^(['"`])([\s\S]*)\1$/mg, '$2')
|
||||
|
||||
// Expand newlines if double quoted
|
||||
if (maybeQuote === '"') {
|
||||
value = value.replace(/\\n/g, '\n')
|
||||
value = value.replace(/\\r/g, '\r')
|
||||
}
|
||||
|
||||
// Add to object
|
||||
obj[key] = value
|
||||
}
|
||||
|
||||
return obj
|
||||
}
|
||||
|
||||
function _parseVault (options) {
|
||||
const vaultPath = _vaultPath(options)
|
||||
|
||||
// Parse .env.vault
|
||||
const result = DotenvModule.configDotenv({ path: vaultPath })
|
||||
if (!result.parsed) {
|
||||
throw new Error(`MISSING_DATA: Cannot parse ${vaultPath} for an unknown reason`)
|
||||
}
|
||||
|
||||
// handle scenario for comma separated keys - for use with key rotation
|
||||
// example: DOTENV_KEY="dotenv://:key_1234@dotenv.org/vault/.env.vault?environment=prod,dotenv://:key_7890@dotenv.org/vault/.env.vault?environment=prod"
|
||||
const keys = _dotenvKey(options).split(',')
|
||||
const length = keys.length
|
||||
|
||||
let decrypted
|
||||
for (let i = 0; i < length; i++) {
|
||||
try {
|
||||
// Get full key
|
||||
const key = keys[i].trim()
|
||||
|
||||
// Get instructions for decrypt
|
||||
const attrs = _instructions(result, key)
|
||||
|
||||
// Decrypt
|
||||
decrypted = DotenvModule.decrypt(attrs.ciphertext, attrs.key)
|
||||
|
||||
break
|
||||
} catch (error) {
|
||||
// last key
|
||||
if (i + 1 >= length) {
|
||||
throw error
|
||||
}
|
||||
// try next key
|
||||
}
|
||||
}
|
||||
|
||||
// Parse decrypted .env string
|
||||
return DotenvModule.parse(decrypted)
|
||||
}
|
||||
|
||||
function _log (message) {
|
||||
console.log(`[dotenv@${version}][INFO] ${message}`)
|
||||
}
|
||||
|
||||
function _warn (message) {
|
||||
console.log(`[dotenv@${version}][WARN] ${message}`)
|
||||
}
|
||||
|
||||
function _debug (message) {
|
||||
console.log(`[dotenv@${version}][DEBUG] ${message}`)
|
||||
}
|
||||
|
||||
function _dotenvKey (options) {
|
||||
// prioritize developer directly setting options.DOTENV_KEY
|
||||
if (options && options.DOTENV_KEY && options.DOTENV_KEY.length > 0) {
|
||||
return options.DOTENV_KEY
|
||||
}
|
||||
|
||||
// secondary infra already contains a DOTENV_KEY environment variable
|
||||
if (process.env.DOTENV_KEY && process.env.DOTENV_KEY.length > 0) {
|
||||
return process.env.DOTENV_KEY
|
||||
}
|
||||
|
||||
// fallback to empty string
|
||||
return ''
|
||||
}
|
||||
|
||||
function _instructions (result, dotenvKey) {
|
||||
// Parse DOTENV_KEY. Format is a URI
|
||||
let uri
|
||||
try {
|
||||
uri = new URL(dotenvKey)
|
||||
} catch (error) {
|
||||
if (error.code === 'ERR_INVALID_URL') {
|
||||
throw new Error('INVALID_DOTENV_KEY: Wrong format. Must be in valid uri format like dotenv://:key_1234@dotenv.org/vault/.env.vault?environment=development')
|
||||
}
|
||||
|
||||
throw error
|
||||
}
|
||||
|
||||
// Get decrypt key
|
||||
const key = uri.password
|
||||
if (!key) {
|
||||
throw new Error('INVALID_DOTENV_KEY: Missing key part')
|
||||
}
|
||||
|
||||
// Get environment
|
||||
const environment = uri.searchParams.get('environment')
|
||||
if (!environment) {
|
||||
throw new Error('INVALID_DOTENV_KEY: Missing environment part')
|
||||
}
|
||||
|
||||
// Get ciphertext payload
|
||||
const environmentKey = `DOTENV_VAULT_${environment.toUpperCase()}`
|
||||
const ciphertext = result.parsed[environmentKey] // DOTENV_VAULT_PRODUCTION
|
||||
if (!ciphertext) {
|
||||
throw new Error(`NOT_FOUND_DOTENV_ENVIRONMENT: Cannot locate environment ${environmentKey} in your .env.vault file.`)
|
||||
}
|
||||
|
||||
return { ciphertext, key }
|
||||
}
|
||||
|
||||
function _vaultPath (options) {
|
||||
let dotenvPath = path.resolve(process.cwd(), '.env')
|
||||
|
||||
if (options && options.path && options.path.length > 0) {
|
||||
dotenvPath = options.path
|
||||
}
|
||||
|
||||
// Locate .env.vault
|
||||
return dotenvPath.endsWith('.vault') ? dotenvPath : `${dotenvPath}.vault`
|
||||
}
|
||||
|
||||
function _resolveHome (envPath) {
|
||||
return envPath[0] === '~' ? path.join(os.homedir(), envPath.slice(1)) : envPath
|
||||
}
|
||||
|
||||
function _configVault (options) {
|
||||
_log('Loading env from encrypted .env.vault')
|
||||
|
||||
const parsed = DotenvModule._parseVault(options)
|
||||
|
||||
let processEnv = process.env
|
||||
if (options && options.processEnv != null) {
|
||||
processEnv = options.processEnv
|
||||
}
|
||||
|
||||
DotenvModule.populate(processEnv, parsed, options)
|
||||
|
||||
return { parsed }
|
||||
}
|
||||
|
||||
function configDotenv (options) {
|
||||
let dotenvPath = path.resolve(process.cwd(), '.env')
|
||||
let encoding = 'utf8'
|
||||
const debug = Boolean(options && options.debug)
|
||||
|
||||
if (options) {
|
||||
if (options.path != null) {
|
||||
dotenvPath = _resolveHome(options.path)
|
||||
}
|
||||
if (options.encoding != null) {
|
||||
encoding = options.encoding
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Specifying an encoding returns a string instead of a buffer
|
||||
const parsed = DotenvModule.parse(fs.readFileSync(dotenvPath, { encoding }))
|
||||
|
||||
let processEnv = process.env
|
||||
if (options && options.processEnv != null) {
|
||||
processEnv = options.processEnv
|
||||
}
|
||||
|
||||
DotenvModule.populate(processEnv, parsed, options)
|
||||
|
||||
return { parsed }
|
||||
} catch (e) {
|
||||
if (debug) {
|
||||
_debug(`Failed to load ${dotenvPath} ${e.message}`)
|
||||
}
|
||||
|
||||
return { error: e }
|
||||
}
|
||||
}
|
||||
|
||||
// Populates process.env from .env file
|
||||
function config (options) {
|
||||
const vaultPath = _vaultPath(options)
|
||||
|
||||
// fallback to original dotenv if DOTENV_KEY is not set
|
||||
if (_dotenvKey(options).length === 0) {
|
||||
return DotenvModule.configDotenv(options)
|
||||
}
|
||||
|
||||
// dotenvKey exists but .env.vault file does not exist
|
||||
if (!fs.existsSync(vaultPath)) {
|
||||
_warn(`You set DOTENV_KEY but you are missing a .env.vault file at ${vaultPath}. Did you forget to build it?`)
|
||||
|
||||
return DotenvModule.configDotenv(options)
|
||||
}
|
||||
|
||||
return DotenvModule._configVault(options)
|
||||
}
|
||||
|
||||
function decrypt (encrypted, keyStr) {
|
||||
const key = Buffer.from(keyStr.slice(-64), 'hex')
|
||||
let ciphertext = Buffer.from(encrypted, 'base64')
|
||||
|
||||
const nonce = ciphertext.slice(0, 12)
|
||||
const authTag = ciphertext.slice(-16)
|
||||
ciphertext = ciphertext.slice(12, -16)
|
||||
|
||||
try {
|
||||
const aesgcm = crypto.createDecipheriv('aes-256-gcm', key, nonce)
|
||||
aesgcm.setAuthTag(authTag)
|
||||
return `${aesgcm.update(ciphertext)}${aesgcm.final()}`
|
||||
} catch (error) {
|
||||
const isRange = error instanceof RangeError
|
||||
const invalidKeyLength = error.message === 'Invalid key length'
|
||||
const decryptionFailed = error.message === 'Unsupported state or unable to authenticate data'
|
||||
|
||||
if (isRange || invalidKeyLength) {
|
||||
const msg = 'INVALID_DOTENV_KEY: It must be 64 characters long (or more)'
|
||||
throw new Error(msg)
|
||||
} else if (decryptionFailed) {
|
||||
const msg = 'DECRYPTION_FAILED: Please check your DOTENV_KEY'
|
||||
throw new Error(msg)
|
||||
} else {
|
||||
console.error('Error: ', error.code)
|
||||
console.error('Error: ', error.message)
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Populate process.env with parsed values
|
||||
function populate (processEnv, parsed, options = {}) {
|
||||
const debug = Boolean(options && options.debug)
|
||||
const override = Boolean(options && options.override)
|
||||
|
||||
if (typeof parsed !== 'object') {
|
||||
throw new Error('OBJECT_REQUIRED: Please check the processEnv argument being passed to populate')
|
||||
}
|
||||
|
||||
// Set process.env
|
||||
for (const key of Object.keys(parsed)) {
|
||||
if (Object.prototype.hasOwnProperty.call(processEnv, key)) {
|
||||
if (override === true) {
|
||||
processEnv[key] = parsed[key]
|
||||
}
|
||||
|
||||
if (debug) {
|
||||
if (override === true) {
|
||||
_debug(`"${key}" is already defined and WAS overwritten`)
|
||||
} else {
|
||||
_debug(`"${key}" is already defined and was NOT overwritten`)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
processEnv[key] = parsed[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const DotenvModule = {
|
||||
configDotenv,
|
||||
_configVault,
|
||||
_parseVault,
|
||||
config,
|
||||
decrypt,
|
||||
parse,
|
||||
populate
|
||||
}
|
||||
|
||||
module.exports.configDotenv = DotenvModule.configDotenv
|
||||
module.exports._configVault = DotenvModule._configVault
|
||||
module.exports._parseVault = DotenvModule._parseVault
|
||||
module.exports.config = DotenvModule.config
|
||||
module.exports.decrypt = DotenvModule.decrypt
|
||||
module.exports.parse = DotenvModule.parse
|
||||
module.exports.populate = DotenvModule.populate
|
||||
|
||||
module.exports = DotenvModule
|
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
"name": "dotenv",
|
||||
"version": "16.3.1",
|
||||
"description": "Loads environment variables from .env file",
|
||||
"main": "lib/main.js",
|
||||
"types": "lib/main.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./lib/main.d.ts",
|
||||
"require": "./lib/main.js",
|
||||
"default": "./lib/main.js"
|
||||
},
|
||||
"./config": "./config.js",
|
||||
"./config.js": "./config.js",
|
||||
"./lib/env-options": "./lib/env-options.js",
|
||||
"./lib/env-options.js": "./lib/env-options.js",
|
||||
"./lib/cli-options": "./lib/cli-options.js",
|
||||
"./lib/cli-options.js": "./lib/cli-options.js",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"scripts": {
|
||||
"dts-check": "tsc --project tests/types/tsconfig.json",
|
||||
"lint": "standard",
|
||||
"lint-readme": "standard-markdown",
|
||||
"pretest": "npm run lint && npm run dts-check",
|
||||
"test": "tap tests/*.js --100 -Rspec",
|
||||
"prerelease": "npm test",
|
||||
"release": "standard-version"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/motdotla/dotenv.git"
|
||||
},
|
||||
"funding": "https://github.com/motdotla/dotenv?sponsor=1",
|
||||
"keywords": [
|
||||
"dotenv",
|
||||
"env",
|
||||
".env",
|
||||
"environment",
|
||||
"variables",
|
||||
"config",
|
||||
"settings"
|
||||
],
|
||||
"readmeFilename": "README.md",
|
||||
"license": "BSD-2-Clause",
|
||||
"devDependencies": {
|
||||
"@definitelytyped/dtslint": "^0.0.133",
|
||||
"@types/node": "^18.11.3",
|
||||
"decache": "^4.6.1",
|
||||
"sinon": "^14.0.1",
|
||||
"standard": "^17.0.0",
|
||||
"standard-markdown": "^7.1.0",
|
||||
"standard-version": "^9.5.0",
|
||||
"tap": "^16.3.0",
|
||||
"tar": "^6.1.11",
|
||||
"typescript": "^4.8.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"browser": {
|
||||
"fs": false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,442 @@
|
|||
1.17.3 / 2022-05-11
|
||||
===================
|
||||
|
||||
* Fix resaving already-saved new session at end of request
|
||||
* deps: cookie@0.4.2
|
||||
|
||||
1.17.2 / 2021-05-19
|
||||
===================
|
||||
|
||||
* Fix `res.end` patch to always commit headers
|
||||
* deps: cookie@0.4.1
|
||||
* deps: safe-buffer@5.2.1
|
||||
|
||||
1.17.1 / 2020-04-16
|
||||
===================
|
||||
|
||||
* Fix internal method wrapping error on failed reloads
|
||||
|
||||
1.17.0 / 2019-10-10
|
||||
===================
|
||||
|
||||
* deps: cookie@0.4.0
|
||||
- Add `SameSite=None` support
|
||||
* deps: safe-buffer@5.2.0
|
||||
|
||||
1.16.2 / 2019-06-12
|
||||
===================
|
||||
|
||||
* Fix restoring `cookie.originalMaxAge` when store returns `Date`
|
||||
* deps: parseurl@~1.3.3
|
||||
|
||||
1.16.1 / 2019-04-11
|
||||
===================
|
||||
|
||||
* Fix error passing `data` option to `Cookie` constructor
|
||||
* Fix uncaught error from bad session data
|
||||
|
||||
1.16.0 / 2019-04-10
|
||||
===================
|
||||
|
||||
* Catch invalid `cookie.maxAge` value earlier
|
||||
* Deprecate setting `cookie.maxAge` to a `Date` object
|
||||
* Fix issue where `resave: false` may not save altered sessions
|
||||
* Remove `utils-merge` dependency
|
||||
* Use `safe-buffer` for improved Buffer API
|
||||
* Use `Set-Cookie` as cookie header name for compatibility
|
||||
* deps: depd@~2.0.0
|
||||
- Replace internal `eval` usage with `Function` constructor
|
||||
- Use instance methods on `process` to check for listeners
|
||||
- perf: remove argument reassignment
|
||||
* deps: on-headers@~1.0.2
|
||||
- Fix `res.writeHead` patch missing return value
|
||||
|
||||
1.15.6 / 2017-09-26
|
||||
===================
|
||||
|
||||
* deps: debug@2.6.9
|
||||
* deps: parseurl@~1.3.2
|
||||
- perf: reduce overhead for full URLs
|
||||
- perf: unroll the "fast-path" `RegExp`
|
||||
* deps: uid-safe@~2.1.5
|
||||
- perf: remove only trailing `=`
|
||||
* deps: utils-merge@1.0.1
|
||||
|
||||
1.15.5 / 2017-08-02
|
||||
===================
|
||||
|
||||
* Fix `TypeError` when `req.url` is an empty string
|
||||
* deps: depd@~1.1.1
|
||||
- Remove unnecessary `Buffer` loading
|
||||
|
||||
1.15.4 / 2017-07-18
|
||||
===================
|
||||
|
||||
* deps: debug@2.6.8
|
||||
|
||||
1.15.3 / 2017-05-17
|
||||
===================
|
||||
|
||||
* deps: debug@2.6.7
|
||||
- deps: ms@2.0.0
|
||||
|
||||
1.15.2 / 2017-03-26
|
||||
===================
|
||||
|
||||
* deps: debug@2.6.3
|
||||
- Fix `DEBUG_MAX_ARRAY_LENGTH`
|
||||
* deps: uid-safe@~2.1.4
|
||||
- Remove `base64-url` dependency
|
||||
|
||||
1.15.1 / 2017-02-10
|
||||
===================
|
||||
|
||||
* deps: debug@2.6.1
|
||||
- Fix deprecation messages in WebStorm and other editors
|
||||
- Undeprecate `DEBUG_FD` set to `1` or `2`
|
||||
|
||||
1.15.0 / 2017-01-22
|
||||
===================
|
||||
|
||||
* Fix detecting modified session when session contains "cookie" property
|
||||
* Fix resaving already-saved reloaded session at end of request
|
||||
* deps: crc@3.4.4
|
||||
- perf: use `Buffer.from` when available
|
||||
* deps: debug@2.6.0
|
||||
- Allow colors in workers
|
||||
- Deprecated `DEBUG_FD` environment variable
|
||||
- Use same color for same namespace
|
||||
- Fix error when running under React Native
|
||||
- deps: ms@0.7.2
|
||||
* perf: remove unreachable branch in set-cookie method
|
||||
|
||||
1.14.2 / 2016-10-30
|
||||
===================
|
||||
|
||||
* deps: crc@3.4.1
|
||||
- Fix deprecation warning in Node.js 7.x
|
||||
* deps: uid-safe@~2.1.3
|
||||
- deps: base64-url@1.3.3
|
||||
|
||||
1.14.1 / 2016-08-24
|
||||
===================
|
||||
|
||||
* Fix not always resetting session max age before session save
|
||||
* Fix the cookie `sameSite` option to actually alter the `Set-Cookie`
|
||||
* deps: uid-safe@~2.1.2
|
||||
- deps: base64-url@1.3.2
|
||||
|
||||
1.14.0 / 2016-07-01
|
||||
===================
|
||||
|
||||
* Correctly inherit from `EventEmitter` class in `Store` base class
|
||||
* Fix issue where `Set-Cookie` `Expires` was not always updated
|
||||
* Methods are no longer enumerable on `req.session` object
|
||||
* deps: cookie@0.3.1
|
||||
- Add `sameSite` option
|
||||
- Improve error message when `encode` is not a function
|
||||
- Improve error message when `expires` is not a `Date`
|
||||
- perf: enable strict mode
|
||||
- perf: use for loop in parse
|
||||
- perf: use string concatination for serialization
|
||||
* deps: parseurl@~1.3.1
|
||||
- perf: enable strict mode
|
||||
* deps: uid-safe@~2.1.1
|
||||
- Use `random-bytes` for byte source
|
||||
- deps: base64-url@1.2.2
|
||||
* perf: enable strict mode
|
||||
* perf: remove argument reassignment
|
||||
|
||||
1.13.0 / 2016-01-10
|
||||
===================
|
||||
|
||||
* Fix `rolling: true` to not set cookie when no session exists
|
||||
- Better `saveUninitialized: false` + `rolling: true` behavior
|
||||
* deps: crc@3.4.0
|
||||
|
||||
1.12.1 / 2015-10-29
|
||||
===================
|
||||
|
||||
* deps: cookie@0.2.3
|
||||
- Fix cookie `Max-Age` to never be a floating point number
|
||||
|
||||
1.12.0 / 2015-10-25
|
||||
===================
|
||||
|
||||
* Support the value `'auto'` in the `cookie.secure` option
|
||||
* deps: cookie@0.2.2
|
||||
- Throw on invalid values provided to `serialize`
|
||||
* deps: depd@~1.1.0
|
||||
- Enable strict mode in more places
|
||||
- Support web browser loading
|
||||
* deps: on-headers@~1.0.1
|
||||
- perf: enable strict mode
|
||||
|
||||
1.11.3 / 2015-05-22
|
||||
===================
|
||||
|
||||
* deps: cookie@0.1.3
|
||||
- Slight optimizations
|
||||
* deps: crc@3.3.0
|
||||
|
||||
1.11.2 / 2015-05-10
|
||||
===================
|
||||
|
||||
* deps: debug@~2.2.0
|
||||
- deps: ms@0.7.1
|
||||
* deps: uid-safe@~2.0.0
|
||||
|
||||
1.11.1 / 2015-04-08
|
||||
===================
|
||||
|
||||
* Fix mutating `options.secret` value
|
||||
|
||||
1.11.0 / 2015-04-07
|
||||
===================
|
||||
|
||||
* Support an array in `secret` option for key rotation
|
||||
* deps: depd@~1.0.1
|
||||
|
||||
1.10.4 / 2015-03-15
|
||||
===================
|
||||
|
||||
* deps: debug@~2.1.3
|
||||
- Fix high intensity foreground color for bold
|
||||
- deps: ms@0.7.0
|
||||
|
||||
1.10.3 / 2015-02-16
|
||||
===================
|
||||
|
||||
* deps: cookie-signature@1.0.6
|
||||
* deps: uid-safe@1.1.0
|
||||
- Use `crypto.randomBytes`, if available
|
||||
- deps: base64-url@1.2.1
|
||||
|
||||
1.10.2 / 2015-01-31
|
||||
===================
|
||||
|
||||
* deps: uid-safe@1.0.3
|
||||
- Fix error branch that would throw
|
||||
- deps: base64-url@1.2.0
|
||||
|
||||
1.10.1 / 2015-01-08
|
||||
===================
|
||||
|
||||
* deps: uid-safe@1.0.2
|
||||
- Remove dependency on `mz`
|
||||
|
||||
1.10.0 / 2015-01-05
|
||||
===================
|
||||
|
||||
* Add `store.touch` interface for session stores
|
||||
* Fix `MemoryStore` expiration with `resave: false`
|
||||
* deps: debug@~2.1.1
|
||||
|
||||
1.9.3 / 2014-12-02
|
||||
==================
|
||||
|
||||
* Fix error when `req.sessionID` contains a non-string value
|
||||
|
||||
1.9.2 / 2014-11-22
|
||||
==================
|
||||
|
||||
* deps: crc@3.2.1
|
||||
- Minor fixes
|
||||
|
||||
1.9.1 / 2014-10-22
|
||||
==================
|
||||
|
||||
* Remove unnecessary empty write call
|
||||
- Fixes Node.js 0.11.14 behavior change
|
||||
- Helps work-around Node.js 0.10.1 zlib bug
|
||||
|
||||
1.9.0 / 2014-09-16
|
||||
==================
|
||||
|
||||
* deps: debug@~2.1.0
|
||||
- Implement `DEBUG_FD` env variable support
|
||||
* deps: depd@~1.0.0
|
||||
|
||||
1.8.2 / 2014-09-15
|
||||
==================
|
||||
|
||||
* Use `crc` instead of `buffer-crc32` for speed
|
||||
* deps: depd@0.4.5
|
||||
|
||||
1.8.1 / 2014-09-08
|
||||
==================
|
||||
|
||||
* Keep `req.session.save` non-enumerable
|
||||
* Prevent session prototype methods from being overwritten
|
||||
|
||||
1.8.0 / 2014-09-07
|
||||
==================
|
||||
|
||||
* Do not resave already-saved session at end of request
|
||||
* deps: cookie-signature@1.0.5
|
||||
* deps: debug@~2.0.0
|
||||
|
||||
1.7.6 / 2014-08-18
|
||||
==================
|
||||
|
||||
* Fix exception on `res.end(null)` calls
|
||||
|
||||
1.7.5 / 2014-08-10
|
||||
==================
|
||||
|
||||
* Fix parsing original URL
|
||||
* deps: on-headers@~1.0.0
|
||||
* deps: parseurl@~1.3.0
|
||||
|
||||
1.7.4 / 2014-08-05
|
||||
==================
|
||||
|
||||
* Fix response end delay for non-chunked responses
|
||||
|
||||
1.7.3 / 2014-08-05
|
||||
==================
|
||||
|
||||
* Fix `res.end` patch to call correct upstream `res.write`
|
||||
|
||||
1.7.2 / 2014-07-27
|
||||
==================
|
||||
|
||||
* deps: depd@0.4.4
|
||||
- Work-around v8 generating empty stack traces
|
||||
|
||||
1.7.1 / 2014-07-26
|
||||
==================
|
||||
|
||||
* deps: depd@0.4.3
|
||||
- Fix exception when global `Error.stackTraceLimit` is too low
|
||||
|
||||
1.7.0 / 2014-07-22
|
||||
==================
|
||||
|
||||
* Improve session-ending error handling
|
||||
- Errors are passed to `next(err)` instead of `console.error`
|
||||
* deps: debug@1.0.4
|
||||
* deps: depd@0.4.2
|
||||
- Add `TRACE_DEPRECATION` environment variable
|
||||
- Remove non-standard grey color from color output
|
||||
- Support `--no-deprecation` argument
|
||||
- Support `--trace-deprecation` argument
|
||||
|
||||
1.6.5 / 2014-07-11
|
||||
==================
|
||||
|
||||
* Do not require `req.originalUrl`
|
||||
* deps: debug@1.0.3
|
||||
- Add support for multiple wildcards in namespaces
|
||||
|
||||
1.6.4 / 2014-07-07
|
||||
==================
|
||||
|
||||
* Fix blank responses for stores with synchronous operations
|
||||
|
||||
1.6.3 / 2014-07-04
|
||||
==================
|
||||
|
||||
* Fix resave deprecation message
|
||||
|
||||
1.6.2 / 2014-07-04
|
||||
==================
|
||||
|
||||
* Fix confusing option deprecation messages
|
||||
|
||||
1.6.1 / 2014-06-28
|
||||
==================
|
||||
|
||||
* Fix saveUninitialized deprecation message
|
||||
|
||||
1.6.0 / 2014-06-28
|
||||
==================
|
||||
|
||||
* Add deprecation message to undefined `resave` option
|
||||
* Add deprecation message to undefined `saveUninitialized` option
|
||||
* Fix `res.end` patch to return correct value
|
||||
* Fix `res.end` patch to handle multiple `res.end` calls
|
||||
* Reject cookies with missing signatures
|
||||
|
||||
1.5.2 / 2014-06-26
|
||||
==================
|
||||
|
||||
* deps: cookie-signature@1.0.4
|
||||
- fix for timing attacks
|
||||
|
||||
1.5.1 / 2014-06-21
|
||||
==================
|
||||
|
||||
* Move hard-to-track-down `req.secret` deprecation message
|
||||
|
||||
1.5.0 / 2014-06-19
|
||||
==================
|
||||
|
||||
* Debug name is now "express-session"
|
||||
* Deprecate integration with `cookie-parser` middleware
|
||||
* Deprecate looking for secret in `req.secret`
|
||||
* Directly read cookies; `cookie-parser` no longer required
|
||||
* Directly set cookies; `res.cookie` no longer required
|
||||
* Generate session IDs with `uid-safe`, faster and even less collisions
|
||||
|
||||
1.4.0 / 2014-06-17
|
||||
==================
|
||||
|
||||
* Add `genid` option to generate custom session IDs
|
||||
* Add `saveUninitialized` option to control saving uninitialized sessions
|
||||
* Add `unset` option to control unsetting `req.session`
|
||||
* Generate session IDs with `rand-token` by default; reduce collisions
|
||||
* deps: buffer-crc32@0.2.3
|
||||
|
||||
1.3.1 / 2014-06-14
|
||||
==================
|
||||
|
||||
* Add description in package for npmjs.org listing
|
||||
|
||||
1.3.0 / 2014-06-14
|
||||
==================
|
||||
|
||||
* Integrate with express "trust proxy" by default
|
||||
* deps: debug@1.0.2
|
||||
|
||||
1.2.1 / 2014-05-27
|
||||
==================
|
||||
|
||||
* Fix `resave` such that `resave: true` works
|
||||
|
||||
1.2.0 / 2014-05-19
|
||||
==================
|
||||
|
||||
* Add `resave` option to control saving unmodified sessions
|
||||
|
||||
1.1.0 / 2014-05-12
|
||||
==================
|
||||
|
||||
* Add `name` option; replacement for `key` option
|
||||
* Use `setImmediate` in MemoryStore for node.js >= 0.10
|
||||
|
||||
1.0.4 / 2014-04-27
|
||||
==================
|
||||
|
||||
* deps: debug@0.8.1
|
||||
|
||||
1.0.3 / 2014-04-19
|
||||
==================
|
||||
|
||||
* Use `res.cookie()` instead of `res.setHeader()`
|
||||
* deps: cookie@0.1.2
|
||||
|
||||
1.0.2 / 2014-02-23
|
||||
==================
|
||||
|
||||
* Add missing dependency to `package.json`
|
||||
|
||||
1.0.1 / 2014-02-15
|
||||
==================
|
||||
|
||||
* Add missing dependencies to `package.json`
|
||||
|
||||
1.0.0 / 2014-02-15
|
||||
==================
|
||||
|
||||
* Genesis from `connect`
|
|
@ -0,0 +1,24 @@
|
|||
(The MIT License)
|
||||
|
||||
Copyright (c) 2010 Sencha Inc.
|
||||
Copyright (c) 2011 TJ Holowaychuk <tj@vision-media.ca>
|
||||
Copyright (c) 2014-2015 Douglas Christopher Wilson <doug@somethingdoug.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,985 @@
|
|||
# express-session
|
||||
|
||||
[![NPM Version][npm-version-image]][npm-url]
|
||||
[![NPM Downloads][npm-downloads-image]][node-url]
|
||||
[![Build Status][ci-image]][ci-url]
|
||||
[![Test Coverage][coveralls-image]][coveralls-url]
|
||||
|
||||
## Installation
|
||||
|
||||
This is a [Node.js](https://nodejs.org/en/) module available through the
|
||||
[npm registry](https://www.npmjs.com/). Installation is done using the
|
||||
[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
|
||||
|
||||
```sh
|
||||
$ npm install express-session
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
```js
|
||||
var session = require('express-session')
|
||||
```
|
||||
|
||||
### session(options)
|
||||
|
||||
Create a session middleware with the given `options`.
|
||||
|
||||
**Note** Session data is _not_ saved in the cookie itself, just the session ID.
|
||||
Session data is stored server-side.
|
||||
|
||||
**Note** Since version 1.5.0, the [`cookie-parser` middleware](https://www.npmjs.com/package/cookie-parser)
|
||||
no longer needs to be used for this module to work. This module now directly reads
|
||||
and writes cookies on `req`/`res`. Using `cookie-parser` may result in issues
|
||||
if the `secret` is not the same between this module and `cookie-parser`.
|
||||
|
||||
**Warning** The default server-side session storage, `MemoryStore`, is _purposely_
|
||||
not designed for a production environment. It will leak memory under most
|
||||
conditions, does not scale past a single process, and is meant for debugging and
|
||||
developing.
|
||||
|
||||
For a list of stores, see [compatible session stores](#compatible-session-stores).
|
||||
|
||||
#### Options
|
||||
|
||||
`express-session` accepts these properties in the options object.
|
||||
|
||||
##### cookie
|
||||
|
||||
Settings object for the session ID cookie. The default value is
|
||||
`{ path: '/', httpOnly: true, secure: false, maxAge: null }`.
|
||||
|
||||
The following are options that can be set in this object.
|
||||
|
||||
##### cookie.domain
|
||||
|
||||
Specifies the value for the `Domain` `Set-Cookie` attribute. By default, no domain
|
||||
is set, and most clients will consider the cookie to apply to only the current
|
||||
domain.
|
||||
|
||||
##### cookie.expires
|
||||
|
||||
Specifies the `Date` object to be the value for the `Expires` `Set-Cookie` attribute.
|
||||
By default, no expiration is set, and most clients will consider this a
|
||||
"non-persistent cookie" and will delete it on a condition like exiting a web browser
|
||||
application.
|
||||
|
||||
**Note** If both `expires` and `maxAge` are set in the options, then the last one
|
||||
defined in the object is what is used.
|
||||
|
||||
**Note** The `expires` option should not be set directly; instead only use the `maxAge`
|
||||
option.
|
||||
|
||||
##### cookie.httpOnly
|
||||
|
||||
Specifies the `boolean` value for the `HttpOnly` `Set-Cookie` attribute. When truthy,
|
||||
the `HttpOnly` attribute is set, otherwise it is not. By default, the `HttpOnly`
|
||||
attribute is set.
|
||||
|
||||
**Note** be careful when setting this to `true`, as compliant clients will not allow
|
||||
client-side JavaScript to see the cookie in `document.cookie`.
|
||||
|
||||
##### cookie.maxAge
|
||||
|
||||
Specifies the `number` (in milliseconds) to use when calculating the `Expires`
|
||||
`Set-Cookie` attribute. This is done by taking the current server time and adding
|
||||
`maxAge` milliseconds to the value to calculate an `Expires` datetime. By default,
|
||||
no maximum age is set.
|
||||
|
||||
**Note** If both `expires` and `maxAge` are set in the options, then the last one
|
||||
defined in the object is what is used.
|
||||
|
||||
##### cookie.path
|
||||
|
||||
Specifies the value for the `Path` `Set-Cookie`. By default, this is set to `'/'`, which
|
||||
is the root path of the domain.
|
||||
|
||||
##### cookie.sameSite
|
||||
|
||||
Specifies the `boolean` or `string` to be the value for the `SameSite` `Set-Cookie` attribute.
|
||||
By default, this is `false`.
|
||||
|
||||
- `true` will set the `SameSite` attribute to `Strict` for strict same site enforcement.
|
||||
- `false` will not set the `SameSite` attribute.
|
||||
- `'lax'` will set the `SameSite` attribute to `Lax` for lax same site enforcement.
|
||||
- `'none'` will set the `SameSite` attribute to `None` for an explicit cross-site cookie.
|
||||
- `'strict'` will set the `SameSite` attribute to `Strict` for strict same site enforcement.
|
||||
|
||||
More information about the different enforcement levels can be found in
|
||||
[the specification][rfc-6265bis-03-4.1.2.7].
|
||||
|
||||
**Note** This is an attribute that has not yet been fully standardized, and may change in
|
||||
the future. This also means many clients may ignore this attribute until they understand it.
|
||||
|
||||
**Note** There is a [draft spec](https://tools.ietf.org/html/draft-west-cookie-incrementalism-01)
|
||||
that requires that the `Secure` attribute be set to `true` when the `SameSite` attribute has been
|
||||
set to `'none'`. Some web browsers or other clients may be adopting this specification.
|
||||
|
||||
##### cookie.secure
|
||||
|
||||
Specifies the `boolean` value for the `Secure` `Set-Cookie` attribute. When truthy,
|
||||
the `Secure` attribute is set, otherwise it is not. By default, the `Secure`
|
||||
attribute is not set.
|
||||
|
||||
**Note** be careful when setting this to `true`, as compliant clients will not send
|
||||
the cookie back to the server in the future if the browser does not have an HTTPS
|
||||
connection.
|
||||
|
||||
Please note that `secure: true` is a **recommended** option. However, it requires
|
||||
an https-enabled website, i.e., HTTPS is necessary for secure cookies. If `secure`
|
||||
is set, and you access your site over HTTP, the cookie will not be set. If you
|
||||
have your node.js behind a proxy and are using `secure: true`, you need to set
|
||||
"trust proxy" in express:
|
||||
|
||||
```js
|
||||
var app = express()
|
||||
app.set('trust proxy', 1) // trust first proxy
|
||||
app.use(session({
|
||||
secret: 'keyboard cat',
|
||||
resave: false,
|
||||
saveUninitialized: true,
|
||||
cookie: { secure: true }
|
||||
}))
|
||||
```
|
||||
|
||||
For using secure cookies in production, but allowing for testing in development,
|
||||
the following is an example of enabling this setup based on `NODE_ENV` in express:
|
||||
|
||||
```js
|
||||
var app = express()
|
||||
var sess = {
|
||||
secret: 'keyboard cat',
|
||||
cookie: {}
|
||||
}
|
||||
|
||||
if (app.get('env') === 'production') {
|
||||
app.set('trust proxy', 1) // trust first proxy
|
||||
sess.cookie.secure = true // serve secure cookies
|
||||
}
|
||||
|
||||
app.use(session(sess))
|
||||
```
|
||||
|
||||
The `cookie.secure` option can also be set to the special value `'auto'` to have
|
||||
this setting automatically match the determined security of the connection. Be
|
||||
careful when using this setting if the site is available both as HTTP and HTTPS,
|
||||
as once the cookie is set on HTTPS, it will no longer be visible over HTTP. This
|
||||
is useful when the Express `"trust proxy"` setting is properly setup to simplify
|
||||
development vs production configuration.
|
||||
|
||||
##### genid
|
||||
|
||||
Function to call to generate a new session ID. Provide a function that returns
|
||||
a string that will be used as a session ID. The function is given `req` as the
|
||||
first argument if you want to use some value attached to `req` when generating
|
||||
the ID.
|
||||
|
||||
The default value is a function which uses the `uid-safe` library to generate IDs.
|
||||
|
||||
**NOTE** be careful to generate unique IDs so your sessions do not conflict.
|
||||
|
||||
```js
|
||||
app.use(session({
|
||||
genid: function(req) {
|
||||
return genuuid() // use UUIDs for session IDs
|
||||
},
|
||||
secret: 'keyboard cat'
|
||||
}))
|
||||
```
|
||||
|
||||
##### name
|
||||
|
||||
The name of the session ID cookie to set in the response (and read from in the
|
||||
request).
|
||||
|
||||
The default value is `'connect.sid'`.
|
||||
|
||||
**Note** if you have multiple apps running on the same hostname (this is just
|
||||
the name, i.e. `localhost` or `127.0.0.1`; different schemes and ports do not
|
||||
name a different hostname), then you need to separate the session cookies from
|
||||
each other. The simplest method is to simply set different `name`s per app.
|
||||
|
||||
##### proxy
|
||||
|
||||
Trust the reverse proxy when setting secure cookies (via the "X-Forwarded-Proto"
|
||||
header).
|
||||
|
||||
The default value is `undefined`.
|
||||
|
||||
- `true` The "X-Forwarded-Proto" header will be used.
|
||||
- `false` All headers are ignored and the connection is considered secure only
|
||||
if there is a direct TLS/SSL connection.
|
||||
- `undefined` Uses the "trust proxy" setting from express
|
||||
|
||||
##### resave
|
||||
|
||||
Forces the session to be saved back to the session store, even if the session
|
||||
was never modified during the request. Depending on your store this may be
|
||||
necessary, but it can also create race conditions where a client makes two
|
||||
parallel requests to your server and changes made to the session in one
|
||||
request may get overwritten when the other request ends, even if it made no
|
||||
changes (this behavior also depends on what store you're using).
|
||||
|
||||
The default value is `true`, but using the default has been deprecated,
|
||||
as the default will change in the future. Please research into this setting
|
||||
and choose what is appropriate to your use-case. Typically, you'll want
|
||||
`false`.
|
||||
|
||||
How do I know if this is necessary for my store? The best way to know is to
|
||||
check with your store if it implements the `touch` method. If it does, then
|
||||
you can safely set `resave: false`. If it does not implement the `touch`
|
||||
method and your store sets an expiration date on stored sessions, then you
|
||||
likely need `resave: true`.
|
||||
|
||||
##### rolling
|
||||
|
||||
Force the session identifier cookie to be set on every response. The expiration
|
||||
is reset to the original [`maxAge`](#cookiemaxage), resetting the expiration
|
||||
countdown.
|
||||
|
||||
The default value is `false`.
|
||||
|
||||
With this enabled, the session identifier cookie will expire in
|
||||
[`maxAge`](#cookiemaxage) since the last response was sent instead of in
|
||||
[`maxAge`](#cookiemaxage) since the session was last modified by the server.
|
||||
|
||||
This is typically used in conjuction with short, non-session-length
|
||||
[`maxAge`](#cookiemaxage) values to provide a quick timeout of the session data
|
||||
with reduced potential of it occurring during on going server interactions.
|
||||
|
||||
**Note** When this option is set to `true` but the `saveUninitialized` option is
|
||||
set to `false`, the cookie will not be set on a response with an uninitialized
|
||||
session. This option only modifies the behavior when an existing session was
|
||||
loaded for the request.
|
||||
|
||||
##### saveUninitialized
|
||||
|
||||
Forces a session that is "uninitialized" to be saved to the store. A session is
|
||||
uninitialized when it is new but not modified. Choosing `false` is useful for
|
||||
implementing login sessions, reducing server storage usage, or complying with
|
||||
laws that require permission before setting a cookie. Choosing `false` will also
|
||||
help with race conditions where a client makes multiple parallel requests
|
||||
without a session.
|
||||
|
||||
The default value is `true`, but using the default has been deprecated, as the
|
||||
default will change in the future. Please research into this setting and
|
||||
choose what is appropriate to your use-case.
|
||||
|
||||
**Note** if you are using Session in conjunction with PassportJS, Passport
|
||||
will add an empty Passport object to the session for use after a user is
|
||||
authenticated, which will be treated as a modification to the session, causing
|
||||
it to be saved. *This has been fixed in PassportJS 0.3.0*
|
||||
|
||||
##### secret
|
||||
|
||||
**Required option**
|
||||
|
||||
This is the secret used to sign the session ID cookie. This can be either a string
|
||||
for a single secret, or an array of multiple secrets. If an array of secrets is
|
||||
provided, only the first element will be used to sign the session ID cookie, while
|
||||
all the elements will be considered when verifying the signature in requests. The
|
||||
secret itself should be not easily parsed by a human and would best be a random set
|
||||
of characters. A best practice may include:
|
||||
|
||||
- The use of environment variables to store the secret, ensuring the secret itself
|
||||
does not exist in your repository.
|
||||
- Periodic updates of the secret, while ensuring the previous secret is in the
|
||||
array.
|
||||
|
||||
Using a secret that cannot be guessed will reduce the ability to hijack a session to
|
||||
only guessing the session ID (as determined by the `genid` option).
|
||||
|
||||
Changing the secret value will invalidate all existing sessions. In order to rotate
|
||||
the secret without invalidating sessions, provide an array of secrets, with the new
|
||||
secret as first element of the array, and including previous secrets as the later
|
||||
elements.
|
||||
|
||||
##### store
|
||||
|
||||
The session store instance, defaults to a new `MemoryStore` instance.
|
||||
|
||||
##### unset
|
||||
|
||||
Control the result of unsetting `req.session` (through `delete`, setting to `null`,
|
||||
etc.).
|
||||
|
||||
The default value is `'keep'`.
|
||||
|
||||
- `'destroy'` The session will be destroyed (deleted) when the response ends.
|
||||
- `'keep'` The session in the store will be kept, but modifications made during
|
||||
the request are ignored and not saved.
|
||||
|
||||
### req.session
|
||||
|
||||
To store or access session data, simply use the request property `req.session`,
|
||||
which is (generally) serialized as JSON by the store, so nested objects
|
||||
are typically fine. For example below is a user-specific view counter:
|
||||
|
||||
```js
|
||||
// Use the session middleware
|
||||
app.use(session({ secret: 'keyboard cat', cookie: { maxAge: 60000 }}))
|
||||
|
||||
// Access the session as req.session
|
||||
app.get('/', function(req, res, next) {
|
||||
if (req.session.views) {
|
||||
req.session.views++
|
||||
res.setHeader('Content-Type', 'text/html')
|
||||
res.write('<p>views: ' + req.session.views + '</p>')
|
||||
res.write('<p>expires in: ' + (req.session.cookie.maxAge / 1000) + 's</p>')
|
||||
res.end()
|
||||
} else {
|
||||
req.session.views = 1
|
||||
res.end('welcome to the session demo. refresh!')
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
#### Session.regenerate(callback)
|
||||
|
||||
To regenerate the session simply invoke the method. Once complete,
|
||||
a new SID and `Session` instance will be initialized at `req.session`
|
||||
and the `callback` will be invoked.
|
||||
|
||||
```js
|
||||
req.session.regenerate(function(err) {
|
||||
// will have a new session here
|
||||
})
|
||||
```
|
||||
|
||||
#### Session.destroy(callback)
|
||||
|
||||
Destroys the session and will unset the `req.session` property.
|
||||
Once complete, the `callback` will be invoked.
|
||||
|
||||
```js
|
||||
req.session.destroy(function(err) {
|
||||
// cannot access session here
|
||||
})
|
||||
```
|
||||
|
||||
#### Session.reload(callback)
|
||||
|
||||
Reloads the session data from the store and re-populates the
|
||||
`req.session` object. Once complete, the `callback` will be invoked.
|
||||
|
||||
```js
|
||||
req.session.reload(function(err) {
|
||||
// session updated
|
||||
})
|
||||
```
|
||||
|
||||
#### Session.save(callback)
|
||||
|
||||
Save the session back to the store, replacing the contents on the store with the
|
||||
contents in memory (though a store may do something else--consult the store's
|
||||
documentation for exact behavior).
|
||||
|
||||
This method is automatically called at the end of the HTTP response if the
|
||||
session data has been altered (though this behavior can be altered with various
|
||||
options in the middleware constructor). Because of this, typically this method
|
||||
does not need to be called.
|
||||
|
||||
There are some cases where it is useful to call this method, for example,
|
||||
redirects, long-lived requests or in WebSockets.
|
||||
|
||||
```js
|
||||
req.session.save(function(err) {
|
||||
// session saved
|
||||
})
|
||||
```
|
||||
|
||||
#### Session.touch()
|
||||
|
||||
Updates the `.maxAge` property. Typically this is
|
||||
not necessary to call, as the session middleware does this for you.
|
||||
|
||||
### req.session.id
|
||||
|
||||
Each session has a unique ID associated with it. This property is an
|
||||
alias of [`req.sessionID`](#reqsessionid-1) and cannot be modified.
|
||||
It has been added to make the session ID accessible from the `session`
|
||||
object.
|
||||
|
||||
### req.session.cookie
|
||||
|
||||
Each session has a unique cookie object accompany it. This allows
|
||||
you to alter the session cookie per visitor. For example we can
|
||||
set `req.session.cookie.expires` to `false` to enable the cookie
|
||||
to remain for only the duration of the user-agent.
|
||||
|
||||
#### Cookie.maxAge
|
||||
|
||||
Alternatively `req.session.cookie.maxAge` will return the time
|
||||
remaining in milliseconds, which we may also re-assign a new value
|
||||
to adjust the `.expires` property appropriately. The following
|
||||
are essentially equivalent
|
||||
|
||||
```js
|
||||
var hour = 3600000
|
||||
req.session.cookie.expires = new Date(Date.now() + hour)
|
||||
req.session.cookie.maxAge = hour
|
||||
```
|
||||
|
||||
For example when `maxAge` is set to `60000` (one minute), and 30 seconds
|
||||
has elapsed it will return `30000` until the current request has completed,
|
||||
at which time `req.session.touch()` is called to reset
|
||||
`req.session.cookie.maxAge` to its original value.
|
||||
|
||||
```js
|
||||
req.session.cookie.maxAge // => 30000
|
||||
```
|
||||
|
||||
#### Cookie.originalMaxAge
|
||||
|
||||
The `req.session.cookie.originalMaxAge` property returns the original
|
||||
`maxAge` (time-to-live), in milliseconds, of the session cookie.
|
||||
|
||||
### req.sessionID
|
||||
|
||||
To get the ID of the loaded session, access the request property
|
||||
`req.sessionID`. This is simply a read-only value set when a session
|
||||
is loaded/created.
|
||||
|
||||
## Session Store Implementation
|
||||
|
||||
Every session store _must_ be an `EventEmitter` and implement specific
|
||||
methods. The following methods are the list of **required**, **recommended**,
|
||||
and **optional**.
|
||||
|
||||
* Required methods are ones that this module will always call on the store.
|
||||
* Recommended methods are ones that this module will call on the store if
|
||||
available.
|
||||
* Optional methods are ones this module does not call at all, but helps
|
||||
present uniform stores to users.
|
||||
|
||||
For an example implementation view the [connect-redis](http://github.com/visionmedia/connect-redis) repo.
|
||||
|
||||
### store.all(callback)
|
||||
|
||||
**Optional**
|
||||
|
||||
This optional method is used to get all sessions in the store as an array. The
|
||||
`callback` should be called as `callback(error, sessions)`.
|
||||
|
||||
### store.destroy(sid, callback)
|
||||
|
||||
**Required**
|
||||
|
||||
This required method is used to destroy/delete a session from the store given
|
||||
a session ID (`sid`). The `callback` should be called as `callback(error)` once
|
||||
the session is destroyed.
|
||||
|
||||
### store.clear(callback)
|
||||
|
||||
**Optional**
|
||||
|
||||
This optional method is used to delete all sessions from the store. The
|
||||
`callback` should be called as `callback(error)` once the store is cleared.
|
||||
|
||||
### store.length(callback)
|
||||
|
||||
**Optional**
|
||||
|
||||
This optional method is used to get the count of all sessions in the store.
|
||||
The `callback` should be called as `callback(error, len)`.
|
||||
|
||||
### store.get(sid, callback)
|
||||
|
||||
**Required**
|
||||
|
||||
This required method is used to get a session from the store given a session
|
||||
ID (`sid`). The `callback` should be called as `callback(error, session)`.
|
||||
|
||||
The `session` argument should be a session if found, otherwise `null` or
|
||||
`undefined` if the session was not found (and there was no error). A special
|
||||
case is made when `error.code === 'ENOENT'` to act like `callback(null, null)`.
|
||||
|
||||
### store.set(sid, session, callback)
|
||||
|
||||
**Required**
|
||||
|
||||
This required method is used to upsert a session into the store given a
|
||||
session ID (`sid`) and session (`session`) object. The callback should be
|
||||
called as `callback(error)` once the session has been set in the store.
|
||||
|
||||
### store.touch(sid, session, callback)
|
||||
|
||||
**Recommended**
|
||||
|
||||
This recommended method is used to "touch" a given session given a
|
||||
session ID (`sid`) and session (`session`) object. The `callback` should be
|
||||
called as `callback(error)` once the session has been touched.
|
||||
|
||||
This is primarily used when the store will automatically delete idle sessions
|
||||
and this method is used to signal to the store the given session is active,
|
||||
potentially resetting the idle timer.
|
||||
|
||||
## Compatible Session Stores
|
||||
|
||||
The following modules implement a session store that is compatible with this
|
||||
module. Please make a PR to add additional modules :)
|
||||
|
||||
[![★][aerospike-session-store-image] aerospike-session-store][aerospike-session-store-url] A session store using [Aerospike](http://www.aerospike.com/).
|
||||
|
||||
[aerospike-session-store-url]: https://www.npmjs.com/package/aerospike-session-store
|
||||
[aerospike-session-store-image]: https://badgen.net/github/stars/aerospike/aerospike-session-store-expressjs?label=%E2%98%85
|
||||
|
||||
[![★][better-sqlite3-session-store-image] better-sqlite3-session-store][better-sqlite3-session-store-url] A session store based on [better-sqlite3](https://github.com/JoshuaWise/better-sqlite3).
|
||||
|
||||
[better-sqlite3-session-store-url]: https://www.npmjs.com/package/better-sqlite3-session-store
|
||||
[better-sqlite3-session-store-image]: https://badgen.net/github/stars/timdaub/better-sqlite3-session-store?label=%E2%98%85
|
||||
|
||||
[![★][cassandra-store-image] cassandra-store][cassandra-store-url] An Apache Cassandra-based session store.
|
||||
|
||||
[cassandra-store-url]: https://www.npmjs.com/package/cassandra-store
|
||||
[cassandra-store-image]: https://badgen.net/github/stars/webcc/cassandra-store?label=%E2%98%85
|
||||
|
||||
[![★][cluster-store-image] cluster-store][cluster-store-url] A wrapper for using in-process / embedded
|
||||
stores - such as SQLite (via knex), leveldb, files, or memory - with node cluster (desirable for Raspberry Pi 2
|
||||
and other multi-core embedded devices).
|
||||
|
||||
[cluster-store-url]: https://www.npmjs.com/package/cluster-store
|
||||
[cluster-store-image]: https://badgen.net/github/stars/coolaj86/cluster-store?label=%E2%98%85
|
||||
|
||||
[![★][connect-arango-image] connect-arango][connect-arango-url] An ArangoDB-based session store.
|
||||
|
||||
[connect-arango-url]: https://www.npmjs.com/package/connect-arango
|
||||
[connect-arango-image]: https://badgen.net/github/stars/AlexanderArvidsson/connect-arango?label=%E2%98%85
|
||||
|
||||
[![★][connect-azuretables-image] connect-azuretables][connect-azuretables-url] An [Azure Table Storage](https://azure.microsoft.com/en-gb/services/storage/tables/)-based session store.
|
||||
|
||||
[connect-azuretables-url]: https://www.npmjs.com/package/connect-azuretables
|
||||
[connect-azuretables-image]: https://badgen.net/github/stars/mike-goodwin/connect-azuretables?label=%E2%98%85
|
||||
|
||||
[![★][connect-cloudant-store-image] connect-cloudant-store][connect-cloudant-store-url] An [IBM Cloudant](https://cloudant.com/)-based session store.
|
||||
|
||||
[connect-cloudant-store-url]: https://www.npmjs.com/package/connect-cloudant-store
|
||||
[connect-cloudant-store-image]: https://badgen.net/github/stars/adriantanasa/connect-cloudant-store?label=%E2%98%85
|
||||
|
||||
[![★][connect-couchbase-image] connect-couchbase][connect-couchbase-url] A [couchbase](http://www.couchbase.com/)-based session store.
|
||||
|
||||
[connect-couchbase-url]: https://www.npmjs.com/package/connect-couchbase
|
||||
[connect-couchbase-image]: https://badgen.net/github/stars/christophermina/connect-couchbase?label=%E2%98%85
|
||||
|
||||
[![★][connect-datacache-image] connect-datacache][connect-datacache-url] An [IBM Bluemix Data Cache](http://www.ibm.com/cloud-computing/bluemix/)-based session store.
|
||||
|
||||
[connect-datacache-url]: https://www.npmjs.com/package/connect-datacache
|
||||
[connect-datacache-image]: https://badgen.net/github/stars/adriantanasa/connect-datacache?label=%E2%98%85
|
||||
|
||||
[![★][@google-cloud/connect-datastore-image] @google-cloud/connect-datastore][@google-cloud/connect-datastore-url] A [Google Cloud Datastore](https://cloud.google.com/datastore/docs/concepts/overview)-based session store.
|
||||
|
||||
[@google-cloud/connect-datastore-url]: https://www.npmjs.com/package/@google-cloud/connect-datastore
|
||||
[@google-cloud/connect-datastore-image]: https://badgen.net/github/stars/GoogleCloudPlatform/cloud-datastore-session-node?label=%E2%98%85
|
||||
|
||||
[![★][connect-db2-image] connect-db2][connect-db2-url] An IBM DB2-based session store built using [ibm_db](https://www.npmjs.com/package/ibm_db) module.
|
||||
|
||||
[connect-db2-url]: https://www.npmjs.com/package/connect-db2
|
||||
[connect-db2-image]: https://badgen.net/github/stars/wallali/connect-db2?label=%E2%98%85
|
||||
|
||||
[![★][connect-dynamodb-image] connect-dynamodb][connect-dynamodb-url] A DynamoDB-based session store.
|
||||
|
||||
[connect-dynamodb-url]: https://www.npmjs.com/package/connect-dynamodb
|
||||
[connect-dynamodb-image]: https://badgen.net/github/stars/ca98am79/connect-dynamodb?label=%E2%98%85
|
||||
|
||||
[![★][@google-cloud/connect-firestore-image] @google-cloud/connect-firestore][@google-cloud/connect-firestore-url] A [Google Cloud Firestore](https://cloud.google.com/firestore/docs/overview)-based session store.
|
||||
|
||||
[@google-cloud/connect-firestore-url]: https://www.npmjs.com/package/@google-cloud/connect-firestore
|
||||
[@google-cloud/connect-firestore-image]: https://badgen.net/github/stars/googleapis/nodejs-firestore-session?label=%E2%98%85
|
||||
|
||||
[![★][connect-hazelcast-image] connect-hazelcast][connect-hazelcast-url] Hazelcast session store for Connect and Express.
|
||||
|
||||
[connect-hazelcast-url]: https://www.npmjs.com/package/connect-hazelcast
|
||||
[connect-hazelcast-image]: https://badgen.net/github/stars/huseyinbabal/connect-hazelcast?label=%E2%98%85
|
||||
|
||||
[![★][connect-loki-image] connect-loki][connect-loki-url] A Loki.js-based session store.
|
||||
|
||||
[connect-loki-url]: https://www.npmjs.com/package/connect-loki
|
||||
[connect-loki-image]: https://badgen.net/github/stars/Requarks/connect-loki?label=%E2%98%85
|
||||
|
||||
[![★][connect-lowdb-image] connect-lowdb][connect-lowdb-url] A lowdb-based session store.
|
||||
|
||||
[connect-lowdb-url]: https://www.npmjs.com/package/connect-lowdb
|
||||
[connect-lowdb-image]: https://badgen.net/github/stars/travishorn/connect-lowdb?label=%E2%98%85
|
||||
|
||||
[![★][connect-memcached-image] connect-memcached][connect-memcached-url] A memcached-based session store.
|
||||
|
||||
[connect-memcached-url]: https://www.npmjs.com/package/connect-memcached
|
||||
[connect-memcached-image]: https://badgen.net/github/stars/balor/connect-memcached?label=%E2%98%85
|
||||
|
||||
[![★][connect-memjs-image] connect-memjs][connect-memjs-url] A memcached-based session store using
|
||||
[memjs](https://www.npmjs.com/package/memjs) as the memcached client.
|
||||
|
||||
[connect-memjs-url]: https://www.npmjs.com/package/connect-memjs
|
||||
[connect-memjs-image]: https://badgen.net/github/stars/liamdon/connect-memjs?label=%E2%98%85
|
||||
|
||||
[![★][connect-ml-image] connect-ml][connect-ml-url] A MarkLogic Server-based session store.
|
||||
|
||||
[connect-ml-url]: https://www.npmjs.com/package/connect-ml
|
||||
[connect-ml-image]: https://badgen.net/github/stars/bluetorch/connect-ml?label=%E2%98%85
|
||||
|
||||
[![★][connect-monetdb-image] connect-monetdb][connect-monetdb-url] A MonetDB-based session store.
|
||||
|
||||
[connect-monetdb-url]: https://www.npmjs.com/package/connect-monetdb
|
||||
[connect-monetdb-image]: https://badgen.net/github/stars/MonetDB/npm-connect-monetdb?label=%E2%98%85
|
||||
|
||||
[![★][connect-mongo-image] connect-mongo][connect-mongo-url] A MongoDB-based session store.
|
||||
|
||||
[connect-mongo-url]: https://www.npmjs.com/package/connect-mongo
|
||||
[connect-mongo-image]: https://badgen.net/github/stars/kcbanner/connect-mongo?label=%E2%98%85
|
||||
|
||||
[![★][connect-mongodb-session-image] connect-mongodb-session][connect-mongodb-session-url] Lightweight MongoDB-based session store built and maintained by MongoDB.
|
||||
|
||||
[connect-mongodb-session-url]: https://www.npmjs.com/package/connect-mongodb-session
|
||||
[connect-mongodb-session-image]: https://badgen.net/github/stars/mongodb-js/connect-mongodb-session?label=%E2%98%85
|
||||
|
||||
[![★][connect-mssql-v2-image] connect-mssql-v2][connect-mssql-v2-url] A Microsoft SQL Server-based session store based on [connect-mssql](https://www.npmjs.com/package/connect-mssql).
|
||||
|
||||
[connect-mssql-v2-url]: https://www.npmjs.com/package/connect-mssql-v2
|
||||
[connect-mssql-v2-image]: https://badgen.net/github/stars/jluboff/connect-mssql-v2?label=%E2%98%85
|
||||
|
||||
[![★][connect-neo4j-image] connect-neo4j][connect-neo4j-url] A [Neo4j](https://neo4j.com)-based session store.
|
||||
|
||||
[connect-neo4j-url]: https://www.npmjs.com/package/connect-neo4j
|
||||
[connect-neo4j-image]: https://badgen.net/github/stars/MaxAndersson/connect-neo4j?label=%E2%98%85
|
||||
|
||||
[![★][connect-pg-simple-image] connect-pg-simple][connect-pg-simple-url] A PostgreSQL-based session store.
|
||||
|
||||
[connect-pg-simple-url]: https://www.npmjs.com/package/connect-pg-simple
|
||||
[connect-pg-simple-image]: https://badgen.net/github/stars/voxpelli/node-connect-pg-simple?label=%E2%98%85
|
||||
|
||||
[![★][connect-redis-image] connect-redis][connect-redis-url] A Redis-based session store.
|
||||
|
||||
[connect-redis-url]: https://www.npmjs.com/package/connect-redis
|
||||
[connect-redis-image]: https://badgen.net/github/stars/tj/connect-redis?label=%E2%98%85
|
||||
|
||||
[![★][connect-session-firebase-image] connect-session-firebase][connect-session-firebase-url] A session store based on the [Firebase Realtime Database](https://firebase.google.com/docs/database/)
|
||||
|
||||
[connect-session-firebase-url]: https://www.npmjs.com/package/connect-session-firebase
|
||||
[connect-session-firebase-image]: https://badgen.net/github/stars/benweier/connect-session-firebase?label=%E2%98%85
|
||||
|
||||
[![★][connect-session-knex-image] connect-session-knex][connect-session-knex-url] A session store using
|
||||
[Knex.js](http://knexjs.org/), which is a SQL query builder for PostgreSQL, MySQL, MariaDB, SQLite3, and Oracle.
|
||||
|
||||
[connect-session-knex-url]: https://www.npmjs.com/package/connect-session-knex
|
||||
[connect-session-knex-image]: https://badgen.net/github/stars/llambda/connect-session-knex?label=%E2%98%85
|
||||
|
||||
[![★][connect-session-sequelize-image] connect-session-sequelize][connect-session-sequelize-url] A session store using
|
||||
[Sequelize.js](http://sequelizejs.com/), which is a Node.js / io.js ORM for PostgreSQL, MySQL, SQLite and MSSQL.
|
||||
|
||||
[connect-session-sequelize-url]: https://www.npmjs.com/package/connect-session-sequelize
|
||||
[connect-session-sequelize-image]: https://badgen.net/github/stars/mweibel/connect-session-sequelize?label=%E2%98%85
|
||||
|
||||
[![★][connect-sqlite3-image] connect-sqlite3][connect-sqlite3-url] A [SQLite3](https://github.com/mapbox/node-sqlite3) session store modeled after the TJ's `connect-redis` store.
|
||||
|
||||
[connect-sqlite3-url]: https://www.npmjs.com/package/connect-sqlite3
|
||||
[connect-sqlite3-image]: https://badgen.net/github/stars/rawberg/connect-sqlite3?label=%E2%98%85
|
||||
|
||||
[![★][connect-typeorm-image] connect-typeorm][connect-typeorm-url] A [TypeORM](https://github.com/typeorm/typeorm)-based session store.
|
||||
|
||||
[connect-typeorm-url]: https://www.npmjs.com/package/connect-typeorm
|
||||
[connect-typeorm-image]: https://badgen.net/github/stars/makepost/connect-typeorm?label=%E2%98%85
|
||||
|
||||
[![★][couchdb-expression-image] couchdb-expression][couchdb-expression-url] A [CouchDB](https://couchdb.apache.org/)-based session store.
|
||||
|
||||
[couchdb-expression-url]: https://www.npmjs.com/package/couchdb-expression
|
||||
[couchdb-expression-image]: https://badgen.net/github/stars/tkshnwesper/couchdb-expression?label=%E2%98%85
|
||||
|
||||
[![★][dynamodb-store-image] dynamodb-store][dynamodb-store-url] A DynamoDB-based session store.
|
||||
|
||||
[dynamodb-store-url]: https://www.npmjs.com/package/dynamodb-store
|
||||
[dynamodb-store-image]: https://badgen.net/github/stars/rafaelrpinto/dynamodb-store?label=%E2%98%85
|
||||
|
||||
[![★][express-etcd-image] express-etcd][express-etcd-url] An [etcd](https://github.com/stianeikeland/node-etcd) based session store.
|
||||
|
||||
[express-etcd-url]: https://www.npmjs.com/package/express-etcd
|
||||
[express-etcd-image]: https://badgen.net/github/stars/gildean/express-etcd?label=%E2%98%85
|
||||
|
||||
[![★][express-mysql-session-image] express-mysql-session][express-mysql-session-url] A session store using native
|
||||
[MySQL](https://www.mysql.com/) via the [node-mysql](https://github.com/felixge/node-mysql) module.
|
||||
|
||||
[express-mysql-session-url]: https://www.npmjs.com/package/express-mysql-session
|
||||
[express-mysql-session-image]: https://badgen.net/github/stars/chill117/express-mysql-session?label=%E2%98%85
|
||||
|
||||
[![★][express-nedb-session-image] express-nedb-session][express-nedb-session-url] A NeDB-based session store.
|
||||
|
||||
[express-nedb-session-url]: https://www.npmjs.com/package/express-nedb-session
|
||||
[express-nedb-session-image]: https://badgen.net/github/stars/louischatriot/express-nedb-session?label=%E2%98%85
|
||||
|
||||
[![★][express-oracle-session-image] express-oracle-session][express-oracle-session-url] A session store using native
|
||||
[oracle](https://www.oracle.com/) via the [node-oracledb](https://www.npmjs.com/package/oracledb) module.
|
||||
|
||||
[express-oracle-session-url]: https://www.npmjs.com/package/express-oracle-session
|
||||
[express-oracle-session-image]: https://badgen.net/github/stars/slumber86/express-oracle-session?label=%E2%98%85
|
||||
|
||||
[![★][express-session-cache-manager-image] express-session-cache-manager][express-session-cache-manager-url]
|
||||
A store that implements [cache-manager](https://www.npmjs.com/package/cache-manager), which supports
|
||||
a [variety of storage types](https://www.npmjs.com/package/cache-manager#store-engines).
|
||||
|
||||
[express-session-cache-manager-url]: https://www.npmjs.com/package/express-session-cache-manager
|
||||
[express-session-cache-manager-image]: https://badgen.net/github/stars/theogravity/express-session-cache-manager?label=%E2%98%85
|
||||
|
||||
[![★][express-session-etcd3-image] express-session-etcd3][express-session-etcd3-url] An [etcd3](https://github.com/mixer/etcd3) based session store.
|
||||
|
||||
[express-session-etcd3-url]: https://www.npmjs.com/package/express-session-etcd3
|
||||
[express-session-etcd3-image]: https://badgen.net/github/stars/willgm/express-session-etcd3?label=%E2%98%85
|
||||
|
||||
[![★][express-session-level-image] express-session-level][express-session-level-url] A [LevelDB](https://github.com/Level/levelup) based session store.
|
||||
|
||||
[express-session-level-url]: https://www.npmjs.com/package/express-session-level
|
||||
[express-session-level-image]: https://badgen.net/github/stars/tgohn/express-session-level?label=%E2%98%85
|
||||
|
||||
[![★][express-session-rsdb-image] express-session-rsdb][express-session-rsdb-url] Session store based on Rocket-Store: A very simple, super fast and yet powerfull, flat file database.
|
||||
|
||||
[express-session-rsdb-url]: https://www.npmjs.com/package/express-session-rsdb
|
||||
[express-session-rsdb-image]: https://badgen.net/github/stars/paragi/express-session-rsdb?label=%E2%98%85
|
||||
|
||||
[![★][express-sessions-image] express-sessions][express-sessions-url] A session store supporting both MongoDB and Redis.
|
||||
|
||||
[express-sessions-url]: https://www.npmjs.com/package/express-sessions
|
||||
[express-sessions-image]: https://badgen.net/github/stars/konteck/express-sessions?label=%E2%98%85
|
||||
|
||||
[![★][firestore-store-image] firestore-store][firestore-store-url] A [Firestore](https://github.com/hendrysadrak/firestore-store)-based session store.
|
||||
|
||||
[firestore-store-url]: https://www.npmjs.com/package/firestore-store
|
||||
[firestore-store-image]: https://badgen.net/github/stars/hendrysadrak/firestore-store?label=%E2%98%85
|
||||
|
||||
[![★][fortune-session-image] fortune-session][fortune-session-url] A [Fortune.js](https://github.com/fortunejs/fortune)
|
||||
based session store. Supports all backends supported by Fortune (MongoDB, Redis, Postgres, NeDB).
|
||||
|
||||
[fortune-session-url]: https://www.npmjs.com/package/fortune-session
|
||||
[fortune-session-image]: https://badgen.net/github/stars/aliceklipper/fortune-session?label=%E2%98%85
|
||||
|
||||
[![★][hazelcast-store-image] hazelcast-store][hazelcast-store-url] A Hazelcast-based session store built on the [Hazelcast Node Client](https://www.npmjs.com/package/hazelcast-client).
|
||||
|
||||
[hazelcast-store-url]: https://www.npmjs.com/package/hazelcast-store
|
||||
[hazelcast-store-image]: https://badgen.net/github/stars/jackspaniel/hazelcast-store?label=%E2%98%85
|
||||
|
||||
[![★][level-session-store-image] level-session-store][level-session-store-url] A LevelDB-based session store.
|
||||
|
||||
[level-session-store-url]: https://www.npmjs.com/package/level-session-store
|
||||
[level-session-store-image]: https://badgen.net/github/stars/toddself/level-session-store?label=%E2%98%85
|
||||
|
||||
[![★][lowdb-session-store-image] lowdb-session-store][lowdb-session-store-url] A [lowdb](https://www.npmjs.com/package/lowdb)-based session store.
|
||||
|
||||
[lowdb-session-store-url]: https://www.npmjs.com/package/lowdb-session-store
|
||||
[lowdb-session-store-image]: https://badgen.net/github/stars/fhellwig/lowdb-session-store?label=%E2%98%85
|
||||
|
||||
[![★][medea-session-store-image] medea-session-store][medea-session-store-url] A Medea-based session store.
|
||||
|
||||
[medea-session-store-url]: https://www.npmjs.com/package/medea-session-store
|
||||
[medea-session-store-image]: https://badgen.net/github/stars/BenjaminVadant/medea-session-store?label=%E2%98%85
|
||||
|
||||
[![★][memorystore-image] memorystore][memorystore-url] A memory session store made for production.
|
||||
|
||||
[memorystore-url]: https://www.npmjs.com/package/memorystore
|
||||
[memorystore-image]: https://badgen.net/github/stars/roccomuso/memorystore?label=%E2%98%85
|
||||
|
||||
[![★][mssql-session-store-image] mssql-session-store][mssql-session-store-url] A SQL Server-based session store.
|
||||
|
||||
[mssql-session-store-url]: https://www.npmjs.com/package/mssql-session-store
|
||||
[mssql-session-store-image]: https://badgen.net/github/stars/jwathen/mssql-session-store?label=%E2%98%85
|
||||
|
||||
[![★][nedb-session-store-image] nedb-session-store][nedb-session-store-url] An alternate NeDB-based (either in-memory or file-persisted) session store.
|
||||
|
||||
[nedb-session-store-url]: https://www.npmjs.com/package/nedb-session-store
|
||||
[nedb-session-store-image]: https://badgen.net/github/stars/JamesMGreene/nedb-session-store?label=%E2%98%85
|
||||
|
||||
[![★][@quixo3/prisma-session-store-image] @quixo3/prisma-session-store][@quixo3/prisma-session-store-url] A session store for the [Prisma Framework](https://www.prisma.io).
|
||||
|
||||
[@quixo3/prisma-session-store-url]: https://www.npmjs.com/package/@quixo3/prisma-session-store
|
||||
[@quixo3/prisma-session-store-image]: https://badgen.net/github/stars/kleydon/prisma-session-store?label=%E2%98%85
|
||||
|
||||
[![★][restsession-image] restsession][restsession-url] Store sessions utilizing a RESTful API
|
||||
|
||||
[restsession-url]: https://www.npmjs.com/package/restsession
|
||||
[restsession-image]: https://badgen.net/github/stars/jankal/restsession?label=%E2%98%85
|
||||
|
||||
[![★][sequelstore-connect-image] sequelstore-connect][sequelstore-connect-url] A session store using [Sequelize.js](http://sequelizejs.com/).
|
||||
|
||||
[sequelstore-connect-url]: https://www.npmjs.com/package/sequelstore-connect
|
||||
[sequelstore-connect-image]: https://badgen.net/github/stars/MattMcFarland/sequelstore-connect?label=%E2%98%85
|
||||
|
||||
[![★][session-file-store-image] session-file-store][session-file-store-url] A file system-based session store.
|
||||
|
||||
[session-file-store-url]: https://www.npmjs.com/package/session-file-store
|
||||
[session-file-store-image]: https://badgen.net/github/stars/valery-barysok/session-file-store?label=%E2%98%85
|
||||
|
||||
[![★][session-pouchdb-store-image] session-pouchdb-store][session-pouchdb-store-url] Session store for PouchDB / CouchDB. Accepts embedded, custom, or remote PouchDB instance and realtime synchronization.
|
||||
|
||||
[session-pouchdb-store-url]: https://www.npmjs.com/package/session-pouchdb-store
|
||||
[session-pouchdb-store-image]: https://badgen.net/github/stars/solzimer/session-pouchdb-store?label=%E2%98%85
|
||||
|
||||
[![★][session-rethinkdb-image] session-rethinkdb][session-rethinkdb-url] A [RethinkDB](http://rethinkdb.com/)-based session store.
|
||||
|
||||
[session-rethinkdb-url]: https://www.npmjs.com/package/session-rethinkdb
|
||||
[session-rethinkdb-image]: https://badgen.net/github/stars/llambda/session-rethinkdb?label=%E2%98%85
|
||||
|
||||
[![★][@databunker/session-store-image] @databunker/session-store][@databunker/session-store-url] A [Databunker](https://databunker.org/)-based encrypted session store.
|
||||
|
||||
[@databunker/session-store-url]: https://www.npmjs.com/package/@databunker/session-store
|
||||
[@databunker/session-store-image]: https://badgen.net/github/stars/securitybunker/databunker-session-store?label=%E2%98%85
|
||||
|
||||
[![★][sessionstore-image] sessionstore][sessionstore-url] A session store that works with various databases.
|
||||
|
||||
[sessionstore-url]: https://www.npmjs.com/package/sessionstore
|
||||
[sessionstore-image]: https://badgen.net/github/stars/adrai/sessionstore?label=%E2%98%85
|
||||
|
||||
[![★][tch-nedb-session-image] tch-nedb-session][tch-nedb-session-url] A file system session store based on NeDB.
|
||||
|
||||
[tch-nedb-session-url]: https://www.npmjs.com/package/tch-nedb-session
|
||||
[tch-nedb-session-image]: https://badgen.net/github/stars/tomaschyly/NeDBSession?label=%E2%98%85
|
||||
|
||||
## Examples
|
||||
|
||||
### View counter
|
||||
|
||||
A simple example using `express-session` to store page views for a user.
|
||||
|
||||
```js
|
||||
var express = require('express')
|
||||
var parseurl = require('parseurl')
|
||||
var session = require('express-session')
|
||||
|
||||
var app = express()
|
||||
|
||||
app.use(session({
|
||||
secret: 'keyboard cat',
|
||||
resave: false,
|
||||
saveUninitialized: true
|
||||
}))
|
||||
|
||||
app.use(function (req, res, next) {
|
||||
if (!req.session.views) {
|
||||
req.session.views = {}
|
||||
}
|
||||
|
||||
// get the url pathname
|
||||
var pathname = parseurl(req).pathname
|
||||
|
||||
// count the views
|
||||
req.session.views[pathname] = (req.session.views[pathname] || 0) + 1
|
||||
|
||||
next()
|
||||
})
|
||||
|
||||
app.get('/foo', function (req, res, next) {
|
||||
res.send('you viewed this page ' + req.session.views['/foo'] + ' times')
|
||||
})
|
||||
|
||||
app.get('/bar', function (req, res, next) {
|
||||
res.send('you viewed this page ' + req.session.views['/bar'] + ' times')
|
||||
})
|
||||
|
||||
app.listen(3000)
|
||||
```
|
||||
|
||||
### User login
|
||||
|
||||
A simple example using `express-session` to keep a user log in session.
|
||||
|
||||
```js
|
||||
var escapeHtml = require('escape-html')
|
||||
var express = require('express')
|
||||
var session = require('express-session')
|
||||
|
||||
var app = express()
|
||||
|
||||
app.use(session({
|
||||
secret: 'keyboard cat',
|
||||
resave: false,
|
||||
saveUninitialized: true
|
||||
}))
|
||||
|
||||
// middleware to test if authenticated
|
||||
function isAuthenticated (req, res, next) {
|
||||
if (req.session.user) next()
|
||||
else next('route')
|
||||
}
|
||||
|
||||
app.get('/', isAuthenticated, function (req, res) {
|
||||
// this is only called when there is an authentication user due to isAuthenticated
|
||||
res.send('hello, ' + escapeHtml(req.session.user) + '!' +
|
||||
' <a href="/logout">Logout</a>')
|
||||
})
|
||||
|
||||
app.get('/', function (req, res) {
|
||||
res.send('<form action="/login" method="post">' +
|
||||
'Username: <input name="user"><br>' +
|
||||
'Password: <input name="pass" type="password"><br>' +
|
||||
'<input type="submit" text="Login"></form>')
|
||||
})
|
||||
|
||||
app.post('/login', express.urlencoded({ extended: false }), function (req, res) {
|
||||
// login logic to validate req.body.user and req.body.pass
|
||||
// would be implemented here. for this example any combo works
|
||||
|
||||
// regenerate the session, which is good practice to help
|
||||
// guard against forms of session fixation
|
||||
req.session.regenerate(function (err) {
|
||||
if (err) next(err)
|
||||
|
||||
// store user information in session, typically a user id
|
||||
req.session.user = req.body.user
|
||||
|
||||
// save the session before redirection to ensure page
|
||||
// load does not happen before session is saved
|
||||
req.session.save(function (err) {
|
||||
if (err) return next(err)
|
||||
res.redirect('/')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
app.get('/logout', function (req, res, next) {
|
||||
// logout logic
|
||||
|
||||
// clear the user from the session object and save.
|
||||
// this will ensure that re-using the old session id
|
||||
// does not have a logged in user
|
||||
req.session.user = null
|
||||
req.session.save(function (err) {
|
||||
if (err) next(err)
|
||||
|
||||
// regenerate the session, which is good practice to help
|
||||
// guard against forms of session fixation
|
||||
req.session.regenerate(function (err) {
|
||||
if (err) next(err)
|
||||
res.redirect('/')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
app.listen(3000)
|
||||
```
|
||||
|
||||
## Debugging
|
||||
|
||||
This module uses the [debug](https://www.npmjs.com/package/debug) module
|
||||
internally to log information about session operations.
|
||||
|
||||
To see all the internal logs, set the `DEBUG` environment variable to
|
||||
`express-session` when launching your app (`npm start`, in this example):
|
||||
|
||||
```sh
|
||||
$ DEBUG=express-session npm start
|
||||
```
|
||||
|
||||
On Windows, use the corresponding command;
|
||||
|
||||
```sh
|
||||
> set DEBUG=express-session & npm start
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
[rfc-6265bis-03-4.1.2.7]: https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.2.7
|
||||
[ci-image]: https://badgen.net/github/checks/expressjs/session/master?label=ci
|
||||
[ci-url]: https://github.com/expressjs/session/actions?query=workflow%3Aci
|
||||
[coveralls-image]: https://badgen.net/coveralls/c/github/expressjs/session/master
|
||||
[coveralls-url]: https://coveralls.io/r/expressjs/session?branch=master
|
||||
[node-url]: https://nodejs.org/en/download
|
||||
[npm-downloads-image]: https://badgen.net/npm/dm/express-session
|
||||
[npm-url]: https://npmjs.org/package/express-session
|
||||
[npm-version-image]: https://badgen.net/npm/v/express-session
|
|
@ -0,0 +1,685 @@
|
|||
/*!
|
||||
* express-session
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* Copyright(c) 2014-2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var Buffer = require('safe-buffer').Buffer
|
||||
var cookie = require('cookie');
|
||||
var crypto = require('crypto')
|
||||
var debug = require('debug')('express-session');
|
||||
var deprecate = require('depd')('express-session');
|
||||
var onHeaders = require('on-headers')
|
||||
var parseUrl = require('parseurl');
|
||||
var signature = require('cookie-signature')
|
||||
var uid = require('uid-safe').sync
|
||||
|
||||
var Cookie = require('./session/cookie')
|
||||
var MemoryStore = require('./session/memory')
|
||||
var Session = require('./session/session')
|
||||
var Store = require('./session/store')
|
||||
|
||||
// environment
|
||||
|
||||
var env = process.env.NODE_ENV;
|
||||
|
||||
/**
|
||||
* Expose the middleware.
|
||||
*/
|
||||
|
||||
exports = module.exports = session;
|
||||
|
||||
/**
|
||||
* Expose constructors.
|
||||
*/
|
||||
|
||||
exports.Store = Store;
|
||||
exports.Cookie = Cookie;
|
||||
exports.Session = Session;
|
||||
exports.MemoryStore = MemoryStore;
|
||||
|
||||
/**
|
||||
* Warning message for `MemoryStore` usage in production.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var warning = 'Warning: connect.session() MemoryStore is not\n'
|
||||
+ 'designed for a production environment, as it will leak\n'
|
||||
+ 'memory, and will not scale past a single process.';
|
||||
|
||||
/**
|
||||
* Node.js 0.8+ async implementation.
|
||||
* @private
|
||||
*/
|
||||
|
||||
/* istanbul ignore next */
|
||||
var defer = typeof setImmediate === 'function'
|
||||
? setImmediate
|
||||
: function(fn){ process.nextTick(fn.bind.apply(fn, arguments)) }
|
||||
|
||||
/**
|
||||
* Setup session store with the given `options`.
|
||||
*
|
||||
* @param {Object} [options]
|
||||
* @param {Object} [options.cookie] Options for cookie
|
||||
* @param {Function} [options.genid]
|
||||
* @param {String} [options.name=connect.sid] Session ID cookie name
|
||||
* @param {Boolean} [options.proxy]
|
||||
* @param {Boolean} [options.resave] Resave unmodified sessions back to the store
|
||||
* @param {Boolean} [options.rolling] Enable/disable rolling session expiration
|
||||
* @param {Boolean} [options.saveUninitialized] Save uninitialized sessions to the store
|
||||
* @param {String|Array} [options.secret] Secret for signing session ID
|
||||
* @param {Object} [options.store=MemoryStore] Session store
|
||||
* @param {String} [options.unset]
|
||||
* @return {Function} middleware
|
||||
* @public
|
||||
*/
|
||||
|
||||
function session(options) {
|
||||
var opts = options || {}
|
||||
|
||||
// get the cookie options
|
||||
var cookieOptions = opts.cookie || {}
|
||||
|
||||
// get the session id generate function
|
||||
var generateId = opts.genid || generateSessionId
|
||||
|
||||
// get the session cookie name
|
||||
var name = opts.name || opts.key || 'connect.sid'
|
||||
|
||||
// get the session store
|
||||
var store = opts.store || new MemoryStore()
|
||||
|
||||
// get the trust proxy setting
|
||||
var trustProxy = opts.proxy
|
||||
|
||||
// get the resave session option
|
||||
var resaveSession = opts.resave;
|
||||
|
||||
// get the rolling session option
|
||||
var rollingSessions = Boolean(opts.rolling)
|
||||
|
||||
// get the save uninitialized session option
|
||||
var saveUninitializedSession = opts.saveUninitialized
|
||||
|
||||
// get the cookie signing secret
|
||||
var secret = opts.secret
|
||||
|
||||
if (typeof generateId !== 'function') {
|
||||
throw new TypeError('genid option must be a function');
|
||||
}
|
||||
|
||||
if (resaveSession === undefined) {
|
||||
deprecate('undefined resave option; provide resave option');
|
||||
resaveSession = true;
|
||||
}
|
||||
|
||||
if (saveUninitializedSession === undefined) {
|
||||
deprecate('undefined saveUninitialized option; provide saveUninitialized option');
|
||||
saveUninitializedSession = true;
|
||||
}
|
||||
|
||||
if (opts.unset && opts.unset !== 'destroy' && opts.unset !== 'keep') {
|
||||
throw new TypeError('unset option must be "destroy" or "keep"');
|
||||
}
|
||||
|
||||
// TODO: switch to "destroy" on next major
|
||||
var unsetDestroy = opts.unset === 'destroy'
|
||||
|
||||
if (Array.isArray(secret) && secret.length === 0) {
|
||||
throw new TypeError('secret option array must contain one or more strings');
|
||||
}
|
||||
|
||||
if (secret && !Array.isArray(secret)) {
|
||||
secret = [secret];
|
||||
}
|
||||
|
||||
if (!secret) {
|
||||
deprecate('req.secret; provide secret option');
|
||||
}
|
||||
|
||||
// notify user that this store is not
|
||||
// meant for a production environment
|
||||
/* istanbul ignore next: not tested */
|
||||
if (env === 'production' && store instanceof MemoryStore) {
|
||||
console.warn(warning);
|
||||
}
|
||||
|
||||
// generates the new session
|
||||
store.generate = function(req){
|
||||
req.sessionID = generateId(req);
|
||||
req.session = new Session(req);
|
||||
req.session.cookie = new Cookie(cookieOptions);
|
||||
|
||||
if (cookieOptions.secure === 'auto') {
|
||||
req.session.cookie.secure = issecure(req, trustProxy);
|
||||
}
|
||||
};
|
||||
|
||||
var storeImplementsTouch = typeof store.touch === 'function';
|
||||
|
||||
// register event listeners for the store to track readiness
|
||||
var storeReady = true
|
||||
store.on('disconnect', function ondisconnect() {
|
||||
storeReady = false
|
||||
})
|
||||
store.on('connect', function onconnect() {
|
||||
storeReady = true
|
||||
})
|
||||
|
||||
return function session(req, res, next) {
|
||||
// self-awareness
|
||||
if (req.session) {
|
||||
next()
|
||||
return
|
||||
}
|
||||
|
||||
// Handle connection as if there is no session if
|
||||
// the store has temporarily disconnected etc
|
||||
if (!storeReady) {
|
||||
debug('store is disconnected')
|
||||
next()
|
||||
return
|
||||
}
|
||||
|
||||
// pathname mismatch
|
||||
var originalPath = parseUrl.original(req).pathname || '/'
|
||||
if (originalPath.indexOf(cookieOptions.path || '/') !== 0) return next();
|
||||
|
||||
// ensure a secret is available or bail
|
||||
if (!secret && !req.secret) {
|
||||
next(new Error('secret option required for sessions'));
|
||||
return;
|
||||
}
|
||||
|
||||
// backwards compatibility for signed cookies
|
||||
// req.secret is passed from the cookie parser middleware
|
||||
var secrets = secret || [req.secret];
|
||||
|
||||
var originalHash;
|
||||
var originalId;
|
||||
var savedHash;
|
||||
var touched = false
|
||||
|
||||
// expose store
|
||||
req.sessionStore = store;
|
||||
|
||||
// get the session ID from the cookie
|
||||
var cookieId = req.sessionID = getcookie(req, name, secrets);
|
||||
|
||||
// set-cookie
|
||||
onHeaders(res, function(){
|
||||
if (!req.session) {
|
||||
debug('no session');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!shouldSetCookie(req)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// only send secure cookies via https
|
||||
if (req.session.cookie.secure && !issecure(req, trustProxy)) {
|
||||
debug('not secured');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!touched) {
|
||||
// touch session
|
||||
req.session.touch()
|
||||
touched = true
|
||||
}
|
||||
|
||||
// set cookie
|
||||
setcookie(res, name, req.sessionID, secrets[0], req.session.cookie.data);
|
||||
});
|
||||
|
||||
// proxy end() to commit the session
|
||||
var _end = res.end;
|
||||
var _write = res.write;
|
||||
var ended = false;
|
||||
res.end = function end(chunk, encoding) {
|
||||
if (ended) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ended = true;
|
||||
|
||||
var ret;
|
||||
var sync = true;
|
||||
|
||||
function writeend() {
|
||||
if (sync) {
|
||||
ret = _end.call(res, chunk, encoding);
|
||||
sync = false;
|
||||
return;
|
||||
}
|
||||
|
||||
_end.call(res);
|
||||
}
|
||||
|
||||
function writetop() {
|
||||
if (!sync) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (!res._header) {
|
||||
res._implicitHeader()
|
||||
}
|
||||
|
||||
if (chunk == null) {
|
||||
ret = true;
|
||||
return ret;
|
||||
}
|
||||
|
||||
var contentLength = Number(res.getHeader('Content-Length'));
|
||||
|
||||
if (!isNaN(contentLength) && contentLength > 0) {
|
||||
// measure chunk
|
||||
chunk = !Buffer.isBuffer(chunk)
|
||||
? Buffer.from(chunk, encoding)
|
||||
: chunk;
|
||||
encoding = undefined;
|
||||
|
||||
if (chunk.length !== 0) {
|
||||
debug('split response');
|
||||
ret = _write.call(res, chunk.slice(0, chunk.length - 1));
|
||||
chunk = chunk.slice(chunk.length - 1, chunk.length);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
ret = _write.call(res, chunk, encoding);
|
||||
sync = false;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (shouldDestroy(req)) {
|
||||
// destroy session
|
||||
debug('destroying');
|
||||
store.destroy(req.sessionID, function ondestroy(err) {
|
||||
if (err) {
|
||||
defer(next, err);
|
||||
}
|
||||
|
||||
debug('destroyed');
|
||||
writeend();
|
||||
});
|
||||
|
||||
return writetop();
|
||||
}
|
||||
|
||||
// no session to save
|
||||
if (!req.session) {
|
||||
debug('no session');
|
||||
return _end.call(res, chunk, encoding);
|
||||
}
|
||||
|
||||
if (!touched) {
|
||||
// touch session
|
||||
req.session.touch()
|
||||
touched = true
|
||||
}
|
||||
|
||||
if (shouldSave(req)) {
|
||||
req.session.save(function onsave(err) {
|
||||
if (err) {
|
||||
defer(next, err);
|
||||
}
|
||||
|
||||
writeend();
|
||||
});
|
||||
|
||||
return writetop();
|
||||
} else if (storeImplementsTouch && shouldTouch(req)) {
|
||||
// store implements touch method
|
||||
debug('touching');
|
||||
store.touch(req.sessionID, req.session, function ontouch(err) {
|
||||
if (err) {
|
||||
defer(next, err);
|
||||
}
|
||||
|
||||
debug('touched');
|
||||
writeend();
|
||||
});
|
||||
|
||||
return writetop();
|
||||
}
|
||||
|
||||
return _end.call(res, chunk, encoding);
|
||||
};
|
||||
|
||||
// generate the session
|
||||
function generate() {
|
||||
store.generate(req);
|
||||
originalId = req.sessionID;
|
||||
originalHash = hash(req.session);
|
||||
wrapmethods(req.session);
|
||||
}
|
||||
|
||||
// inflate the session
|
||||
function inflate (req, sess) {
|
||||
store.createSession(req, sess)
|
||||
originalId = req.sessionID
|
||||
originalHash = hash(sess)
|
||||
|
||||
if (!resaveSession) {
|
||||
savedHash = originalHash
|
||||
}
|
||||
|
||||
wrapmethods(req.session)
|
||||
}
|
||||
|
||||
function rewrapmethods (sess, callback) {
|
||||
return function () {
|
||||
if (req.session !== sess) {
|
||||
wrapmethods(req.session)
|
||||
}
|
||||
|
||||
callback.apply(this, arguments)
|
||||
}
|
||||
}
|
||||
|
||||
// wrap session methods
|
||||
function wrapmethods(sess) {
|
||||
var _reload = sess.reload
|
||||
var _save = sess.save;
|
||||
|
||||
function reload(callback) {
|
||||
debug('reloading %s', this.id)
|
||||
_reload.call(this, rewrapmethods(this, callback))
|
||||
}
|
||||
|
||||
function save() {
|
||||
debug('saving %s', this.id);
|
||||
savedHash = hash(this);
|
||||
_save.apply(this, arguments);
|
||||
}
|
||||
|
||||
Object.defineProperty(sess, 'reload', {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: reload,
|
||||
writable: true
|
||||
})
|
||||
|
||||
Object.defineProperty(sess, 'save', {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: save,
|
||||
writable: true
|
||||
});
|
||||
}
|
||||
|
||||
// check if session has been modified
|
||||
function isModified(sess) {
|
||||
return originalId !== sess.id || originalHash !== hash(sess);
|
||||
}
|
||||
|
||||
// check if session has been saved
|
||||
function isSaved(sess) {
|
||||
return originalId === sess.id && savedHash === hash(sess);
|
||||
}
|
||||
|
||||
// determine if session should be destroyed
|
||||
function shouldDestroy(req) {
|
||||
return req.sessionID && unsetDestroy && req.session == null;
|
||||
}
|
||||
|
||||
// determine if session should be saved to store
|
||||
function shouldSave(req) {
|
||||
// cannot set cookie without a session ID
|
||||
if (typeof req.sessionID !== 'string') {
|
||||
debug('session ignored because of bogus req.sessionID %o', req.sessionID);
|
||||
return false;
|
||||
}
|
||||
|
||||
return !saveUninitializedSession && !savedHash && cookieId !== req.sessionID
|
||||
? isModified(req.session)
|
||||
: !isSaved(req.session)
|
||||
}
|
||||
|
||||
// determine if session should be touched
|
||||
function shouldTouch(req) {
|
||||
// cannot set cookie without a session ID
|
||||
if (typeof req.sessionID !== 'string') {
|
||||
debug('session ignored because of bogus req.sessionID %o', req.sessionID);
|
||||
return false;
|
||||
}
|
||||
|
||||
return cookieId === req.sessionID && !shouldSave(req);
|
||||
}
|
||||
|
||||
// determine if cookie should be set on response
|
||||
function shouldSetCookie(req) {
|
||||
// cannot set cookie without a session ID
|
||||
if (typeof req.sessionID !== 'string') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return cookieId !== req.sessionID
|
||||
? saveUninitializedSession || isModified(req.session)
|
||||
: rollingSessions || req.session.cookie.expires != null && isModified(req.session);
|
||||
}
|
||||
|
||||
// generate a session if the browser doesn't send a sessionID
|
||||
if (!req.sessionID) {
|
||||
debug('no SID sent, generating session');
|
||||
generate();
|
||||
next();
|
||||
return;
|
||||
}
|
||||
|
||||
// generate the session object
|
||||
debug('fetching %s', req.sessionID);
|
||||
store.get(req.sessionID, function(err, sess){
|
||||
// error handling
|
||||
if (err && err.code !== 'ENOENT') {
|
||||
debug('error %j', err);
|
||||
next(err)
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
if (err || !sess) {
|
||||
debug('no session found')
|
||||
generate()
|
||||
} else {
|
||||
debug('session found')
|
||||
inflate(req, sess)
|
||||
}
|
||||
} catch (e) {
|
||||
next(e)
|
||||
return
|
||||
}
|
||||
|
||||
next()
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Generate a session ID for a new session.
|
||||
*
|
||||
* @return {String}
|
||||
* @private
|
||||
*/
|
||||
|
||||
function generateSessionId(sess) {
|
||||
return uid(24);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the session ID cookie from request.
|
||||
*
|
||||
* @return {string}
|
||||
* @private
|
||||
*/
|
||||
|
||||
function getcookie(req, name, secrets) {
|
||||
var header = req.headers.cookie;
|
||||
var raw;
|
||||
var val;
|
||||
|
||||
// read from cookie header
|
||||
if (header) {
|
||||
var cookies = cookie.parse(header);
|
||||
|
||||
raw = cookies[name];
|
||||
|
||||
if (raw) {
|
||||
if (raw.substr(0, 2) === 's:') {
|
||||
val = unsigncookie(raw.slice(2), secrets);
|
||||
|
||||
if (val === false) {
|
||||
debug('cookie signature invalid');
|
||||
val = undefined;
|
||||
}
|
||||
} else {
|
||||
debug('cookie unsigned')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// back-compat read from cookieParser() signedCookies data
|
||||
if (!val && req.signedCookies) {
|
||||
val = req.signedCookies[name];
|
||||
|
||||
if (val) {
|
||||
deprecate('cookie should be available in req.headers.cookie');
|
||||
}
|
||||
}
|
||||
|
||||
// back-compat read from cookieParser() cookies data
|
||||
if (!val && req.cookies) {
|
||||
raw = req.cookies[name];
|
||||
|
||||
if (raw) {
|
||||
if (raw.substr(0, 2) === 's:') {
|
||||
val = unsigncookie(raw.slice(2), secrets);
|
||||
|
||||
if (val) {
|
||||
deprecate('cookie should be available in req.headers.cookie');
|
||||
}
|
||||
|
||||
if (val === false) {
|
||||
debug('cookie signature invalid');
|
||||
val = undefined;
|
||||
}
|
||||
} else {
|
||||
debug('cookie unsigned')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hash the given `sess` object omitting changes to `.cookie`.
|
||||
*
|
||||
* @param {Object} sess
|
||||
* @return {String}
|
||||
* @private
|
||||
*/
|
||||
|
||||
function hash(sess) {
|
||||
// serialize
|
||||
var str = JSON.stringify(sess, function (key, val) {
|
||||
// ignore sess.cookie property
|
||||
if (this === sess && key === 'cookie') {
|
||||
return
|
||||
}
|
||||
|
||||
return val
|
||||
})
|
||||
|
||||
// hash
|
||||
return crypto
|
||||
.createHash('sha1')
|
||||
.update(str, 'utf8')
|
||||
.digest('hex')
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if request is secure.
|
||||
*
|
||||
* @param {Object} req
|
||||
* @param {Boolean} [trustProxy]
|
||||
* @return {Boolean}
|
||||
* @private
|
||||
*/
|
||||
|
||||
function issecure(req, trustProxy) {
|
||||
// socket is https server
|
||||
if (req.connection && req.connection.encrypted) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// do not trust proxy
|
||||
if (trustProxy === false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// no explicit trust; try req.secure from express
|
||||
if (trustProxy !== true) {
|
||||
return req.secure === true
|
||||
}
|
||||
|
||||
// read the proto from x-forwarded-proto header
|
||||
var header = req.headers['x-forwarded-proto'] || '';
|
||||
var index = header.indexOf(',');
|
||||
var proto = index !== -1
|
||||
? header.substr(0, index).toLowerCase().trim()
|
||||
: header.toLowerCase().trim()
|
||||
|
||||
return proto === 'https';
|
||||
}
|
||||
|
||||
/**
|
||||
* Set cookie on response.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
|
||||
function setcookie(res, name, val, secret, options) {
|
||||
var signed = 's:' + signature.sign(val, secret);
|
||||
var data = cookie.serialize(name, signed, options);
|
||||
|
||||
debug('set-cookie %s', data);
|
||||
|
||||
var prev = res.getHeader('Set-Cookie') || []
|
||||
var header = Array.isArray(prev) ? prev.concat(data) : [prev, data];
|
||||
|
||||
res.setHeader('Set-Cookie', header)
|
||||
}
|
||||
|
||||
/**
|
||||
* Verify and decode the given `val` with `secrets`.
|
||||
*
|
||||
* @param {String} val
|
||||
* @param {Array} secrets
|
||||
* @returns {String|Boolean}
|
||||
* @private
|
||||
*/
|
||||
function unsigncookie(val, secrets) {
|
||||
for (var i = 0; i < secrets.length; i++) {
|
||||
var result = signature.unsign(val, secrets[i]);
|
||||
|
||||
if (result !== false) {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
0.4.2 / 2022-02-02
|
||||
==================
|
||||
|
||||
* pref: read value only when assigning in parse
|
||||
* pref: remove unnecessary regexp in parse
|
||||
|
||||
0.4.1 / 2020-04-21
|
||||
==================
|
||||
|
||||
* Fix `maxAge` option to reject invalid values
|
||||
|
||||
0.4.0 / 2019-05-15
|
||||
==================
|
||||
|
||||
* Add `SameSite=None` support
|
||||
|
||||
0.3.1 / 2016-05-26
|
||||
==================
|
||||
|
||||
* Fix `sameSite: true` to work with draft-7 clients
|
||||
- `true` now sends `SameSite=Strict` instead of `SameSite`
|
||||
|
||||
0.3.0 / 2016-05-26
|
||||
==================
|
||||
|
||||
* Add `sameSite` option
|
||||
- Replaces `firstPartyOnly` option, never implemented by browsers
|
||||
* Improve error message when `encode` is not a function
|
||||
* Improve error message when `expires` is not a `Date`
|
||||
|
||||
0.2.4 / 2016-05-20
|
||||
==================
|
||||
|
||||
* perf: enable strict mode
|
||||
* perf: use for loop in parse
|
||||
* perf: use string concatination for serialization
|
||||
|
||||
0.2.3 / 2015-10-25
|
||||
==================
|
||||
|
||||
* Fix cookie `Max-Age` to never be a floating point number
|
||||
|
||||
0.2.2 / 2015-09-17
|
||||
==================
|
||||
|
||||
* Fix regression when setting empty cookie value
|
||||
- Ease the new restriction, which is just basic header-level validation
|
||||
* Fix typo in invalid value errors
|
||||
|
||||
0.2.1 / 2015-09-17
|
||||
==================
|
||||
|
||||
* Throw on invalid values provided to `serialize`
|
||||
- Ensures the resulting string is a valid HTTP header value
|
||||
|
||||
0.2.0 / 2015-08-13
|
||||
==================
|
||||
|
||||
* Add `firstPartyOnly` option
|
||||
* Throw better error for invalid argument to parse
|
||||
* perf: hoist regular expression
|
||||
|
||||
0.1.5 / 2015-09-17
|
||||
==================
|
||||
|
||||
* Fix regression when setting empty cookie value
|
||||
- Ease the new restriction, which is just basic header-level validation
|
||||
* Fix typo in invalid value errors
|
||||
|
||||
0.1.4 / 2015-09-17
|
||||
==================
|
||||
|
||||
* Throw better error for invalid argument to parse
|
||||
* Throw on invalid values provided to `serialize`
|
||||
- Ensures the resulting string is a valid HTTP header value
|
||||
|
||||
0.1.3 / 2015-05-19
|
||||
==================
|
||||
|
||||
* Reduce the scope of try-catch deopt
|
||||
* Remove argument reassignments
|
||||
|
||||
0.1.2 / 2014-04-16
|
||||
==================
|
||||
|
||||
* Remove unnecessary files from npm package
|
||||
|
||||
0.1.1 / 2014-02-23
|
||||
==================
|
||||
|
||||
* Fix bad parse when cookie value contained a comma
|
||||
* Fix support for `maxAge` of `0`
|
||||
|
||||
0.1.0 / 2013-05-01
|
||||
==================
|
||||
|
||||
* Add `decode` option
|
||||
* Add `encode` option
|
||||
|
||||
0.0.6 / 2013-04-08
|
||||
==================
|
||||
|
||||
* Ignore cookie parts missing `=`
|
||||
|
||||
0.0.5 / 2012-10-29
|
||||
==================
|
||||
|
||||
* Return raw cookie value if value unescape errors
|
||||
|
||||
0.0.4 / 2012-06-21
|
||||
==================
|
||||
|
||||
* Use encode/decodeURIComponent for cookie encoding/decoding
|
||||
- Improve server/client interoperability
|
||||
|
||||
0.0.3 / 2012-06-06
|
||||
==================
|
||||
|
||||
* Only escape special characters per the cookie RFC
|
||||
|
||||
0.0.2 / 2012-06-01
|
||||
==================
|
||||
|
||||
* Fix `maxAge` option to not throw error
|
||||
|
||||
0.0.1 / 2012-05-28
|
||||
==================
|
||||
|
||||
* Add more tests
|
||||
|
||||
0.0.0 / 2012-05-28
|
||||
==================
|
||||
|
||||
* Initial release
|
|
@ -0,0 +1,24 @@
|
|||
(The MIT License)
|
||||
|
||||
Copyright (c) 2012-2014 Roman Shtylman <shtylman@gmail.com>
|
||||
Copyright (c) 2015 Douglas Christopher Wilson <doug@somethingdoug.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
@ -0,0 +1,286 @@
|
|||
# cookie
|
||||
|
||||
[![NPM Version][npm-version-image]][npm-url]
|
||||
[![NPM Downloads][npm-downloads-image]][npm-url]
|
||||
[![Node.js Version][node-version-image]][node-version-url]
|
||||
[![Build Status][github-actions-ci-image]][github-actions-ci-url]
|
||||
[![Test Coverage][coveralls-image]][coveralls-url]
|
||||
|
||||
Basic HTTP cookie parser and serializer for HTTP servers.
|
||||
|
||||
## Installation
|
||||
|
||||
This is a [Node.js](https://nodejs.org/en/) module available through the
|
||||
[npm registry](https://www.npmjs.com/). Installation is done using the
|
||||
[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
|
||||
|
||||
```sh
|
||||
$ npm install cookie
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
```js
|
||||
var cookie = require('cookie');
|
||||
```
|
||||
|
||||
### cookie.parse(str, options)
|
||||
|
||||
Parse an HTTP `Cookie` header string and returning an object of all cookie name-value pairs.
|
||||
The `str` argument is the string representing a `Cookie` header value and `options` is an
|
||||
optional object containing additional parsing options.
|
||||
|
||||
```js
|
||||
var cookies = cookie.parse('foo=bar; equation=E%3Dmc%5E2');
|
||||
// { foo: 'bar', equation: 'E=mc^2' }
|
||||
```
|
||||
|
||||
#### Options
|
||||
|
||||
`cookie.parse` accepts these properties in the options object.
|
||||
|
||||
##### decode
|
||||
|
||||
Specifies a function that will be used to decode a cookie's value. Since the value of a cookie
|
||||
has a limited character set (and must be a simple string), this function can be used to decode
|
||||
a previously-encoded cookie value into a JavaScript string or other object.
|
||||
|
||||
The default function is the global `decodeURIComponent`, which will decode any URL-encoded
|
||||
sequences into their byte representations.
|
||||
|
||||
**note** if an error is thrown from this function, the original, non-decoded cookie value will
|
||||
be returned as the cookie's value.
|
||||
|
||||
### cookie.serialize(name, value, options)
|
||||
|
||||
Serialize a cookie name-value pair into a `Set-Cookie` header string. The `name` argument is the
|
||||
name for the cookie, the `value` argument is the value to set the cookie to, and the `options`
|
||||
argument is an optional object containing additional serialization options.
|
||||
|
||||
```js
|
||||
var setCookie = cookie.serialize('foo', 'bar');
|
||||
// foo=bar
|
||||
```
|
||||
|
||||
#### Options
|
||||
|
||||
`cookie.serialize` accepts these properties in the options object.
|
||||
|
||||
##### domain
|
||||
|
||||
Specifies the value for the [`Domain` `Set-Cookie` attribute][rfc-6265-5.2.3]. By default, no
|
||||
domain is set, and most clients will consider the cookie to apply to only the current domain.
|
||||
|
||||
##### encode
|
||||
|
||||
Specifies a function that will be used to encode a cookie's value. Since value of a cookie
|
||||
has a limited character set (and must be a simple string), this function can be used to encode
|
||||
a value into a string suited for a cookie's value.
|
||||
|
||||
The default function is the global `encodeURIComponent`, which will encode a JavaScript string
|
||||
into UTF-8 byte sequences and then URL-encode any that fall outside of the cookie range.
|
||||
|
||||
##### expires
|
||||
|
||||
Specifies the `Date` object to be the value for the [`Expires` `Set-Cookie` attribute][rfc-6265-5.2.1].
|
||||
By default, no expiration is set, and most clients will consider this a "non-persistent cookie" and
|
||||
will delete it on a condition like exiting a web browser application.
|
||||
|
||||
**note** the [cookie storage model specification][rfc-6265-5.3] states that if both `expires` and
|
||||
`maxAge` are set, then `maxAge` takes precedence, but it is possible not all clients by obey this,
|
||||
so if both are set, they should point to the same date and time.
|
||||
|
||||
##### httpOnly
|
||||
|
||||
Specifies the `boolean` value for the [`HttpOnly` `Set-Cookie` attribute][rfc-6265-5.2.6]. When truthy,
|
||||
the `HttpOnly` attribute is set, otherwise it is not. By default, the `HttpOnly` attribute is not set.
|
||||
|
||||
**note** be careful when setting this to `true`, as compliant clients will not allow client-side
|
||||
JavaScript to see the cookie in `document.cookie`.
|
||||
|
||||
##### maxAge
|
||||
|
||||
Specifies the `number` (in seconds) to be the value for the [`Max-Age` `Set-Cookie` attribute][rfc-6265-5.2.2].
|
||||
The given number will be converted to an integer by rounding down. By default, no maximum age is set.
|
||||
|
||||
**note** the [cookie storage model specification][rfc-6265-5.3] states that if both `expires` and
|
||||
`maxAge` are set, then `maxAge` takes precedence, but it is possible not all clients by obey this,
|
||||
so if both are set, they should point to the same date and time.
|
||||
|
||||
##### path
|
||||
|
||||
Specifies the value for the [`Path` `Set-Cookie` attribute][rfc-6265-5.2.4]. By default, the path
|
||||
is considered the ["default path"][rfc-6265-5.1.4].
|
||||
|
||||
##### sameSite
|
||||
|
||||
Specifies the `boolean` or `string` to be the value for the [`SameSite` `Set-Cookie` attribute][rfc-6265bis-03-4.1.2.7].
|
||||
|
||||
- `true` will set the `SameSite` attribute to `Strict` for strict same site enforcement.
|
||||
- `false` will not set the `SameSite` attribute.
|
||||
- `'lax'` will set the `SameSite` attribute to `Lax` for lax same site enforcement.
|
||||
- `'none'` will set the `SameSite` attribute to `None` for an explicit cross-site cookie.
|
||||
- `'strict'` will set the `SameSite` attribute to `Strict` for strict same site enforcement.
|
||||
|
||||
More information about the different enforcement levels can be found in
|
||||
[the specification][rfc-6265bis-03-4.1.2.7].
|
||||
|
||||
**note** This is an attribute that has not yet been fully standardized, and may change in the future.
|
||||
This also means many clients may ignore this attribute until they understand it.
|
||||
|
||||
##### secure
|
||||
|
||||
Specifies the `boolean` value for the [`Secure` `Set-Cookie` attribute][rfc-6265-5.2.5]. When truthy,
|
||||
the `Secure` attribute is set, otherwise it is not. By default, the `Secure` attribute is not set.
|
||||
|
||||
**note** be careful when setting this to `true`, as compliant clients will not send the cookie back to
|
||||
the server in the future if the browser does not have an HTTPS connection.
|
||||
|
||||
## Example
|
||||
|
||||
The following example uses this module in conjunction with the Node.js core HTTP server
|
||||
to prompt a user for their name and display it back on future visits.
|
||||
|
||||
```js
|
||||
var cookie = require('cookie');
|
||||
var escapeHtml = require('escape-html');
|
||||
var http = require('http');
|
||||
var url = require('url');
|
||||
|
||||
function onRequest(req, res) {
|
||||
// Parse the query string
|
||||
var query = url.parse(req.url, true, true).query;
|
||||
|
||||
if (query && query.name) {
|
||||
// Set a new cookie with the name
|
||||
res.setHeader('Set-Cookie', cookie.serialize('name', String(query.name), {
|
||||
httpOnly: true,
|
||||
maxAge: 60 * 60 * 24 * 7 // 1 week
|
||||
}));
|
||||
|
||||
// Redirect back after setting cookie
|
||||
res.statusCode = 302;
|
||||
res.setHeader('Location', req.headers.referer || '/');
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse the cookies on the request
|
||||
var cookies = cookie.parse(req.headers.cookie || '');
|
||||
|
||||
// Get the visitor name set in the cookie
|
||||
var name = cookies.name;
|
||||
|
||||
res.setHeader('Content-Type', 'text/html; charset=UTF-8');
|
||||
|
||||
if (name) {
|
||||
res.write('<p>Welcome back, <b>' + escapeHtml(name) + '</b>!</p>');
|
||||
} else {
|
||||
res.write('<p>Hello, new visitor!</p>');
|
||||
}
|
||||
|
||||
res.write('<form method="GET">');
|
||||
res.write('<input placeholder="enter your name" name="name"> <input type="submit" value="Set Name">');
|
||||
res.end('</form>');
|
||||
}
|
||||
|
||||
http.createServer(onRequest).listen(3000);
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
```sh
|
||||
$ npm test
|
||||
```
|
||||
|
||||
## Benchmark
|
||||
|
||||
```
|
||||
$ npm run bench
|
||||
|
||||
> cookie@0.4.1 bench
|
||||
> node benchmark/index.js
|
||||
|
||||
node@16.13.1
|
||||
v8@9.4.146.24-node.14
|
||||
uv@1.42.0
|
||||
zlib@1.2.11
|
||||
brotli@1.0.9
|
||||
ares@1.18.1
|
||||
modules@93
|
||||
nghttp2@1.45.1
|
||||
napi@8
|
||||
llhttp@6.0.4
|
||||
openssl@1.1.1l+quic
|
||||
cldr@39.0
|
||||
icu@69.1
|
||||
tz@2021a
|
||||
unicode@13.0
|
||||
ngtcp2@0.1.0-DEV
|
||||
nghttp3@0.1.0-DEV
|
||||
|
||||
> node benchmark/parse-top.js
|
||||
|
||||
cookie.parse - top sites
|
||||
|
||||
15 tests completed.
|
||||
|
||||
parse accounts.google.com x 504,358 ops/sec ±6.55% (171 runs sampled)
|
||||
parse apple.com x 1,369,991 ops/sec ±0.84% (189 runs sampled)
|
||||
parse cloudflare.com x 360,669 ops/sec ±3.75% (182 runs sampled)
|
||||
parse docs.google.com x 521,496 ops/sec ±4.90% (180 runs sampled)
|
||||
parse drive.google.com x 553,514 ops/sec ±0.59% (189 runs sampled)
|
||||
parse en.wikipedia.org x 286,052 ops/sec ±0.62% (188 runs sampled)
|
||||
parse linkedin.com x 178,817 ops/sec ±0.61% (192 runs sampled)
|
||||
parse maps.google.com x 284,585 ops/sec ±0.68% (188 runs sampled)
|
||||
parse microsoft.com x 161,230 ops/sec ±0.56% (192 runs sampled)
|
||||
parse play.google.com x 352,144 ops/sec ±1.01% (181 runs sampled)
|
||||
parse plus.google.com x 275,204 ops/sec ±7.78% (156 runs sampled)
|
||||
parse support.google.com x 339,493 ops/sec ±1.02% (191 runs sampled)
|
||||
parse www.google.com x 286,110 ops/sec ±0.90% (191 runs sampled)
|
||||
parse youtu.be x 548,557 ops/sec ±0.60% (184 runs sampled)
|
||||
parse youtube.com x 545,293 ops/sec ±0.65% (191 runs sampled)
|
||||
|
||||
> node benchmark/parse.js
|
||||
|
||||
cookie.parse - generic
|
||||
|
||||
6 tests completed.
|
||||
|
||||
simple x 1,266,646 ops/sec ±0.65% (191 runs sampled)
|
||||
decode x 838,413 ops/sec ±0.60% (191 runs sampled)
|
||||
unquote x 877,820 ops/sec ±0.72% (189 runs sampled)
|
||||
duplicates x 516,680 ops/sec ±0.61% (191 runs sampled)
|
||||
10 cookies x 156,874 ops/sec ±0.52% (189 runs sampled)
|
||||
100 cookies x 14,663 ops/sec ±0.53% (191 runs sampled)
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [RFC 6265: HTTP State Management Mechanism][rfc-6265]
|
||||
- [Same-site Cookies][rfc-6265bis-03-4.1.2.7]
|
||||
|
||||
[rfc-6265bis-03-4.1.2.7]: https://tools.ietf.org/html/draft-ietf-httpbis-rfc6265bis-03#section-4.1.2.7
|
||||
[rfc-6265]: https://tools.ietf.org/html/rfc6265
|
||||
[rfc-6265-5.1.4]: https://tools.ietf.org/html/rfc6265#section-5.1.4
|
||||
[rfc-6265-5.2.1]: https://tools.ietf.org/html/rfc6265#section-5.2.1
|
||||
[rfc-6265-5.2.2]: https://tools.ietf.org/html/rfc6265#section-5.2.2
|
||||
[rfc-6265-5.2.3]: https://tools.ietf.org/html/rfc6265#section-5.2.3
|
||||
[rfc-6265-5.2.4]: https://tools.ietf.org/html/rfc6265#section-5.2.4
|
||||
[rfc-6265-5.2.5]: https://tools.ietf.org/html/rfc6265#section-5.2.5
|
||||
[rfc-6265-5.2.6]: https://tools.ietf.org/html/rfc6265#section-5.2.6
|
||||
[rfc-6265-5.3]: https://tools.ietf.org/html/rfc6265#section-5.3
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
[coveralls-image]: https://badgen.net/coveralls/c/github/jshttp/cookie/master
|
||||
[coveralls-url]: https://coveralls.io/r/jshttp/cookie?branch=master
|
||||
[github-actions-ci-image]: https://img.shields.io/github/workflow/status/jshttp/cookie/ci/master?label=ci
|
||||
[github-actions-ci-url]: https://github.com/jshttp/cookie/actions/workflows/ci.yml
|
||||
[node-version-image]: https://badgen.net/npm/node/cookie
|
||||
[node-version-url]: https://nodejs.org/en/download
|
||||
[npm-downloads-image]: https://badgen.net/npm/dm/cookie
|
||||
[npm-url]: https://npmjs.org/package/cookie
|
||||
[npm-version-image]: https://badgen.net/npm/v/cookie
|
|
@ -0,0 +1,202 @@
|
|||
/*!
|
||||
* cookie
|
||||
* Copyright(c) 2012-2014 Roman Shtylman
|
||||
* Copyright(c) 2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module exports.
|
||||
* @public
|
||||
*/
|
||||
|
||||
exports.parse = parse;
|
||||
exports.serialize = serialize;
|
||||
|
||||
/**
|
||||
* Module variables.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var decode = decodeURIComponent;
|
||||
var encode = encodeURIComponent;
|
||||
|
||||
/**
|
||||
* RegExp to match field-content in RFC 7230 sec 3.2
|
||||
*
|
||||
* field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
|
||||
* field-vchar = VCHAR / obs-text
|
||||
* obs-text = %x80-FF
|
||||
*/
|
||||
|
||||
var fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;
|
||||
|
||||
/**
|
||||
* Parse a cookie header.
|
||||
*
|
||||
* Parse the given cookie header string into an object
|
||||
* The object has the various cookies as keys(names) => values
|
||||
*
|
||||
* @param {string} str
|
||||
* @param {object} [options]
|
||||
* @return {object}
|
||||
* @public
|
||||
*/
|
||||
|
||||
function parse(str, options) {
|
||||
if (typeof str !== 'string') {
|
||||
throw new TypeError('argument str must be a string');
|
||||
}
|
||||
|
||||
var obj = {}
|
||||
var opt = options || {};
|
||||
var pairs = str.split(';')
|
||||
var dec = opt.decode || decode;
|
||||
|
||||
for (var i = 0; i < pairs.length; i++) {
|
||||
var pair = pairs[i];
|
||||
var index = pair.indexOf('=')
|
||||
|
||||
// skip things that don't look like key=value
|
||||
if (index < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var key = pair.substring(0, index).trim()
|
||||
|
||||
// only assign once
|
||||
if (undefined == obj[key]) {
|
||||
var val = pair.substring(index + 1, pair.length).trim()
|
||||
|
||||
// quoted values
|
||||
if (val[0] === '"') {
|
||||
val = val.slice(1, -1)
|
||||
}
|
||||
|
||||
obj[key] = tryDecode(val, dec);
|
||||
}
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialize data into a cookie header.
|
||||
*
|
||||
* Serialize the a name value pair into a cookie string suitable for
|
||||
* http headers. An optional options object specified cookie parameters.
|
||||
*
|
||||
* serialize('foo', 'bar', { httpOnly: true })
|
||||
* => "foo=bar; httpOnly"
|
||||
*
|
||||
* @param {string} name
|
||||
* @param {string} val
|
||||
* @param {object} [options]
|
||||
* @return {string}
|
||||
* @public
|
||||
*/
|
||||
|
||||
function serialize(name, val, options) {
|
||||
var opt = options || {};
|
||||
var enc = opt.encode || encode;
|
||||
|
||||
if (typeof enc !== 'function') {
|
||||
throw new TypeError('option encode is invalid');
|
||||
}
|
||||
|
||||
if (!fieldContentRegExp.test(name)) {
|
||||
throw new TypeError('argument name is invalid');
|
||||
}
|
||||
|
||||
var value = enc(val);
|
||||
|
||||
if (value && !fieldContentRegExp.test(value)) {
|
||||
throw new TypeError('argument val is invalid');
|
||||
}
|
||||
|
||||
var str = name + '=' + value;
|
||||
|
||||
if (null != opt.maxAge) {
|
||||
var maxAge = opt.maxAge - 0;
|
||||
|
||||
if (isNaN(maxAge) || !isFinite(maxAge)) {
|
||||
throw new TypeError('option maxAge is invalid')
|
||||
}
|
||||
|
||||
str += '; Max-Age=' + Math.floor(maxAge);
|
||||
}
|
||||
|
||||
if (opt.domain) {
|
||||
if (!fieldContentRegExp.test(opt.domain)) {
|
||||
throw new TypeError('option domain is invalid');
|
||||
}
|
||||
|
||||
str += '; Domain=' + opt.domain;
|
||||
}
|
||||
|
||||
if (opt.path) {
|
||||
if (!fieldContentRegExp.test(opt.path)) {
|
||||
throw new TypeError('option path is invalid');
|
||||
}
|
||||
|
||||
str += '; Path=' + opt.path;
|
||||
}
|
||||
|
||||
if (opt.expires) {
|
||||
if (typeof opt.expires.toUTCString !== 'function') {
|
||||
throw new TypeError('option expires is invalid');
|
||||
}
|
||||
|
||||
str += '; Expires=' + opt.expires.toUTCString();
|
||||
}
|
||||
|
||||
if (opt.httpOnly) {
|
||||
str += '; HttpOnly';
|
||||
}
|
||||
|
||||
if (opt.secure) {
|
||||
str += '; Secure';
|
||||
}
|
||||
|
||||
if (opt.sameSite) {
|
||||
var sameSite = typeof opt.sameSite === 'string'
|
||||
? opt.sameSite.toLowerCase() : opt.sameSite;
|
||||
|
||||
switch (sameSite) {
|
||||
case true:
|
||||
str += '; SameSite=Strict';
|
||||
break;
|
||||
case 'lax':
|
||||
str += '; SameSite=Lax';
|
||||
break;
|
||||
case 'strict':
|
||||
str += '; SameSite=Strict';
|
||||
break;
|
||||
case 'none':
|
||||
str += '; SameSite=None';
|
||||
break;
|
||||
default:
|
||||
throw new TypeError('option sameSite is invalid');
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try decoding a string using a decoding function.
|
||||
*
|
||||
* @param {string} str
|
||||
* @param {function} decode
|
||||
* @private
|
||||
*/
|
||||
|
||||
function tryDecode(str, decode) {
|
||||
try {
|
||||
return decode(str);
|
||||
} catch (e) {
|
||||
return str;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"name": "cookie",
|
||||
"description": "HTTP server cookie parsing and serialization",
|
||||
"version": "0.4.2",
|
||||
"author": "Roman Shtylman <shtylman@gmail.com>",
|
||||
"contributors": [
|
||||
"Douglas Christopher Wilson <doug@somethingdoug.com>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"cookie",
|
||||
"cookies"
|
||||
],
|
||||
"repository": "jshttp/cookie",
|
||||
"devDependencies": {
|
||||
"beautify-benchmark": "0.2.4",
|
||||
"benchmark": "2.1.4",
|
||||
"eslint": "7.32.0",
|
||||
"eslint-plugin-markdown": "2.2.1",
|
||||
"mocha": "9.2.0",
|
||||
"nyc": "15.1.0",
|
||||
"top-sites": "1.1.85"
|
||||
},
|
||||
"files": [
|
||||
"HISTORY.md",
|
||||
"LICENSE",
|
||||
"README.md",
|
||||
"index.js"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
},
|
||||
"scripts": {
|
||||
"bench": "node benchmark/index.js",
|
||||
"lint": "eslint .",
|
||||
"test": "mocha --reporter spec --bail --check-leaks --ui qunit test/",
|
||||
"test-ci": "nyc --reporter=lcov --reporter=text npm test",
|
||||
"test-cov": "nyc --reporter=html --reporter=text npm test",
|
||||
"update-bench": "node scripts/update-benchmark.js",
|
||||
"version": "node scripts/version-history.js && git add HISTORY.md"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
"name": "express-session",
|
||||
"version": "1.17.3",
|
||||
"description": "Simple session middleware for Express",
|
||||
"author": "TJ Holowaychuk <tj@vision-media.ca> (http://tjholowaychuk.com)",
|
||||
"contributors": [
|
||||
"Douglas Christopher Wilson <doug@somethingdoug.com>",
|
||||
"Joe Wagner <njwjs722@gmail.com>"
|
||||
],
|
||||
"repository": "expressjs/session",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cookie": "0.4.2",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~2.0.0",
|
||||
"on-headers": "~1.0.2",
|
||||
"parseurl": "~1.3.3",
|
||||
"safe-buffer": "5.2.1",
|
||||
"uid-safe": "~2.1.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"after": "0.8.2",
|
||||
"cookie-parser": "1.4.6",
|
||||
"eslint": "7.32.0",
|
||||
"eslint-plugin-markdown": "2.2.1",
|
||||
"express": "4.17.3",
|
||||
"mocha": "10.0.0",
|
||||
"nyc": "15.1.0",
|
||||
"supertest": "6.2.3"
|
||||
},
|
||||
"files": [
|
||||
"session/",
|
||||
"HISTORY.md",
|
||||
"index.js"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint . && node ./scripts/lint-readme.js",
|
||||
"test": "mocha --require test/support/env --check-leaks --bail --no-exit --reporter spec test/",
|
||||
"test-ci": "nyc --reporter=lcov --reporter=text npm test",
|
||||
"test-cov": "nyc npm test",
|
||||
"version": "node scripts/version-history.js && git add HISTORY.md"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
/*!
|
||||
* Connect - session - Cookie
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var cookie = require('cookie')
|
||||
var deprecate = require('depd')('express-session')
|
||||
|
||||
/**
|
||||
* Initialize a new `Cookie` with the given `options`.
|
||||
*
|
||||
* @param {IncomingMessage} req
|
||||
* @param {Object} options
|
||||
* @api private
|
||||
*/
|
||||
|
||||
var Cookie = module.exports = function Cookie(options) {
|
||||
this.path = '/';
|
||||
this.maxAge = null;
|
||||
this.httpOnly = true;
|
||||
|
||||
if (options) {
|
||||
if (typeof options !== 'object') {
|
||||
throw new TypeError('argument options must be a object')
|
||||
}
|
||||
|
||||
for (var key in options) {
|
||||
if (key !== 'data') {
|
||||
this[key] = options[key]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (this.originalMaxAge === undefined || this.originalMaxAge === null) {
|
||||
this.originalMaxAge = this.maxAge
|
||||
}
|
||||
};
|
||||
|
||||
/*!
|
||||
* Prototype.
|
||||
*/
|
||||
|
||||
Cookie.prototype = {
|
||||
|
||||
/**
|
||||
* Set expires `date`.
|
||||
*
|
||||
* @param {Date} date
|
||||
* @api public
|
||||
*/
|
||||
|
||||
set expires(date) {
|
||||
this._expires = date;
|
||||
this.originalMaxAge = this.maxAge;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get expires `date`.
|
||||
*
|
||||
* @return {Date}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
get expires() {
|
||||
return this._expires;
|
||||
},
|
||||
|
||||
/**
|
||||
* Set expires via max-age in `ms`.
|
||||
*
|
||||
* @param {Number} ms
|
||||
* @api public
|
||||
*/
|
||||
|
||||
set maxAge(ms) {
|
||||
if (ms && typeof ms !== 'number' && !(ms instanceof Date)) {
|
||||
throw new TypeError('maxAge must be a number or Date')
|
||||
}
|
||||
|
||||
if (ms instanceof Date) {
|
||||
deprecate('maxAge as Date; pass number of milliseconds instead')
|
||||
}
|
||||
|
||||
this.expires = typeof ms === 'number'
|
||||
? new Date(Date.now() + ms)
|
||||
: ms;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get expires max-age in `ms`.
|
||||
*
|
||||
* @return {Number}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
get maxAge() {
|
||||
return this.expires instanceof Date
|
||||
? this.expires.valueOf() - Date.now()
|
||||
: this.expires;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return cookie data object.
|
||||
*
|
||||
* @return {Object}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
get data() {
|
||||
return {
|
||||
originalMaxAge: this.originalMaxAge
|
||||
, expires: this._expires
|
||||
, secure: this.secure
|
||||
, httpOnly: this.httpOnly
|
||||
, domain: this.domain
|
||||
, path: this.path
|
||||
, sameSite: this.sameSite
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Return a serialized cookie string.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
serialize: function(name, val){
|
||||
return cookie.serialize(name, val, this.data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Return JSON representation of this cookie.
|
||||
*
|
||||
* @return {Object}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
toJSON: function(){
|
||||
return this.data;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,187 @@
|
|||
/*!
|
||||
* express-session
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* Copyright(c) 2015 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var Store = require('./store')
|
||||
var util = require('util')
|
||||
|
||||
/**
|
||||
* Shim setImmediate for node.js < 0.10
|
||||
* @private
|
||||
*/
|
||||
|
||||
/* istanbul ignore next */
|
||||
var defer = typeof setImmediate === 'function'
|
||||
? setImmediate
|
||||
: function(fn){ process.nextTick(fn.bind.apply(fn, arguments)) }
|
||||
|
||||
/**
|
||||
* Module exports.
|
||||
*/
|
||||
|
||||
module.exports = MemoryStore
|
||||
|
||||
/**
|
||||
* A session store in memory.
|
||||
* @public
|
||||
*/
|
||||
|
||||
function MemoryStore() {
|
||||
Store.call(this)
|
||||
this.sessions = Object.create(null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from Store.
|
||||
*/
|
||||
|
||||
util.inherits(MemoryStore, Store)
|
||||
|
||||
/**
|
||||
* Get all active sessions.
|
||||
*
|
||||
* @param {function} callback
|
||||
* @public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.all = function all(callback) {
|
||||
var sessionIds = Object.keys(this.sessions)
|
||||
var sessions = Object.create(null)
|
||||
|
||||
for (var i = 0; i < sessionIds.length; i++) {
|
||||
var sessionId = sessionIds[i]
|
||||
var session = getSession.call(this, sessionId)
|
||||
|
||||
if (session) {
|
||||
sessions[sessionId] = session;
|
||||
}
|
||||
}
|
||||
|
||||
callback && defer(callback, null, sessions)
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all sessions.
|
||||
*
|
||||
* @param {function} callback
|
||||
* @public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.clear = function clear(callback) {
|
||||
this.sessions = Object.create(null)
|
||||
callback && defer(callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the session associated with the given session ID.
|
||||
*
|
||||
* @param {string} sessionId
|
||||
* @public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.destroy = function destroy(sessionId, callback) {
|
||||
delete this.sessions[sessionId]
|
||||
callback && defer(callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch session by the given session ID.
|
||||
*
|
||||
* @param {string} sessionId
|
||||
* @param {function} callback
|
||||
* @public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.get = function get(sessionId, callback) {
|
||||
defer(callback, null, getSession.call(this, sessionId))
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit the given session associated with the given sessionId to the store.
|
||||
*
|
||||
* @param {string} sessionId
|
||||
* @param {object} session
|
||||
* @param {function} callback
|
||||
* @public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.set = function set(sessionId, session, callback) {
|
||||
this.sessions[sessionId] = JSON.stringify(session)
|
||||
callback && defer(callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get number of active sessions.
|
||||
*
|
||||
* @param {function} callback
|
||||
* @public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.length = function length(callback) {
|
||||
this.all(function (err, sessions) {
|
||||
if (err) return callback(err)
|
||||
callback(null, Object.keys(sessions).length)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Touch the given session object associated with the given session ID.
|
||||
*
|
||||
* @param {string} sessionId
|
||||
* @param {object} session
|
||||
* @param {function} callback
|
||||
* @public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.touch = function touch(sessionId, session, callback) {
|
||||
var currentSession = getSession.call(this, sessionId)
|
||||
|
||||
if (currentSession) {
|
||||
// update expiration
|
||||
currentSession.cookie = session.cookie
|
||||
this.sessions[sessionId] = JSON.stringify(currentSession)
|
||||
}
|
||||
|
||||
callback && defer(callback)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get session from the store.
|
||||
* @private
|
||||
*/
|
||||
|
||||
function getSession(sessionId) {
|
||||
var sess = this.sessions[sessionId]
|
||||
|
||||
if (!sess) {
|
||||
return
|
||||
}
|
||||
|
||||
// parse
|
||||
sess = JSON.parse(sess)
|
||||
|
||||
if (sess.cookie) {
|
||||
var expires = typeof sess.cookie.expires === 'string'
|
||||
? new Date(sess.cookie.expires)
|
||||
: sess.cookie.expires
|
||||
|
||||
// destroy expired session
|
||||
if (expires && expires <= Date.now()) {
|
||||
delete this.sessions[sessionId]
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return sess
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
/*!
|
||||
* Connect - session - Session
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Expose Session.
|
||||
*/
|
||||
|
||||
module.exports = Session;
|
||||
|
||||
/**
|
||||
* Create a new `Session` with the given request and `data`.
|
||||
*
|
||||
* @param {IncomingRequest} req
|
||||
* @param {Object} data
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function Session(req, data) {
|
||||
Object.defineProperty(this, 'req', { value: req });
|
||||
Object.defineProperty(this, 'id', { value: req.sessionID });
|
||||
|
||||
if (typeof data === 'object' && data !== null) {
|
||||
// merge data into this, ignoring prototype properties
|
||||
for (var prop in data) {
|
||||
if (!(prop in this)) {
|
||||
this[prop] = data[prop]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update reset `.cookie.maxAge` to prevent
|
||||
* the cookie from expiring when the
|
||||
* session is still active.
|
||||
*
|
||||
* @return {Session} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
defineMethod(Session.prototype, 'touch', function touch() {
|
||||
return this.resetMaxAge();
|
||||
});
|
||||
|
||||
/**
|
||||
* Reset `.maxAge` to `.originalMaxAge`.
|
||||
*
|
||||
* @return {Session} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
defineMethod(Session.prototype, 'resetMaxAge', function resetMaxAge() {
|
||||
this.cookie.maxAge = this.cookie.originalMaxAge;
|
||||
return this;
|
||||
});
|
||||
|
||||
/**
|
||||
* Save the session data with optional callback `fn(err)`.
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @return {Session} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
defineMethod(Session.prototype, 'save', function save(fn) {
|
||||
this.req.sessionStore.set(this.id, this, fn || function(){});
|
||||
return this;
|
||||
});
|
||||
|
||||
/**
|
||||
* Re-loads the session data _without_ altering
|
||||
* the maxAge properties. Invokes the callback `fn(err)`,
|
||||
* after which time if no exception has occurred the
|
||||
* `req.session` property will be a new `Session` object,
|
||||
* although representing the same session.
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @return {Session} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
defineMethod(Session.prototype, 'reload', function reload(fn) {
|
||||
var req = this.req
|
||||
var store = this.req.sessionStore
|
||||
|
||||
store.get(this.id, function(err, sess){
|
||||
if (err) return fn(err);
|
||||
if (!sess) return fn(new Error('failed to load session'));
|
||||
store.createSession(req, sess);
|
||||
fn();
|
||||
});
|
||||
return this;
|
||||
});
|
||||
|
||||
/**
|
||||
* Destroy `this` session.
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @return {Session} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
defineMethod(Session.prototype, 'destroy', function destroy(fn) {
|
||||
delete this.req.session;
|
||||
this.req.sessionStore.destroy(this.id, fn);
|
||||
return this;
|
||||
});
|
||||
|
||||
/**
|
||||
* Regenerate this request's session.
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @return {Session} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
defineMethod(Session.prototype, 'regenerate', function regenerate(fn) {
|
||||
this.req.sessionStore.regenerate(this.req, fn);
|
||||
return this;
|
||||
});
|
||||
|
||||
/**
|
||||
* Helper function for creating a method on a prototype.
|
||||
*
|
||||
* @param {Object} obj
|
||||
* @param {String} name
|
||||
* @param {Function} fn
|
||||
* @private
|
||||
*/
|
||||
function defineMethod(obj, name, fn) {
|
||||
Object.defineProperty(obj, name, {
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
value: fn,
|
||||
writable: true
|
||||
});
|
||||
};
|
|
@ -0,0 +1,102 @@
|
|||
/*!
|
||||
* Connect - session - Store
|
||||
* Copyright(c) 2010 Sencha Inc.
|
||||
* Copyright(c) 2011 TJ Holowaychuk
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
* @private
|
||||
*/
|
||||
|
||||
var Cookie = require('./cookie')
|
||||
var EventEmitter = require('events').EventEmitter
|
||||
var Session = require('./session')
|
||||
var util = require('util')
|
||||
|
||||
/**
|
||||
* Module exports.
|
||||
* @public
|
||||
*/
|
||||
|
||||
module.exports = Store
|
||||
|
||||
/**
|
||||
* Abstract base class for session stores.
|
||||
* @public
|
||||
*/
|
||||
|
||||
function Store () {
|
||||
EventEmitter.call(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from EventEmitter.
|
||||
*/
|
||||
|
||||
util.inherits(Store, EventEmitter)
|
||||
|
||||
/**
|
||||
* Re-generate the given requests's session.
|
||||
*
|
||||
* @param {IncomingRequest} req
|
||||
* @return {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Store.prototype.regenerate = function(req, fn){
|
||||
var self = this;
|
||||
this.destroy(req.sessionID, function(err){
|
||||
self.generate(req);
|
||||
fn(err);
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Load a `Session` instance via the given `sid`
|
||||
* and invoke the callback `fn(err, sess)`.
|
||||
*
|
||||
* @param {String} sid
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Store.prototype.load = function(sid, fn){
|
||||
var self = this;
|
||||
this.get(sid, function(err, sess){
|
||||
if (err) return fn(err);
|
||||
if (!sess) return fn();
|
||||
var req = { sessionID: sid, sessionStore: self };
|
||||
fn(null, self.createSession(req, sess))
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Create session from JSON `sess` data.
|
||||
*
|
||||
* @param {IncomingRequest} req
|
||||
* @param {Object} sess
|
||||
* @return {Session}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Store.prototype.createSession = function(req, sess){
|
||||
var expires = sess.cookie.expires
|
||||
var originalMaxAge = sess.cookie.originalMaxAge
|
||||
|
||||
sess.cookie = new Cookie(sess.cookie);
|
||||
|
||||
if (typeof expires === 'string') {
|
||||
// convert expires to a Date object
|
||||
sess.cookie.expires = new Date(expires)
|
||||
}
|
||||
|
||||
// keep originalMaxAge intact
|
||||
sess.cookie.originalMaxAge = originalMaxAge
|
||||
|
||||
req.session = new Session(req, sess);
|
||||
return req.session;
|
||||
};
|
|
@ -0,0 +1,15 @@
|
|||
The ISC License
|
||||
|
||||
Copyright (c) Isaac Z. Schlueter and Contributors
|
||||
|
||||
Permission to use, copy, modify, and/or distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
|
||||
IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@ -0,0 +1,158 @@
|
|||
# lru cache
|
||||
|
||||
A cache object that deletes the least-recently-used items.
|
||||
|
||||
[![Build Status](https://travis-ci.org/isaacs/node-lru-cache.svg?branch=master)](https://travis-ci.org/isaacs/node-lru-cache) [![Coverage Status](https://coveralls.io/repos/isaacs/node-lru-cache/badge.svg?service=github)](https://coveralls.io/github/isaacs/node-lru-cache)
|
||||
|
||||
## Installation:
|
||||
|
||||
```javascript
|
||||
npm install lru-cache --save
|
||||
```
|
||||
|
||||
## Usage:
|
||||
|
||||
```javascript
|
||||
var LRU = require("lru-cache")
|
||||
, options = { max: 500
|
||||
, length: function (n, key) { return n * 2 + key.length }
|
||||
, dispose: function (key, n) { n.close() }
|
||||
, maxAge: 1000 * 60 * 60 }
|
||||
, cache = LRU(options)
|
||||
, otherCache = LRU(50) // sets just the max size
|
||||
|
||||
cache.set("key", "value")
|
||||
cache.get("key") // "value"
|
||||
|
||||
// non-string keys ARE fully supported
|
||||
// but note that it must be THE SAME object, not
|
||||
// just a JSON-equivalent object.
|
||||
var someObject = { a: 1 }
|
||||
cache.set(someObject, 'a value')
|
||||
// Object keys are not toString()-ed
|
||||
cache.set('[object Object]', 'a different value')
|
||||
assert.equal(cache.get(someObject), 'a value')
|
||||
// A similar object with same keys/values won't work,
|
||||
// because it's a different object identity
|
||||
assert.equal(cache.get({ a: 1 }), undefined)
|
||||
|
||||
cache.reset() // empty the cache
|
||||
```
|
||||
|
||||
If you put more stuff in it, then items will fall out.
|
||||
|
||||
If you try to put an oversized thing in it, then it'll fall out right
|
||||
away.
|
||||
|
||||
## Options
|
||||
|
||||
* `max` The maximum size of the cache, checked by applying the length
|
||||
function to all values in the cache. Not setting this is kind of
|
||||
silly, since that's the whole purpose of this lib, but it defaults
|
||||
to `Infinity`.
|
||||
* `maxAge` Maximum age in ms. Items are not pro-actively pruned out
|
||||
as they age, but if you try to get an item that is too old, it'll
|
||||
drop it and return undefined instead of giving it to you.
|
||||
* `length` Function that is used to calculate the length of stored
|
||||
items. If you're storing strings or buffers, then you probably want
|
||||
to do something like `function(n, key){return n.length}`. The default is
|
||||
`function(){return 1}`, which is fine if you want to store `max`
|
||||
like-sized things. The item is passed as the first argument, and
|
||||
the key is passed as the second argumnet.
|
||||
* `dispose` Function that is called on items when they are dropped
|
||||
from the cache. This can be handy if you want to close file
|
||||
descriptors or do other cleanup tasks when items are no longer
|
||||
accessible. Called with `key, value`. It's called *before*
|
||||
actually removing the item from the internal cache, so if you want
|
||||
to immediately put it back in, you'll have to do that in a
|
||||
`nextTick` or `setTimeout` callback or it won't do anything.
|
||||
* `stale` By default, if you set a `maxAge`, it'll only actually pull
|
||||
stale items out of the cache when you `get(key)`. (That is, it's
|
||||
not pre-emptively doing a `setTimeout` or anything.) If you set
|
||||
`stale:true`, it'll return the stale value before deleting it. If
|
||||
you don't set this, then it'll return `undefined` when you try to
|
||||
get a stale entry, as if it had already been deleted.
|
||||
* `noDisposeOnSet` By default, if you set a `dispose()` method, then
|
||||
it'll be called whenever a `set()` operation overwrites an existing
|
||||
key. If you set this option, `dispose()` will only be called when a
|
||||
key falls out of the cache, not when it is overwritten.
|
||||
|
||||
## API
|
||||
|
||||
* `set(key, value, maxAge)`
|
||||
* `get(key) => value`
|
||||
|
||||
Both of these will update the "recently used"-ness of the key.
|
||||
They do what you think. `maxAge` is optional and overrides the
|
||||
cache `maxAge` option if provided.
|
||||
|
||||
If the key is not found, `get()` will return `undefined`.
|
||||
|
||||
The key and val can be any value.
|
||||
|
||||
* `peek(key)`
|
||||
|
||||
Returns the key value (or `undefined` if not found) without
|
||||
updating the "recently used"-ness of the key.
|
||||
|
||||
(If you find yourself using this a lot, you *might* be using the
|
||||
wrong sort of data structure, but there are some use cases where
|
||||
it's handy.)
|
||||
|
||||
* `del(key)`
|
||||
|
||||
Deletes a key out of the cache.
|
||||
|
||||
* `reset()`
|
||||
|
||||
Clear the cache entirely, throwing away all values.
|
||||
|
||||
* `has(key)`
|
||||
|
||||
Check if a key is in the cache, without updating the recent-ness
|
||||
or deleting it for being stale.
|
||||
|
||||
* `forEach(function(value,key,cache), [thisp])`
|
||||
|
||||
Just like `Array.prototype.forEach`. Iterates over all the keys
|
||||
in the cache, in order of recent-ness. (Ie, more recently used
|
||||
items are iterated over first.)
|
||||
|
||||
* `rforEach(function(value,key,cache), [thisp])`
|
||||
|
||||
The same as `cache.forEach(...)` but items are iterated over in
|
||||
reverse order. (ie, less recently used items are iterated over
|
||||
first.)
|
||||
|
||||
* `keys()`
|
||||
|
||||
Return an array of the keys in the cache.
|
||||
|
||||
* `values()`
|
||||
|
||||
Return an array of the values in the cache.
|
||||
|
||||
* `length`
|
||||
|
||||
Return total length of objects in cache taking into account
|
||||
`length` options function.
|
||||
|
||||
* `itemCount`
|
||||
|
||||
Return total quantity of objects currently in cache. Note, that
|
||||
`stale` (see options) items are returned as part of this item
|
||||
count.
|
||||
|
||||
* `dump()`
|
||||
|
||||
Return an array of the cache entries ready for serialization and usage
|
||||
with 'destinationCache.load(arr)`.
|
||||
|
||||
* `load(cacheEntriesArray)`
|
||||
|
||||
Loads another cache entries array, obtained with `sourceCache.dump()`,
|
||||
into the cache. The destination cache is reset before loading new entries
|
||||
|
||||
* `prune()`
|
||||
|
||||
Manually iterates over the entire cache proactively pruning old entries
|
|
@ -0,0 +1,468 @@
|
|||
'use strict'
|
||||
|
||||
module.exports = LRUCache
|
||||
|
||||
// This will be a proper iterable 'Map' in engines that support it,
|
||||
// or a fakey-fake PseudoMap in older versions.
|
||||
var Map = require('pseudomap')
|
||||
var util = require('util')
|
||||
|
||||
// A linked list to keep track of recently-used-ness
|
||||
var Yallist = require('yallist')
|
||||
|
||||
// use symbols if possible, otherwise just _props
|
||||
var hasSymbol = typeof Symbol === 'function' && process.env._nodeLRUCacheForceNoSymbol !== '1'
|
||||
var makeSymbol
|
||||
if (hasSymbol) {
|
||||
makeSymbol = function (key) {
|
||||
return Symbol(key)
|
||||
}
|
||||
} else {
|
||||
makeSymbol = function (key) {
|
||||
return '_' + key
|
||||
}
|
||||
}
|
||||
|
||||
var MAX = makeSymbol('max')
|
||||
var LENGTH = makeSymbol('length')
|
||||
var LENGTH_CALCULATOR = makeSymbol('lengthCalculator')
|
||||
var ALLOW_STALE = makeSymbol('allowStale')
|
||||
var MAX_AGE = makeSymbol('maxAge')
|
||||
var DISPOSE = makeSymbol('dispose')
|
||||
var NO_DISPOSE_ON_SET = makeSymbol('noDisposeOnSet')
|
||||
var LRU_LIST = makeSymbol('lruList')
|
||||
var CACHE = makeSymbol('cache')
|
||||
|
||||
function naiveLength () { return 1 }
|
||||
|
||||
// lruList is a yallist where the head is the youngest
|
||||
// item, and the tail is the oldest. the list contains the Hit
|
||||
// objects as the entries.
|
||||
// Each Hit object has a reference to its Yallist.Node. This
|
||||
// never changes.
|
||||
//
|
||||
// cache is a Map (or PseudoMap) that matches the keys to
|
||||
// the Yallist.Node object.
|
||||
function LRUCache (options) {
|
||||
if (!(this instanceof LRUCache)) {
|
||||
return new LRUCache(options)
|
||||
}
|
||||
|
||||
if (typeof options === 'number') {
|
||||
options = { max: options }
|
||||
}
|
||||
|
||||
if (!options) {
|
||||
options = {}
|
||||
}
|
||||
|
||||
var max = this[MAX] = options.max
|
||||
// Kind of weird to have a default max of Infinity, but oh well.
|
||||
if (!max ||
|
||||
!(typeof max === 'number') ||
|
||||
max <= 0) {
|
||||
this[MAX] = Infinity
|
||||
}
|
||||
|
||||
var lc = options.length || naiveLength
|
||||
if (typeof lc !== 'function') {
|
||||
lc = naiveLength
|
||||
}
|
||||
this[LENGTH_CALCULATOR] = lc
|
||||
|
||||
this[ALLOW_STALE] = options.stale || false
|
||||
this[MAX_AGE] = options.maxAge || 0
|
||||
this[DISPOSE] = options.dispose
|
||||
this[NO_DISPOSE_ON_SET] = options.noDisposeOnSet || false
|
||||
this.reset()
|
||||
}
|
||||
|
||||
// resize the cache when the max changes.
|
||||
Object.defineProperty(LRUCache.prototype, 'max', {
|
||||
set: function (mL) {
|
||||
if (!mL || !(typeof mL === 'number') || mL <= 0) {
|
||||
mL = Infinity
|
||||
}
|
||||
this[MAX] = mL
|
||||
trim(this)
|
||||
},
|
||||
get: function () {
|
||||
return this[MAX]
|
||||
},
|
||||
enumerable: true
|
||||
})
|
||||
|
||||
Object.defineProperty(LRUCache.prototype, 'allowStale', {
|
||||
set: function (allowStale) {
|
||||
this[ALLOW_STALE] = !!allowStale
|
||||
},
|
||||
get: function () {
|
||||
return this[ALLOW_STALE]
|
||||
},
|
||||
enumerable: true
|
||||
})
|
||||
|
||||
Object.defineProperty(LRUCache.prototype, 'maxAge', {
|
||||
set: function (mA) {
|
||||
if (!mA || !(typeof mA === 'number') || mA < 0) {
|
||||
mA = 0
|
||||
}
|
||||
this[MAX_AGE] = mA
|
||||
trim(this)
|
||||
},
|
||||
get: function () {
|
||||
return this[MAX_AGE]
|
||||
},
|
||||
enumerable: true
|
||||
})
|
||||
|
||||
// resize the cache when the lengthCalculator changes.
|
||||
Object.defineProperty(LRUCache.prototype, 'lengthCalculator', {
|
||||
set: function (lC) {
|
||||
if (typeof lC !== 'function') {
|
||||
lC = naiveLength
|
||||
}
|
||||
if (lC !== this[LENGTH_CALCULATOR]) {
|
||||
this[LENGTH_CALCULATOR] = lC
|
||||
this[LENGTH] = 0
|
||||
this[LRU_LIST].forEach(function (hit) {
|
||||
hit.length = this[LENGTH_CALCULATOR](hit.value, hit.key)
|
||||
this[LENGTH] += hit.length
|
||||
}, this)
|
||||
}
|
||||
trim(this)
|
||||
},
|
||||
get: function () { return this[LENGTH_CALCULATOR] },
|
||||
enumerable: true
|
||||
})
|
||||
|
||||
Object.defineProperty(LRUCache.prototype, 'length', {
|
||||
get: function () { return this[LENGTH] },
|
||||
enumerable: true
|
||||
})
|
||||
|
||||
Object.defineProperty(LRUCache.prototype, 'itemCount', {
|
||||
get: function () { return this[LRU_LIST].length },
|
||||
enumerable: true
|
||||
})
|
||||
|
||||
LRUCache.prototype.rforEach = function (fn, thisp) {
|
||||
thisp = thisp || this
|
||||
for (var walker = this[LRU_LIST].tail; walker !== null;) {
|
||||
var prev = walker.prev
|
||||
forEachStep(this, fn, walker, thisp)
|
||||
walker = prev
|
||||
}
|
||||
}
|
||||
|
||||
function forEachStep (self, fn, node, thisp) {
|
||||
var hit = node.value
|
||||
if (isStale(self, hit)) {
|
||||
del(self, node)
|
||||
if (!self[ALLOW_STALE]) {
|
||||
hit = undefined
|
||||
}
|
||||
}
|
||||
if (hit) {
|
||||
fn.call(thisp, hit.value, hit.key, self)
|
||||
}
|
||||
}
|
||||
|
||||
LRUCache.prototype.forEach = function (fn, thisp) {
|
||||
thisp = thisp || this
|
||||
for (var walker = this[LRU_LIST].head; walker !== null;) {
|
||||
var next = walker.next
|
||||
forEachStep(this, fn, walker, thisp)
|
||||
walker = next
|
||||
}
|
||||
}
|
||||
|
||||
LRUCache.prototype.keys = function () {
|
||||
return this[LRU_LIST].toArray().map(function (k) {
|
||||
return k.key
|
||||
}, this)
|
||||
}
|
||||
|
||||
LRUCache.prototype.values = function () {
|
||||
return this[LRU_LIST].toArray().map(function (k) {
|
||||
return k.value
|
||||
}, this)
|
||||
}
|
||||
|
||||
LRUCache.prototype.reset = function () {
|
||||
if (this[DISPOSE] &&
|
||||
this[LRU_LIST] &&
|
||||
this[LRU_LIST].length) {
|
||||
this[LRU_LIST].forEach(function (hit) {
|
||||
this[DISPOSE](hit.key, hit.value)
|
||||
}, this)
|
||||
}
|
||||
|
||||
this[CACHE] = new Map() // hash of items by key
|
||||
this[LRU_LIST] = new Yallist() // list of items in order of use recency
|
||||
this[LENGTH] = 0 // length of items in the list
|
||||
}
|
||||
|
||||
LRUCache.prototype.dump = function () {
|
||||
return this[LRU_LIST].map(function (hit) {
|
||||
if (!isStale(this, hit)) {
|
||||
return {
|
||||
k: hit.key,
|
||||
v: hit.value,
|
||||
e: hit.now + (hit.maxAge || 0)
|
||||
}
|
||||
}
|
||||
}, this).toArray().filter(function (h) {
|
||||
return h
|
||||
})
|
||||
}
|
||||
|
||||
LRUCache.prototype.dumpLru = function () {
|
||||
return this[LRU_LIST]
|
||||
}
|
||||
|
||||
/* istanbul ignore next */
|
||||
LRUCache.prototype.inspect = function (n, opts) {
|
||||
var str = 'LRUCache {'
|
||||
var extras = false
|
||||
|
||||
var as = this[ALLOW_STALE]
|
||||
if (as) {
|
||||
str += '\n allowStale: true'
|
||||
extras = true
|
||||
}
|
||||
|
||||
var max = this[MAX]
|
||||
if (max && max !== Infinity) {
|
||||
if (extras) {
|
||||
str += ','
|
||||
}
|
||||
str += '\n max: ' + util.inspect(max, opts)
|
||||
extras = true
|
||||
}
|
||||
|
||||
var maxAge = this[MAX_AGE]
|
||||
if (maxAge) {
|
||||
if (extras) {
|
||||
str += ','
|
||||
}
|
||||
str += '\n maxAge: ' + util.inspect(maxAge, opts)
|
||||
extras = true
|
||||
}
|
||||
|
||||
var lc = this[LENGTH_CALCULATOR]
|
||||
if (lc && lc !== naiveLength) {
|
||||
if (extras) {
|
||||
str += ','
|
||||
}
|
||||
str += '\n length: ' + util.inspect(this[LENGTH], opts)
|
||||
extras = true
|
||||
}
|
||||
|
||||
var didFirst = false
|
||||
this[LRU_LIST].forEach(function (item) {
|
||||
if (didFirst) {
|
||||
str += ',\n '
|
||||
} else {
|
||||
if (extras) {
|
||||
str += ',\n'
|
||||
}
|
||||
didFirst = true
|
||||
str += '\n '
|
||||
}
|
||||
var key = util.inspect(item.key).split('\n').join('\n ')
|
||||
var val = { value: item.value }
|
||||
if (item.maxAge !== maxAge) {
|
||||
val.maxAge = item.maxAge
|
||||
}
|
||||
if (lc !== naiveLength) {
|
||||
val.length = item.length
|
||||
}
|
||||
if (isStale(this, item)) {
|
||||
val.stale = true
|
||||
}
|
||||
|
||||
val = util.inspect(val, opts).split('\n').join('\n ')
|
||||
str += key + ' => ' + val
|
||||
})
|
||||
|
||||
if (didFirst || extras) {
|
||||
str += '\n'
|
||||
}
|
||||
str += '}'
|
||||
|
||||
return str
|
||||
}
|
||||
|
||||
LRUCache.prototype.set = function (key, value, maxAge) {
|
||||
maxAge = maxAge || this[MAX_AGE]
|
||||
|
||||
var now = maxAge ? Date.now() : 0
|
||||
var len = this[LENGTH_CALCULATOR](value, key)
|
||||
|
||||
if (this[CACHE].has(key)) {
|
||||
if (len > this[MAX]) {
|
||||
del(this, this[CACHE].get(key))
|
||||
return false
|
||||
}
|
||||
|
||||
var node = this[CACHE].get(key)
|
||||
var item = node.value
|
||||
|
||||
// dispose of the old one before overwriting
|
||||
// split out into 2 ifs for better coverage tracking
|
||||
if (this[DISPOSE]) {
|
||||
if (!this[NO_DISPOSE_ON_SET]) {
|
||||
this[DISPOSE](key, item.value)
|
||||
}
|
||||
}
|
||||
|
||||
item.now = now
|
||||
item.maxAge = maxAge
|
||||
item.value = value
|
||||
this[LENGTH] += len - item.length
|
||||
item.length = len
|
||||
this.get(key)
|
||||
trim(this)
|
||||
return true
|
||||
}
|
||||
|
||||
var hit = new Entry(key, value, len, now, maxAge)
|
||||
|
||||
// oversized objects fall out of cache automatically.
|
||||
if (hit.length > this[MAX]) {
|
||||
if (this[DISPOSE]) {
|
||||
this[DISPOSE](key, value)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
this[LENGTH] += hit.length
|
||||
this[LRU_LIST].unshift(hit)
|
||||
this[CACHE].set(key, this[LRU_LIST].head)
|
||||
trim(this)
|
||||
return true
|
||||
}
|
||||
|
||||
LRUCache.prototype.has = function (key) {
|
||||
if (!this[CACHE].has(key)) return false
|
||||
var hit = this[CACHE].get(key).value
|
||||
if (isStale(this, hit)) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
LRUCache.prototype.get = function (key) {
|
||||
return get(this, key, true)
|
||||
}
|
||||
|
||||
LRUCache.prototype.peek = function (key) {
|
||||
return get(this, key, false)
|
||||
}
|
||||
|
||||
LRUCache.prototype.pop = function () {
|
||||
var node = this[LRU_LIST].tail
|
||||
if (!node) return null
|
||||
del(this, node)
|
||||
return node.value
|
||||
}
|
||||
|
||||
LRUCache.prototype.del = function (key) {
|
||||
del(this, this[CACHE].get(key))
|
||||
}
|
||||
|
||||
LRUCache.prototype.load = function (arr) {
|
||||
// reset the cache
|
||||
this.reset()
|
||||
|
||||
var now = Date.now()
|
||||
// A previous serialized cache has the most recent items first
|
||||
for (var l = arr.length - 1; l >= 0; l--) {
|
||||
var hit = arr[l]
|
||||
var expiresAt = hit.e || 0
|
||||
if (expiresAt === 0) {
|
||||
// the item was created without expiration in a non aged cache
|
||||
this.set(hit.k, hit.v)
|
||||
} else {
|
||||
var maxAge = expiresAt - now
|
||||
// dont add already expired items
|
||||
if (maxAge > 0) {
|
||||
this.set(hit.k, hit.v, maxAge)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LRUCache.prototype.prune = function () {
|
||||
var self = this
|
||||
this[CACHE].forEach(function (value, key) {
|
||||
get(self, key, false)
|
||||
})
|
||||
}
|
||||
|
||||
function get (self, key, doUse) {
|
||||
var node = self[CACHE].get(key)
|
||||
if (node) {
|
||||
var hit = node.value
|
||||
if (isStale(self, hit)) {
|
||||
del(self, node)
|
||||
if (!self[ALLOW_STALE]) hit = undefined
|
||||
} else {
|
||||
if (doUse) {
|
||||
self[LRU_LIST].unshiftNode(node)
|
||||
}
|
||||
}
|
||||
if (hit) hit = hit.value
|
||||
}
|
||||
return hit
|
||||
}
|
||||
|
||||
function isStale (self, hit) {
|
||||
if (!hit || (!hit.maxAge && !self[MAX_AGE])) {
|
||||
return false
|
||||
}
|
||||
var stale = false
|
||||
var diff = Date.now() - hit.now
|
||||
if (hit.maxAge) {
|
||||
stale = diff > hit.maxAge
|
||||
} else {
|
||||
stale = self[MAX_AGE] && (diff > self[MAX_AGE])
|
||||
}
|
||||
return stale
|
||||
}
|
||||
|
||||
function trim (self) {
|
||||
if (self[LENGTH] > self[MAX]) {
|
||||
for (var walker = self[LRU_LIST].tail;
|
||||
self[LENGTH] > self[MAX] && walker !== null;) {
|
||||
// We know that we're about to delete this one, and also
|
||||
// what the next least recently used key will be, so just
|
||||
// go ahead and set it now.
|
||||
var prev = walker.prev
|
||||
del(self, walker)
|
||||
walker = prev
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function del (self, node) {
|
||||
if (node) {
|
||||
var hit = node.value
|
||||
if (self[DISPOSE]) {
|
||||
self[DISPOSE](hit.key, hit.value)
|
||||
}
|
||||
self[LENGTH] -= hit.length
|
||||
self[CACHE].delete(hit.key)
|
||||
self[LRU_LIST].removeNode(node)
|
||||
}
|
||||
}
|
||||
|
||||
// classy, since V8 prefers predictable objects.
|
||||
function Entry (key, value, length, now, maxAge) {
|
||||
this.key = key
|
||||
this.value = value
|
||||
this.length = length
|
||||
this.now = now
|
||||
this.maxAge = maxAge || 0
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
{
|
||||
"name": "lru-cache",
|
||||
"description": "A cache object that deletes the least-recently-used items.",
|
||||
"version": "4.1.5",
|
||||
"author": "Isaac Z. Schlueter <i@izs.me>",
|
||||
"keywords": [
|
||||
"mru",
|
||||
"lru",
|
||||
"cache"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "tap test/*.js --100 -J",
|
||||
"snap": "TAP_SNAPSHOT=1 tap test/*.js -J",
|
||||
"posttest": "standard test/*.js index.js",
|
||||
"coveragerport": "tap --coverage-report=html",
|
||||
"lintfix": "standard --fix test/*.js index.js",
|
||||
"preversion": "npm test",
|
||||
"postversion": "npm publish --tag=legacy",
|
||||
"postpublish": "git push origin --all; git push origin --tags"
|
||||
},
|
||||
"main": "index.js",
|
||||
"repository": "git://github.com/isaacs/node-lru-cache.git",
|
||||
"devDependencies": {
|
||||
"benchmark": "^2.1.4",
|
||||
"standard": "^12.0.1",
|
||||
"tap": "^12.1.0"
|
||||
},
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"pseudomap": "^1.0.2",
|
||||
"yallist": "^2.1.2"
|
||||
},
|
||||
"files": [
|
||||
"index.js"
|
||||
]
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
language: node_js
|
||||
|
||||
node_js:
|
||||
- "10"
|
||||
- "12"
|
||||
- "14"
|
||||
|
||||
cache:
|
||||
directories:
|
||||
- node_modules
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 Rocco Musolino
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,71 @@
|
|||
# memorystore [![NPM Version](https://img.shields.io/npm/v/memorystore.svg)](https://www.npmjs.com/package/memorystore) ![node](https://img.shields.io/node/v/memorystore.svg) [![Build Status](https://travis-ci.org/roccomuso/memorystore.svg?branch=master)](https://travis-ci.org/roccomuso/memorystore) [![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
|
||||
|
||||
> express-session full featured `MemoryStore` module without leaks!
|
||||
|
||||
A session store implementation for Express using [lru-cache](https://github.com/isaacs/node-lru-cache).
|
||||
|
||||
Because the default `MemoryStore` for [express-session](https://github.com/expressjs/session) will lead to a memory leak due to it haven't a suitable way to make them expire.
|
||||
|
||||
The sessions are still stored in memory, so they're not shared with other processes or services.
|
||||
|
||||
## Setup
|
||||
|
||||
$ npm install express-session memorystore
|
||||
|
||||
Pass the `express-session` store into `memorystore` to create a `MemoryStore` constructor.
|
||||
|
||||
```javascript
|
||||
const session = require('express-session')
|
||||
const MemoryStore = require('memorystore')(session)
|
||||
|
||||
app.use(session({
|
||||
cookie: { maxAge: 86400000 },
|
||||
store: new MemoryStore({
|
||||
checkPeriod: 86400000 // prune expired entries every 24h
|
||||
}),
|
||||
resave: false,
|
||||
secret: 'keyboard cat'
|
||||
}))
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
* `checkPeriod` Define how long MemoryStore will check for expired. The period is in ms. The automatic check is disabled by default! Not setting this is kind of silly, since that's the whole purpose of this lib.
|
||||
* `max` The maximum size of the cache, checked by applying the length
|
||||
function to all values in the cache. It defaults to `Infinity`.
|
||||
* `ttl` Session TTL (expiration) in milliseconds. Defaults to session.maxAge (if set), or one day. This may also be set to a function of the form `(options, sess, sessionID) => number`.
|
||||
* `dispose` Function that is called on sessions when they are dropped
|
||||
from the cache. This can be handy if you want to close file
|
||||
descriptors or do other cleanup tasks when sessions are no longer
|
||||
accessible. Called with `key, value`. It's called *before*
|
||||
actually removing the item from the internal cache, so if you want
|
||||
to immediately put it back in, you'll have to do that in a
|
||||
`nextTick` or `setTimeout` callback or it won't do anything.
|
||||
* `stale` By default, if you set a `maxAge`, it'll only actually pull
|
||||
stale items out of the cache when you `get(key)`. (That is, it's
|
||||
not pre-emptively doing a `setTimeout` or anything.) If you set
|
||||
`stale:true`, it'll return the stale value before deleting it. If
|
||||
you don't set this, then it'll return `undefined` when you try to
|
||||
get a stale entry, as if it had already been deleted.
|
||||
* `noDisposeOnSet` By default, if you set a `dispose()` method, then it'll be called whenever a `set()` operation overwrites an existing key. If you set this option, `dispose()` will only be called when a key falls out of the cache, not when it is overwritten.
|
||||
* `serializer` An object containing `stringify` and `parse` methods compatible with Javascript's `JSON` to override the serializer used.
|
||||
|
||||
## Methods
|
||||
|
||||
`memorystore` implements all the **required**, **recommended** and **optional** methods of the [express-session](https://github.com/expressjs/session#session-store-implementation) store. Plus a few more:
|
||||
|
||||
- `startInterval()` and `stopInterval()` methods to start/clear the automatic check for expired.
|
||||
|
||||
- `prune()` that you can use to manually remove only the expired entries from the store.
|
||||
|
||||
## Debug
|
||||
|
||||
To enable debug set the env var `DEBUG=memorystore`
|
||||
|
||||
# Author
|
||||
|
||||
Rocco Musolino ([@roccomuso](https://twitter.com/roccomuso))
|
||||
|
||||
# License
|
||||
|
||||
MIT
|
|
@ -0,0 +1,81 @@
|
|||
import expressSession, {
|
||||
MemoryStore as ExpressMemoryStore,
|
||||
} from "express-session";
|
||||
|
||||
interface MemoryStoreOptions {
|
||||
/**
|
||||
* Define how long MemoryStore will check for expired.
|
||||
* The period is in ms. The automatic check is disabled by default!
|
||||
* Not setting this is kind of silly, since that's the whole purpose of
|
||||
* this lib.
|
||||
*/
|
||||
checkPeriod?: number;
|
||||
/**
|
||||
* The maximum size of the cache, checked by applying the length
|
||||
* function to all values in the cache. It defaults to `Infinity`.
|
||||
*/
|
||||
max?: number;
|
||||
/**
|
||||
* Session TTL (expiration) in milliseconds.
|
||||
* Defaults to `session.maxAge` (if set), or one day.
|
||||
*/
|
||||
ttl?: number | ((options: any, sess: any, sessionId: any) => number);
|
||||
/**
|
||||
* Function that is called on sessions when they are dropped from the
|
||||
* cache. This can be handy if you want to close file descriptors or do
|
||||
* other cleanup tasks when sessions are no longer accessible. It's
|
||||
* called before actually removing the item from the internal cache, so
|
||||
* if you want to immediately put it back in, you'll have to do that in
|
||||
* a `nextTick` or `setTimeout` callback or it won't do anything.
|
||||
*/
|
||||
dispose?: (key: any, value: any) => void;
|
||||
/**
|
||||
* By default, if you set a `maxAge`, it'll only actually pull stale
|
||||
* items out of the cache when you `get(key)`. (That is, it's not
|
||||
* pre-emptively doing a setTimeout or anything.) If you set
|
||||
* `stale:true`, it'll return the stale value before deleting it. If
|
||||
* you don't set this, then it'll return undefined when you try to get
|
||||
* a stale entry, as if it had already been deleted.
|
||||
*/
|
||||
stale?: boolean;
|
||||
/**
|
||||
* By default, if you set a `dispose()` method, then it'll be called
|
||||
* whenever a `set()` operation overwrites an existing key. If you set
|
||||
* this option, `dispose()` will only be called when a key falls out of
|
||||
* the cache, not when it is overwritten.
|
||||
*/
|
||||
noDisposeOnSet?: boolean;
|
||||
/**
|
||||
* An object compatible with Javascript's JSON to override the
|
||||
* serializer used.
|
||||
*/
|
||||
serializer?: {
|
||||
stringify: (arg: object) => string;
|
||||
parse: (str: string) => object;
|
||||
};
|
||||
}
|
||||
|
||||
declare class MemoryStore extends ExpressMemoryStore {
|
||||
constructor(options: MemoryStoreOptions);
|
||||
/** method to start the automatic check for expired. */
|
||||
startInterval(): void;
|
||||
/** method to clear the automatic check for expired. */
|
||||
stopInterval(): void;
|
||||
/** use to manually remove only the expired entries from the store. */
|
||||
prune(): void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sample usage:
|
||||
* ```
|
||||
* import session from 'express-session';
|
||||
* import createMemoryStore from 'memorystore';
|
||||
* const MemoryStore = createMemoryStore(session);
|
||||
* ...
|
||||
* app.use(session({ store: new MemoryStore({ ...options }) }));
|
||||
* ```
|
||||
*/
|
||||
declare function createMemoryStore(
|
||||
session: typeof expressSession
|
||||
): typeof MemoryStore;
|
||||
export = createMemoryStore;
|
|
@ -0,0 +1 @@
|
|||
module.exports = require('./lib/memorystore.js')
|
|
@ -0,0 +1,295 @@
|
|||
/*!
|
||||
* memorystore
|
||||
* Copyright(c) 2020 Rocco Musolino <@roccomuso>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
var debug = require('debug')('memorystore')
|
||||
var LRU = require('lru-cache')
|
||||
var util = require('util')
|
||||
|
||||
/**
|
||||
* One day in milliseconds.
|
||||
*/
|
||||
|
||||
var oneDay = 86400000
|
||||
|
||||
function getTTL (options, sess, sid) {
|
||||
if (typeof options.ttl === 'number') return options.ttl
|
||||
if (typeof options.ttl === 'function') return options.ttl(options, sess, sid)
|
||||
if (options.ttl) throw new TypeError('`options.ttl` must be a number or function.')
|
||||
|
||||
var maxAge = (sess && sess.cookie) ? sess.cookie.maxAge : null
|
||||
return (typeof maxAge === 'number'
|
||||
? Math.floor(maxAge)
|
||||
: oneDay)
|
||||
}
|
||||
|
||||
function prune (store) {
|
||||
debug('Pruning expired entries')
|
||||
store.forEach(function (value, key) {
|
||||
store.get(key)
|
||||
})
|
||||
}
|
||||
|
||||
var defer = typeof setImmediate === 'function'
|
||||
? setImmediate
|
||||
: function (fn) { process.nextTick(fn.bind.apply(fn, arguments)) }
|
||||
|
||||
/**
|
||||
* Return the `MemoryStore` extending `express`'s session Store.
|
||||
*
|
||||
* @param {object} express session
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
module.exports = function (session) {
|
||||
/**
|
||||
* Express's session Store.
|
||||
*/
|
||||
|
||||
var Store = session.Store
|
||||
|
||||
/**
|
||||
* Initialize MemoryStore with the given `options`.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function MemoryStore (options) {
|
||||
if (!(this instanceof MemoryStore)) {
|
||||
throw new TypeError('Cannot call MemoryStore constructor as a function')
|
||||
}
|
||||
|
||||
options = options || {}
|
||||
Store.call(this, options)
|
||||
|
||||
this.options = {}
|
||||
this.options.checkPeriod = options.checkPeriod
|
||||
this.options.max = options.max || Infinity
|
||||
this.options.ttl = options.ttl
|
||||
this.options.dispose = options.dispose
|
||||
this.options.stale = options.stale
|
||||
this.options.noDisposeOnSet = options.noDisposeOnSet
|
||||
|
||||
this.serializer = options.serializer || JSON
|
||||
this.store = LRU(this.options)
|
||||
debug('Init MemoryStore')
|
||||
|
||||
this.startInterval()
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from `Store`.
|
||||
*/
|
||||
|
||||
util.inherits(MemoryStore, Store)
|
||||
|
||||
/**
|
||||
* Attempt to fetch session by the given `sid`.
|
||||
*
|
||||
* @param {String} sid
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.get = function (sid, fn) {
|
||||
var store = this.store
|
||||
|
||||
debug('GET "%s"', sid)
|
||||
|
||||
var data = store.get(sid)
|
||||
if (!data) return fn()
|
||||
|
||||
debug('GOT %s', data)
|
||||
var err = null
|
||||
var result
|
||||
try {
|
||||
result = this.serializer.parse(data)
|
||||
} catch (er) {
|
||||
err = er
|
||||
}
|
||||
|
||||
fn && defer(fn, err, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Commit the given `sess` object associated with the given `sid`.
|
||||
*
|
||||
* @param {String} sid
|
||||
* @param {Session} sess
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.set = function (sid, sess, fn) {
|
||||
var store = this.store
|
||||
|
||||
var ttl = getTTL(this.options, sess, sid)
|
||||
try {
|
||||
var jsess = this.serializer.stringify(sess)
|
||||
} catch (err) {
|
||||
fn && defer(fn, err)
|
||||
}
|
||||
|
||||
store.set(sid, jsess, ttl)
|
||||
debug('SET "%s" %s ttl:%s', sid, jsess, ttl)
|
||||
fn && defer(fn, null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the session associated with the given `sid`.
|
||||
*
|
||||
* @param {String} sid
|
||||
* @api public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.destroy = function (sid, fn) {
|
||||
var store = this.store
|
||||
|
||||
if (Array.isArray(sid)) {
|
||||
sid.forEach(function (s) {
|
||||
debug('DEL "%s"', s)
|
||||
store.del(s)
|
||||
})
|
||||
} else {
|
||||
debug('DEL "%s"', sid)
|
||||
store.del(sid)
|
||||
}
|
||||
fn && defer(fn, null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh the time-to-live for the session with the given `sid`.
|
||||
*
|
||||
* @param {String} sid
|
||||
* @param {Session} sess
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.touch = function (sid, sess, fn) {
|
||||
var store = this.store
|
||||
|
||||
var ttl = getTTL(this.options, sess, sid)
|
||||
|
||||
debug('EXPIRE "%s" ttl:%s', sid, ttl)
|
||||
var err = null
|
||||
if (store.get(sid) !== undefined) {
|
||||
try {
|
||||
var s = this.serializer.parse(store.get(sid))
|
||||
s.cookie = sess.cookie
|
||||
store.set(sid, this.serializer.stringify(s), ttl)
|
||||
} catch (e) {
|
||||
err = e
|
||||
}
|
||||
}
|
||||
fn && defer(fn, err)
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all sessions' ids
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.ids = function (fn) {
|
||||
var store = this.store
|
||||
|
||||
var Ids = store.keys()
|
||||
debug('Getting IDs: %s', Ids)
|
||||
fn && defer(fn, null, Ids)
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch all sessions
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.all = function (fn) {
|
||||
var store = this.store
|
||||
var self = this
|
||||
|
||||
debug('Fetching all sessions')
|
||||
var err = null
|
||||
var result = {}
|
||||
try {
|
||||
store.forEach(function (val, key) {
|
||||
result[key] = self.serializer.parse(val)
|
||||
})
|
||||
} catch (e) {
|
||||
err = e
|
||||
}
|
||||
fn && defer(fn, err, result)
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete all sessions from the store
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.clear = function (fn) {
|
||||
var store = this.store
|
||||
debug('delete all sessions from the store')
|
||||
store.reset()
|
||||
fn && defer(fn, null)
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the count of all sessions in the store
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.length = function (fn) {
|
||||
var store = this.store
|
||||
debug('getting length', store.itemCount)
|
||||
fn && defer(fn, null, store.itemCount)
|
||||
}
|
||||
|
||||
/**
|
||||
* Start the check interval
|
||||
* @api public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.startInterval = function () {
|
||||
var self = this
|
||||
var ms = this.options.checkPeriod
|
||||
if (ms && typeof ms === 'number') {
|
||||
clearInterval(this._checkInterval)
|
||||
debug('starting periodic check for expired sessions')
|
||||
this._checkInterval = setInterval(function () {
|
||||
prune(self.store) // iterates over the entire cache proactively pruning old entries
|
||||
}, Math.floor(ms)).unref()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the check interval
|
||||
* @api public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.stopInterval = function () {
|
||||
debug('stopping periodic check for expired sessions')
|
||||
clearInterval(this._checkInterval)
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove only expired entries from the store
|
||||
* @api public
|
||||
*/
|
||||
|
||||
MemoryStore.prototype.prune = function () {
|
||||
prune(this.store)
|
||||
}
|
||||
|
||||
return MemoryStore
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
(The MIT License)
|
||||
|
||||
Copyright (c) 2014-2017 TJ Holowaychuk <tj@vision-media.ca>
|
||||
Copyright (c) 2018-2021 Josh Junon
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
|
||||
and associated documentation files (the 'Software'), to deal in the Software without restriction,
|
||||
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial
|
||||
portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
|
||||
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
|
@ -0,0 +1,481 @@
|
|||
# debug
|
||||
[![Build Status](https://travis-ci.org/debug-js/debug.svg?branch=master)](https://travis-ci.org/debug-js/debug) [![Coverage Status](https://coveralls.io/repos/github/debug-js/debug/badge.svg?branch=master)](https://coveralls.io/github/debug-js/debug?branch=master) [![Slack](https://visionmedia-community-slackin.now.sh/badge.svg)](https://visionmedia-community-slackin.now.sh/) [![OpenCollective](https://opencollective.com/debug/backers/badge.svg)](#backers)
|
||||
[![OpenCollective](https://opencollective.com/debug/sponsors/badge.svg)](#sponsors)
|
||||
|
||||
<img width="647" src="https://user-images.githubusercontent.com/71256/29091486-fa38524c-7c37-11e7-895f-e7ec8e1039b6.png">
|
||||
|
||||
A tiny JavaScript debugging utility modelled after Node.js core's debugging
|
||||
technique. Works in Node.js and web browsers.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
$ npm install debug
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
`debug` exposes a function; simply pass this function the name of your module, and it will return a decorated version of `console.error` for you to pass debug statements to. This will allow you to toggle the debug output for different parts of your module as well as the module as a whole.
|
||||
|
||||
Example [_app.js_](./examples/node/app.js):
|
||||
|
||||
```js
|
||||
var debug = require('debug')('http')
|
||||
, http = require('http')
|
||||
, name = 'My App';
|
||||
|
||||
// fake app
|
||||
|
||||
debug('booting %o', name);
|
||||
|
||||
http.createServer(function(req, res){
|
||||
debug(req.method + ' ' + req.url);
|
||||
res.end('hello\n');
|
||||
}).listen(3000, function(){
|
||||
debug('listening');
|
||||
});
|
||||
|
||||
// fake worker of some kind
|
||||
|
||||
require('./worker');
|
||||
```
|
||||
|
||||
Example [_worker.js_](./examples/node/worker.js):
|
||||
|
||||
```js
|
||||
var a = require('debug')('worker:a')
|
||||
, b = require('debug')('worker:b');
|
||||
|
||||
function work() {
|
||||
a('doing lots of uninteresting work');
|
||||
setTimeout(work, Math.random() * 1000);
|
||||
}
|
||||
|
||||
work();
|
||||
|
||||
function workb() {
|
||||
b('doing some work');
|
||||
setTimeout(workb, Math.random() * 2000);
|
||||
}
|
||||
|
||||
workb();
|
||||
```
|
||||
|
||||
The `DEBUG` environment variable is then used to enable these based on space or
|
||||
comma-delimited names.
|
||||
|
||||
Here are some examples:
|
||||
|
||||
<img width="647" alt="screen shot 2017-08-08 at 12 53 04 pm" src="https://user-images.githubusercontent.com/71256/29091703-a6302cdc-7c38-11e7-8304-7c0b3bc600cd.png">
|
||||
<img width="647" alt="screen shot 2017-08-08 at 12 53 38 pm" src="https://user-images.githubusercontent.com/71256/29091700-a62a6888-7c38-11e7-800b-db911291ca2b.png">
|
||||
<img width="647" alt="screen shot 2017-08-08 at 12 53 25 pm" src="https://user-images.githubusercontent.com/71256/29091701-a62ea114-7c38-11e7-826a-2692bedca740.png">
|
||||
|
||||
#### Windows command prompt notes
|
||||
|
||||
##### CMD
|
||||
|
||||
On Windows the environment variable is set using the `set` command.
|
||||
|
||||
```cmd
|
||||
set DEBUG=*,-not_this
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```cmd
|
||||
set DEBUG=* & node app.js
|
||||
```
|
||||
|
||||
##### PowerShell (VS Code default)
|
||||
|
||||
PowerShell uses different syntax to set environment variables.
|
||||
|
||||
```cmd
|
||||
$env:DEBUG = "*,-not_this"
|
||||
```
|
||||
|
||||
Example:
|
||||
|
||||
```cmd
|
||||
$env:DEBUG='app';node app.js
|
||||
```
|
||||
|
||||
Then, run the program to be debugged as usual.
|
||||
|
||||
npm script example:
|
||||
```js
|
||||
"windowsDebug": "@powershell -Command $env:DEBUG='*';node app.js",
|
||||
```
|
||||
|
||||
## Namespace Colors
|
||||
|
||||
Every debug instance has a color generated for it based on its namespace name.
|
||||
This helps when visually parsing the debug output to identify which debug instance
|
||||
a debug line belongs to.
|
||||
|
||||
#### Node.js
|
||||
|
||||
In Node.js, colors are enabled when stderr is a TTY. You also _should_ install
|
||||
the [`supports-color`](https://npmjs.org/supports-color) module alongside debug,
|
||||
otherwise debug will only use a small handful of basic colors.
|
||||
|
||||
<img width="521" src="https://user-images.githubusercontent.com/71256/29092181-47f6a9e6-7c3a-11e7-9a14-1928d8a711cd.png">
|
||||
|
||||
#### Web Browser
|
||||
|
||||
Colors are also enabled on "Web Inspectors" that understand the `%c` formatting
|
||||
option. These are WebKit web inspectors, Firefox ([since version
|
||||
31](https://hacks.mozilla.org/2014/05/editable-box-model-multiple-selection-sublime-text-keys-much-more-firefox-developer-tools-episode-31/))
|
||||
and the Firebug plugin for Firefox (any version).
|
||||
|
||||
<img width="524" src="https://user-images.githubusercontent.com/71256/29092033-b65f9f2e-7c39-11e7-8e32-f6f0d8e865c1.png">
|
||||
|
||||
|
||||
## Millisecond diff
|
||||
|
||||
When actively developing an application it can be useful to see when the time spent between one `debug()` call and the next. Suppose for example you invoke `debug()` before requesting a resource, and after as well, the "+NNNms" will show you how much time was spent between calls.
|
||||
|
||||
<img width="647" src="https://user-images.githubusercontent.com/71256/29091486-fa38524c-7c37-11e7-895f-e7ec8e1039b6.png">
|
||||
|
||||
When stdout is not a TTY, `Date#toISOString()` is used, making it more useful for logging the debug information as shown below:
|
||||
|
||||
<img width="647" src="https://user-images.githubusercontent.com/71256/29091956-6bd78372-7c39-11e7-8c55-c948396d6edd.png">
|
||||
|
||||
|
||||
## Conventions
|
||||
|
||||
If you're using this in one or more of your libraries, you _should_ use the name of your library so that developers may toggle debugging as desired without guessing names. If you have more than one debuggers you _should_ prefix them with your library name and use ":" to separate features. For example "bodyParser" from Connect would then be "connect:bodyParser". If you append a "*" to the end of your name, it will always be enabled regardless of the setting of the DEBUG environment variable. You can then use it for normal output as well as debug output.
|
||||
|
||||
## Wildcards
|
||||
|
||||
The `*` character may be used as a wildcard. Suppose for example your library has
|
||||
debuggers named "connect:bodyParser", "connect:compress", "connect:session",
|
||||
instead of listing all three with
|
||||
`DEBUG=connect:bodyParser,connect:compress,connect:session`, you may simply do
|
||||
`DEBUG=connect:*`, or to run everything using this module simply use `DEBUG=*`.
|
||||
|
||||
You can also exclude specific debuggers by prefixing them with a "-" character.
|
||||
For example, `DEBUG=*,-connect:*` would include all debuggers except those
|
||||
starting with "connect:".
|
||||
|
||||
## Environment Variables
|
||||
|
||||
When running through Node.js, you can set a few environment variables that will
|
||||
change the behavior of the debug logging:
|
||||
|
||||
| Name | Purpose |
|
||||
|-----------|-------------------------------------------------|
|
||||
| `DEBUG` | Enables/disables specific debugging namespaces. |
|
||||
| `DEBUG_HIDE_DATE` | Hide date from debug output (non-TTY). |
|
||||
| `DEBUG_COLORS`| Whether or not to use colors in the debug output. |
|
||||
| `DEBUG_DEPTH` | Object inspection depth. |
|
||||
| `DEBUG_SHOW_HIDDEN` | Shows hidden properties on inspected objects. |
|
||||
|
||||
|
||||
__Note:__ The environment variables beginning with `DEBUG_` end up being
|
||||
converted into an Options object that gets used with `%o`/`%O` formatters.
|
||||
See the Node.js documentation for
|
||||
[`util.inspect()`](https://nodejs.org/api/util.html#util_util_inspect_object_options)
|
||||
for the complete list.
|
||||
|
||||
## Formatters
|
||||
|
||||
Debug uses [printf-style](https://wikipedia.org/wiki/Printf_format_string) formatting.
|
||||
Below are the officially supported formatters:
|
||||
|
||||
| Formatter | Representation |
|
||||
|-----------|----------------|
|
||||
| `%O` | Pretty-print an Object on multiple lines. |
|
||||
| `%o` | Pretty-print an Object all on a single line. |
|
||||
| `%s` | String. |
|
||||
| `%d` | Number (both integer and float). |
|
||||
| `%j` | JSON. Replaced with the string '[Circular]' if the argument contains circular references. |
|
||||
| `%%` | Single percent sign ('%'). This does not consume an argument. |
|
||||
|
||||
|
||||
### Custom formatters
|
||||
|
||||
You can add custom formatters by extending the `debug.formatters` object.
|
||||
For example, if you wanted to add support for rendering a Buffer as hex with
|
||||
`%h`, you could do something like:
|
||||
|
||||
```js
|
||||
const createDebug = require('debug')
|
||||
createDebug.formatters.h = (v) => {
|
||||
return v.toString('hex')
|
||||
}
|
||||
|
||||
// …elsewhere
|
||||
const debug = createDebug('foo')
|
||||
debug('this is hex: %h', new Buffer('hello world'))
|
||||
// foo this is hex: 68656c6c6f20776f726c6421 +0ms
|
||||
```
|
||||
|
||||
|
||||
## Browser Support
|
||||
|
||||
You can build a browser-ready script using [browserify](https://github.com/substack/node-browserify),
|
||||
or just use the [browserify-as-a-service](https://wzrd.in/) [build](https://wzrd.in/standalone/debug@latest),
|
||||
if you don't want to build it yourself.
|
||||
|
||||
Debug's enable state is currently persisted by `localStorage`.
|
||||
Consider the situation shown below where you have `worker:a` and `worker:b`,
|
||||
and wish to debug both. You can enable this using `localStorage.debug`:
|
||||
|
||||
```js
|
||||
localStorage.debug = 'worker:*'
|
||||
```
|
||||
|
||||
And then refresh the page.
|
||||
|
||||
```js
|
||||
a = debug('worker:a');
|
||||
b = debug('worker:b');
|
||||
|
||||
setInterval(function(){
|
||||
a('doing some work');
|
||||
}, 1000);
|
||||
|
||||
setInterval(function(){
|
||||
b('doing some work');
|
||||
}, 1200);
|
||||
```
|
||||
|
||||
In Chromium-based web browsers (e.g. Brave, Chrome, and Electron), the JavaScript console will—by default—only show messages logged by `debug` if the "Verbose" log level is _enabled_.
|
||||
|
||||
<img width="647" src="https://user-images.githubusercontent.com/7143133/152083257-29034707-c42c-4959-8add-3cee850e6fcf.png">
|
||||
|
||||
## Output streams
|
||||
|
||||
By default `debug` will log to stderr, however this can be configured per-namespace by overriding the `log` method:
|
||||
|
||||
Example [_stdout.js_](./examples/node/stdout.js):
|
||||
|
||||
```js
|
||||
var debug = require('debug');
|
||||
var error = debug('app:error');
|
||||
|
||||
// by default stderr is used
|
||||
error('goes to stderr!');
|
||||
|
||||
var log = debug('app:log');
|
||||
// set this namespace to log via console.log
|
||||
log.log = console.log.bind(console); // don't forget to bind to console!
|
||||
log('goes to stdout');
|
||||
error('still goes to stderr!');
|
||||
|
||||
// set all output to go via console.info
|
||||
// overrides all per-namespace log settings
|
||||
debug.log = console.info.bind(console);
|
||||
error('now goes to stdout via console.info');
|
||||
log('still goes to stdout, but via console.info now');
|
||||
```
|
||||
|
||||
## Extend
|
||||
You can simply extend debugger
|
||||
```js
|
||||
const log = require('debug')('auth');
|
||||
|
||||
//creates new debug instance with extended namespace
|
||||
const logSign = log.extend('sign');
|
||||
const logLogin = log.extend('login');
|
||||
|
||||
log('hello'); // auth hello
|
||||
logSign('hello'); //auth:sign hello
|
||||
logLogin('hello'); //auth:login hello
|
||||
```
|
||||
|
||||
## Set dynamically
|
||||
|
||||
You can also enable debug dynamically by calling the `enable()` method :
|
||||
|
||||
```js
|
||||
let debug = require('debug');
|
||||
|
||||
console.log(1, debug.enabled('test'));
|
||||
|
||||
debug.enable('test');
|
||||
console.log(2, debug.enabled('test'));
|
||||
|
||||
debug.disable();
|
||||
console.log(3, debug.enabled('test'));
|
||||
|
||||
```
|
||||
|
||||
print :
|
||||
```
|
||||
1 false
|
||||
2 true
|
||||
3 false
|
||||
```
|
||||
|
||||
Usage :
|
||||
`enable(namespaces)`
|
||||
`namespaces` can include modes separated by a colon and wildcards.
|
||||
|
||||
Note that calling `enable()` completely overrides previously set DEBUG variable :
|
||||
|
||||
```
|
||||
$ DEBUG=foo node -e 'var dbg = require("debug"); dbg.enable("bar"); console.log(dbg.enabled("foo"))'
|
||||
=> false
|
||||
```
|
||||
|
||||
`disable()`
|
||||
|
||||
Will disable all namespaces. The functions returns the namespaces currently
|
||||
enabled (and skipped). This can be useful if you want to disable debugging
|
||||
temporarily without knowing what was enabled to begin with.
|
||||
|
||||
For example:
|
||||
|
||||
```js
|
||||
let debug = require('debug');
|
||||
debug.enable('foo:*,-foo:bar');
|
||||
let namespaces = debug.disable();
|
||||
debug.enable(namespaces);
|
||||
```
|
||||
|
||||
Note: There is no guarantee that the string will be identical to the initial
|
||||
enable string, but semantically they will be identical.
|
||||
|
||||
## Checking whether a debug target is enabled
|
||||
|
||||
After you've created a debug instance, you can determine whether or not it is
|
||||
enabled by checking the `enabled` property:
|
||||
|
||||
```javascript
|
||||
const debug = require('debug')('http');
|
||||
|
||||
if (debug.enabled) {
|
||||
// do stuff...
|
||||
}
|
||||
```
|
||||
|
||||
You can also manually toggle this property to force the debug instance to be
|
||||
enabled or disabled.
|
||||
|
||||
## Usage in child processes
|
||||
|
||||
Due to the way `debug` detects if the output is a TTY or not, colors are not shown in child processes when `stderr` is piped. A solution is to pass the `DEBUG_COLORS=1` environment variable to the child process.
|
||||
For example:
|
||||
|
||||
```javascript
|
||||
worker = fork(WORKER_WRAP_PATH, [workerPath], {
|
||||
stdio: [
|
||||
/* stdin: */ 0,
|
||||
/* stdout: */ 'pipe',
|
||||
/* stderr: */ 'pipe',
|
||||
'ipc',
|
||||
],
|
||||
env: Object.assign({}, process.env, {
|
||||
DEBUG_COLORS: 1 // without this settings, colors won't be shown
|
||||
}),
|
||||
});
|
||||
|
||||
worker.stderr.pipe(process.stderr, { end: false });
|
||||
```
|
||||
|
||||
|
||||
## Authors
|
||||
|
||||
- TJ Holowaychuk
|
||||
- Nathan Rajlich
|
||||
- Andrew Rhyne
|
||||
- Josh Junon
|
||||
|
||||
## Backers
|
||||
|
||||
Support us with a monthly donation and help us continue our activities. [[Become a backer](https://opencollective.com/debug#backer)]
|
||||
|
||||
<a href="https://opencollective.com/debug/backer/0/website" target="_blank"><img src="https://opencollective.com/debug/backer/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/1/website" target="_blank"><img src="https://opencollective.com/debug/backer/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/2/website" target="_blank"><img src="https://opencollective.com/debug/backer/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/3/website" target="_blank"><img src="https://opencollective.com/debug/backer/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/4/website" target="_blank"><img src="https://opencollective.com/debug/backer/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/5/website" target="_blank"><img src="https://opencollective.com/debug/backer/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/6/website" target="_blank"><img src="https://opencollective.com/debug/backer/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/7/website" target="_blank"><img src="https://opencollective.com/debug/backer/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/8/website" target="_blank"><img src="https://opencollective.com/debug/backer/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/9/website" target="_blank"><img src="https://opencollective.com/debug/backer/9/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/10/website" target="_blank"><img src="https://opencollective.com/debug/backer/10/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/11/website" target="_blank"><img src="https://opencollective.com/debug/backer/11/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/12/website" target="_blank"><img src="https://opencollective.com/debug/backer/12/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/13/website" target="_blank"><img src="https://opencollective.com/debug/backer/13/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/14/website" target="_blank"><img src="https://opencollective.com/debug/backer/14/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/15/website" target="_blank"><img src="https://opencollective.com/debug/backer/15/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/16/website" target="_blank"><img src="https://opencollective.com/debug/backer/16/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/17/website" target="_blank"><img src="https://opencollective.com/debug/backer/17/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/18/website" target="_blank"><img src="https://opencollective.com/debug/backer/18/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/19/website" target="_blank"><img src="https://opencollective.com/debug/backer/19/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/20/website" target="_blank"><img src="https://opencollective.com/debug/backer/20/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/21/website" target="_blank"><img src="https://opencollective.com/debug/backer/21/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/22/website" target="_blank"><img src="https://opencollective.com/debug/backer/22/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/23/website" target="_blank"><img src="https://opencollective.com/debug/backer/23/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/24/website" target="_blank"><img src="https://opencollective.com/debug/backer/24/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/25/website" target="_blank"><img src="https://opencollective.com/debug/backer/25/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/26/website" target="_blank"><img src="https://opencollective.com/debug/backer/26/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/27/website" target="_blank"><img src="https://opencollective.com/debug/backer/27/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/28/website" target="_blank"><img src="https://opencollective.com/debug/backer/28/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/backer/29/website" target="_blank"><img src="https://opencollective.com/debug/backer/29/avatar.svg"></a>
|
||||
|
||||
|
||||
## Sponsors
|
||||
|
||||
Become a sponsor and get your logo on our README on Github with a link to your site. [[Become a sponsor](https://opencollective.com/debug#sponsor)]
|
||||
|
||||
<a href="https://opencollective.com/debug/sponsor/0/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/0/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/1/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/1/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/2/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/2/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/3/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/3/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/4/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/4/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/5/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/5/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/6/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/6/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/7/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/7/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/8/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/8/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/9/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/9/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/10/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/10/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/11/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/11/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/12/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/12/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/13/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/13/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/14/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/14/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/15/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/15/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/16/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/16/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/17/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/17/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/18/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/18/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/19/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/19/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/20/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/20/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/21/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/21/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/22/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/22/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/23/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/23/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/24/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/24/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/25/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/25/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/26/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/26/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/27/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/27/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/28/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/28/avatar.svg"></a>
|
||||
<a href="https://opencollective.com/debug/sponsor/29/website" target="_blank"><img src="https://opencollective.com/debug/sponsor/29/avatar.svg"></a>
|
||||
|
||||
## License
|
||||
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2014-2017 TJ Holowaychuk <tj@vision-media.ca>
|
||||
Copyright (c) 2018-2021 Josh Junon
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"name": "debug",
|
||||
"version": "4.3.4",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/debug-js/debug.git"
|
||||
},
|
||||
"description": "Lightweight debugging utility for Node.js and the browser",
|
||||
"keywords": [
|
||||
"debug",
|
||||
"log",
|
||||
"debugger"
|
||||
],
|
||||
"files": [
|
||||
"src",
|
||||
"LICENSE",
|
||||
"README.md"
|
||||
],
|
||||
"author": "Josh Junon <josh.junon@protonmail.com>",
|
||||
"contributors": [
|
||||
"TJ Holowaychuk <tj@vision-media.ca>",
|
||||
"Nathan Rajlich <nathan@tootallnate.net> (http://n8.io)",
|
||||
"Andrew Rhyne <rhyneandrew@gmail.com>"
|
||||
],
|
||||
"license": "MIT",
|
||||
"scripts": {
|
||||
"lint": "xo",
|
||||
"test": "npm run test:node && npm run test:browser && npm run lint",
|
||||
"test:node": "istanbul cover _mocha -- test.js",
|
||||
"test:browser": "karma start --single-run",
|
||||
"test:coverage": "cat ./coverage/lcov.info | coveralls"
|
||||
},
|
||||
"dependencies": {
|
||||
"ms": "2.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"brfs": "^2.0.1",
|
||||
"browserify": "^16.2.3",
|
||||
"coveralls": "^3.0.2",
|
||||
"istanbul": "^0.4.5",
|
||||
"karma": "^3.1.4",
|
||||
"karma-browserify": "^6.0.0",
|
||||
"karma-chrome-launcher": "^2.2.0",
|
||||
"karma-mocha": "^1.3.0",
|
||||
"mocha": "^5.2.0",
|
||||
"mocha-lcov-reporter": "^1.2.0",
|
||||
"xo": "^0.23.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"supports-color": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"main": "./src/index.js",
|
||||
"browser": "./src/browser.js",
|
||||
"engines": {
|
||||
"node": ">=6.0"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,269 @@
|
|||
/* eslint-env browser */
|
||||
|
||||
/**
|
||||
* This is the web browser implementation of `debug()`.
|
||||
*/
|
||||
|
||||
exports.formatArgs = formatArgs;
|
||||
exports.save = save;
|
||||
exports.load = load;
|
||||
exports.useColors = useColors;
|
||||
exports.storage = localstorage();
|
||||
exports.destroy = (() => {
|
||||
let warned = false;
|
||||
|
||||
return () => {
|
||||
if (!warned) {
|
||||
warned = true;
|
||||
console.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
/**
|
||||
* Colors.
|
||||
*/
|
||||
|
||||
exports.colors = [
|
||||
'#0000CC',
|
||||
'#0000FF',
|
||||
'#0033CC',
|
||||
'#0033FF',
|
||||
'#0066CC',
|
||||
'#0066FF',
|
||||
'#0099CC',
|
||||
'#0099FF',
|
||||
'#00CC00',
|
||||
'#00CC33',
|
||||
'#00CC66',
|
||||
'#00CC99',
|
||||
'#00CCCC',
|
||||
'#00CCFF',
|
||||
'#3300CC',
|
||||
'#3300FF',
|
||||
'#3333CC',
|
||||
'#3333FF',
|
||||
'#3366CC',
|
||||
'#3366FF',
|
||||
'#3399CC',
|
||||
'#3399FF',
|
||||
'#33CC00',
|
||||
'#33CC33',
|
||||
'#33CC66',
|
||||
'#33CC99',
|
||||
'#33CCCC',
|
||||
'#33CCFF',
|
||||
'#6600CC',
|
||||
'#6600FF',
|
||||
'#6633CC',
|
||||
'#6633FF',
|
||||
'#66CC00',
|
||||
'#66CC33',
|
||||
'#9900CC',
|
||||
'#9900FF',
|
||||
'#9933CC',
|
||||
'#9933FF',
|
||||
'#99CC00',
|
||||
'#99CC33',
|
||||
'#CC0000',
|
||||
'#CC0033',
|
||||
'#CC0066',
|
||||
'#CC0099',
|
||||
'#CC00CC',
|
||||
'#CC00FF',
|
||||
'#CC3300',
|
||||
'#CC3333',
|
||||
'#CC3366',
|
||||
'#CC3399',
|
||||
'#CC33CC',
|
||||
'#CC33FF',
|
||||
'#CC6600',
|
||||
'#CC6633',
|
||||
'#CC9900',
|
||||
'#CC9933',
|
||||
'#CCCC00',
|
||||
'#CCCC33',
|
||||
'#FF0000',
|
||||
'#FF0033',
|
||||
'#FF0066',
|
||||
'#FF0099',
|
||||
'#FF00CC',
|
||||
'#FF00FF',
|
||||
'#FF3300',
|
||||
'#FF3333',
|
||||
'#FF3366',
|
||||
'#FF3399',
|
||||
'#FF33CC',
|
||||
'#FF33FF',
|
||||
'#FF6600',
|
||||
'#FF6633',
|
||||
'#FF9900',
|
||||
'#FF9933',
|
||||
'#FFCC00',
|
||||
'#FFCC33'
|
||||
];
|
||||
|
||||
/**
|
||||
* Currently only WebKit-based Web Inspectors, Firefox >= v31,
|
||||
* and the Firebug extension (any Firefox version) are known
|
||||
* to support "%c" CSS customizations.
|
||||
*
|
||||
* TODO: add a `localStorage` variable to explicitly enable/disable colors
|
||||
*/
|
||||
|
||||
// eslint-disable-next-line complexity
|
||||
function useColors() {
|
||||
// NB: In an Electron preload script, document will be defined but not fully
|
||||
// initialized. Since we know we're in Chrome, we'll just detect this case
|
||||
// explicitly
|
||||
if (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Internet Explorer and Edge do not support colors.
|
||||
if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Is webkit? http://stackoverflow.com/a/16459606/376773
|
||||
// document is undefined in react-native: https://github.com/facebook/react-native/pull/1632
|
||||
return (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||
|
||||
// Is firebug? http://stackoverflow.com/a/398120/376773
|
||||
(typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||
|
||||
// Is firefox >= v31?
|
||||
// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages
|
||||
(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/) && parseInt(RegExp.$1, 10) >= 31) ||
|
||||
// Double check webkit in userAgent just in case we are in a worker
|
||||
(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/));
|
||||
}
|
||||
|
||||
/**
|
||||
* Colorize log arguments if enabled.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function formatArgs(args) {
|
||||
args[0] = (this.useColors ? '%c' : '') +
|
||||
this.namespace +
|
||||
(this.useColors ? ' %c' : ' ') +
|
||||
args[0] +
|
||||
(this.useColors ? '%c ' : ' ') +
|
||||
'+' + module.exports.humanize(this.diff);
|
||||
|
||||
if (!this.useColors) {
|
||||
return;
|
||||
}
|
||||
|
||||
const c = 'color: ' + this.color;
|
||||
args.splice(1, 0, c, 'color: inherit');
|
||||
|
||||
// The final "%c" is somewhat tricky, because there could be other
|
||||
// arguments passed either before or after the %c, so we need to
|
||||
// figure out the correct index to insert the CSS into
|
||||
let index = 0;
|
||||
let lastC = 0;
|
||||
args[0].replace(/%[a-zA-Z%]/g, match => {
|
||||
if (match === '%%') {
|
||||
return;
|
||||
}
|
||||
index++;
|
||||
if (match === '%c') {
|
||||
// We only are interested in the *last* %c
|
||||
// (the user may have provided their own)
|
||||
lastC = index;
|
||||
}
|
||||
});
|
||||
|
||||
args.splice(lastC, 0, c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes `console.debug()` when available.
|
||||
* No-op when `console.debug` is not a "function".
|
||||
* If `console.debug` is not available, falls back
|
||||
* to `console.log`.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
exports.log = console.debug || console.log || (() => {});
|
||||
|
||||
/**
|
||||
* Save `namespaces`.
|
||||
*
|
||||
* @param {String} namespaces
|
||||
* @api private
|
||||
*/
|
||||
function save(namespaces) {
|
||||
try {
|
||||
if (namespaces) {
|
||||
exports.storage.setItem('debug', namespaces);
|
||||
} else {
|
||||
exports.storage.removeItem('debug');
|
||||
}
|
||||
} catch (error) {
|
||||
// Swallow
|
||||
// XXX (@Qix-) should we be logging these?
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load `namespaces`.
|
||||
*
|
||||
* @return {String} returns the previously persisted debug modes
|
||||
* @api private
|
||||
*/
|
||||
function load() {
|
||||
let r;
|
||||
try {
|
||||
r = exports.storage.getItem('debug');
|
||||
} catch (error) {
|
||||
// Swallow
|
||||
// XXX (@Qix-) should we be logging these?
|
||||
}
|
||||
|
||||
// If debug isn't set in LS, and we're in Electron, try to load $DEBUG
|
||||
if (!r && typeof process !== 'undefined' && 'env' in process) {
|
||||
r = process.env.DEBUG;
|
||||
}
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Localstorage attempts to return the localstorage.
|
||||
*
|
||||
* This is necessary because safari throws
|
||||
* when a user disables cookies/localstorage
|
||||
* and you attempt to access it.
|
||||
*
|
||||
* @return {LocalStorage}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function localstorage() {
|
||||
try {
|
||||
// TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context
|
||||
// The Browser also has localStorage in the global context.
|
||||
return localStorage;
|
||||
} catch (error) {
|
||||
// Swallow
|
||||
// XXX (@Qix-) should we be logging these?
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = require('./common')(exports);
|
||||
|
||||
const {formatters} = module.exports;
|
||||
|
||||
/**
|
||||
* Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.
|
||||
*/
|
||||
|
||||
formatters.j = function (v) {
|
||||
try {
|
||||
return JSON.stringify(v);
|
||||
} catch (error) {
|
||||
return '[UnexpectedJSONParseError]: ' + error.message;
|
||||
}
|
||||
};
|
|
@ -0,0 +1,274 @@
|
|||
|
||||
/**
|
||||
* This is the common logic for both the Node.js and web browser
|
||||
* implementations of `debug()`.
|
||||
*/
|
||||
|
||||
function setup(env) {
|
||||
createDebug.debug = createDebug;
|
||||
createDebug.default = createDebug;
|
||||
createDebug.coerce = coerce;
|
||||
createDebug.disable = disable;
|
||||
createDebug.enable = enable;
|
||||
createDebug.enabled = enabled;
|
||||
createDebug.humanize = require('ms');
|
||||
createDebug.destroy = destroy;
|
||||
|
||||
Object.keys(env).forEach(key => {
|
||||
createDebug[key] = env[key];
|
||||
});
|
||||
|
||||
/**
|
||||
* The currently active debug mode names, and names to skip.
|
||||
*/
|
||||
|
||||
createDebug.names = [];
|
||||
createDebug.skips = [];
|
||||
|
||||
/**
|
||||
* Map of special "%n" handling functions, for the debug "format" argument.
|
||||
*
|
||||
* Valid key names are a single, lower or upper-case letter, i.e. "n" and "N".
|
||||
*/
|
||||
createDebug.formatters = {};
|
||||
|
||||
/**
|
||||
* Selects a color for a debug namespace
|
||||
* @param {String} namespace The namespace string for the debug instance to be colored
|
||||
* @return {Number|String} An ANSI color code for the given namespace
|
||||
* @api private
|
||||
*/
|
||||
function selectColor(namespace) {
|
||||
let hash = 0;
|
||||
|
||||
for (let i = 0; i < namespace.length; i++) {
|
||||
hash = ((hash << 5) - hash) + namespace.charCodeAt(i);
|
||||
hash |= 0; // Convert to 32bit integer
|
||||
}
|
||||
|
||||
return createDebug.colors[Math.abs(hash) % createDebug.colors.length];
|
||||
}
|
||||
createDebug.selectColor = selectColor;
|
||||
|
||||
/**
|
||||
* Create a debugger with the given `namespace`.
|
||||
*
|
||||
* @param {String} namespace
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
function createDebug(namespace) {
|
||||
let prevTime;
|
||||
let enableOverride = null;
|
||||
let namespacesCache;
|
||||
let enabledCache;
|
||||
|
||||
function debug(...args) {
|
||||
// Disabled?
|
||||
if (!debug.enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
const self = debug;
|
||||
|
||||
// Set `diff` timestamp
|
||||
const curr = Number(new Date());
|
||||
const ms = curr - (prevTime || curr);
|
||||
self.diff = ms;
|
||||
self.prev = prevTime;
|
||||
self.curr = curr;
|
||||
prevTime = curr;
|
||||
|
||||
args[0] = createDebug.coerce(args[0]);
|
||||
|
||||
if (typeof args[0] !== 'string') {
|
||||
// Anything else let's inspect with %O
|
||||
args.unshift('%O');
|
||||
}
|
||||
|
||||
// Apply any `formatters` transformations
|
||||
let index = 0;
|
||||
args[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => {
|
||||
// If we encounter an escaped % then don't increase the array index
|
||||
if (match === '%%') {
|
||||
return '%';
|
||||
}
|
||||
index++;
|
||||
const formatter = createDebug.formatters[format];
|
||||
if (typeof formatter === 'function') {
|
||||
const val = args[index];
|
||||
match = formatter.call(self, val);
|
||||
|
||||
// Now we need to remove `args[index]` since it's inlined in the `format`
|
||||
args.splice(index, 1);
|
||||
index--;
|
||||
}
|
||||
return match;
|
||||
});
|
||||
|
||||
// Apply env-specific formatting (colors, etc.)
|
||||
createDebug.formatArgs.call(self, args);
|
||||
|
||||
const logFn = self.log || createDebug.log;
|
||||
logFn.apply(self, args);
|
||||
}
|
||||
|
||||
debug.namespace = namespace;
|
||||
debug.useColors = createDebug.useColors();
|
||||
debug.color = createDebug.selectColor(namespace);
|
||||
debug.extend = extend;
|
||||
debug.destroy = createDebug.destroy; // XXX Temporary. Will be removed in the next major release.
|
||||
|
||||
Object.defineProperty(debug, 'enabled', {
|
||||
enumerable: true,
|
||||
configurable: false,
|
||||
get: () => {
|
||||
if (enableOverride !== null) {
|
||||
return enableOverride;
|
||||
}
|
||||
if (namespacesCache !== createDebug.namespaces) {
|
||||
namespacesCache = createDebug.namespaces;
|
||||
enabledCache = createDebug.enabled(namespace);
|
||||
}
|
||||
|
||||
return enabledCache;
|
||||
},
|
||||
set: v => {
|
||||
enableOverride = v;
|
||||
}
|
||||
});
|
||||
|
||||
// Env-specific initialization logic for debug instances
|
||||
if (typeof createDebug.init === 'function') {
|
||||
createDebug.init(debug);
|
||||
}
|
||||
|
||||
return debug;
|
||||
}
|
||||
|
||||
function extend(namespace, delimiter) {
|
||||
const newDebug = createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace);
|
||||
newDebug.log = this.log;
|
||||
return newDebug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables a debug mode by namespaces. This can include modes
|
||||
* separated by a colon and wildcards.
|
||||
*
|
||||
* @param {String} namespaces
|
||||
* @api public
|
||||
*/
|
||||
function enable(namespaces) {
|
||||
createDebug.save(namespaces);
|
||||
createDebug.namespaces = namespaces;
|
||||
|
||||
createDebug.names = [];
|
||||
createDebug.skips = [];
|
||||
|
||||
let i;
|
||||
const split = (typeof namespaces === 'string' ? namespaces : '').split(/[\s,]+/);
|
||||
const len = split.length;
|
||||
|
||||
for (i = 0; i < len; i++) {
|
||||
if (!split[i]) {
|
||||
// ignore empty strings
|
||||
continue;
|
||||
}
|
||||
|
||||
namespaces = split[i].replace(/\*/g, '.*?');
|
||||
|
||||
if (namespaces[0] === '-') {
|
||||
createDebug.skips.push(new RegExp('^' + namespaces.slice(1) + '$'));
|
||||
} else {
|
||||
createDebug.names.push(new RegExp('^' + namespaces + '$'));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Disable debug output.
|
||||
*
|
||||
* @return {String} namespaces
|
||||
* @api public
|
||||
*/
|
||||
function disable() {
|
||||
const namespaces = [
|
||||
...createDebug.names.map(toNamespace),
|
||||
...createDebug.skips.map(toNamespace).map(namespace => '-' + namespace)
|
||||
].join(',');
|
||||
createDebug.enable('');
|
||||
return namespaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given mode name is enabled, false otherwise.
|
||||
*
|
||||
* @param {String} name
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
function enabled(name) {
|
||||
if (name[name.length - 1] === '*') {
|
||||
return true;
|
||||
}
|
||||
|
||||
let i;
|
||||
let len;
|
||||
|
||||
for (i = 0, len = createDebug.skips.length; i < len; i++) {
|
||||
if (createDebug.skips[i].test(name)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0, len = createDebug.names.length; i < len; i++) {
|
||||
if (createDebug.names[i].test(name)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert regexp to namespace
|
||||
*
|
||||
* @param {RegExp} regxep
|
||||
* @return {String} namespace
|
||||
* @api private
|
||||
*/
|
||||
function toNamespace(regexp) {
|
||||
return regexp.toString()
|
||||
.substring(2, regexp.toString().length - 2)
|
||||
.replace(/\.\*\?$/, '*');
|
||||
}
|
||||
|
||||
/**
|
||||
* Coerce `val`.
|
||||
*
|
||||
* @param {Mixed} val
|
||||
* @return {Mixed}
|
||||
* @api private
|
||||
*/
|
||||
function coerce(val) {
|
||||
if (val instanceof Error) {
|
||||
return val.stack || val.message;
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
/**
|
||||
* XXX DO NOT USE. This is a temporary stub function.
|
||||
* XXX It WILL be removed in the next major release.
|
||||
*/
|
||||
function destroy() {
|
||||
console.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');
|
||||
}
|
||||
|
||||
createDebug.enable(createDebug.load());
|
||||
|
||||
return createDebug;
|
||||
}
|
||||
|
||||
module.exports = setup;
|
|
@ -0,0 +1,10 @@
|
|||
/**
|
||||
* Detect Electron renderer / nwjs process, which is node, but we should
|
||||
* treat as a browser.
|
||||
*/
|
||||
|
||||
if (typeof process === 'undefined' || process.type === 'renderer' || process.browser === true || process.__nwjs) {
|
||||
module.exports = require('./browser.js');
|
||||
} else {
|
||||
module.exports = require('./node.js');
|
||||
}
|
|
@ -0,0 +1,263 @@
|
|||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
const tty = require('tty');
|
||||
const util = require('util');
|
||||
|
||||
/**
|
||||
* This is the Node.js implementation of `debug()`.
|
||||
*/
|
||||
|
||||
exports.init = init;
|
||||
exports.log = log;
|
||||
exports.formatArgs = formatArgs;
|
||||
exports.save = save;
|
||||
exports.load = load;
|
||||
exports.useColors = useColors;
|
||||
exports.destroy = util.deprecate(
|
||||
() => {},
|
||||
'Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.'
|
||||
);
|
||||
|
||||
/**
|
||||
* Colors.
|
||||
*/
|
||||
|
||||
exports.colors = [6, 2, 3, 4, 5, 1];
|
||||
|
||||
try {
|
||||
// Optional dependency (as in, doesn't need to be installed, NOT like optionalDependencies in package.json)
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
const supportsColor = require('supports-color');
|
||||
|
||||
if (supportsColor && (supportsColor.stderr || supportsColor).level >= 2) {
|
||||
exports.colors = [
|
||||
20,
|
||||
21,
|
||||
26,
|
||||
27,
|
||||
32,
|
||||
33,
|
||||
38,
|
||||
39,
|
||||
40,
|
||||
41,
|
||||
42,
|
||||
43,
|
||||
44,
|
||||
45,
|
||||
56,
|
||||
57,
|
||||
62,
|
||||
63,
|
||||
68,
|
||||
69,
|
||||
74,
|
||||
75,
|
||||
76,
|
||||
77,
|
||||
78,
|
||||
79,
|
||||
80,
|
||||
81,
|
||||
92,
|
||||
93,
|
||||
98,
|
||||
99,
|
||||
112,
|
||||
113,
|
||||
128,
|
||||
129,
|
||||
134,
|
||||
135,
|
||||
148,
|
||||
149,
|
||||
160,
|
||||
161,
|
||||
162,
|
||||
163,
|
||||
164,
|
||||
165,
|
||||
166,
|
||||
167,
|
||||
168,
|
||||
169,
|
||||
170,
|
||||
171,
|
||||
172,
|
||||
173,
|
||||
178,
|
||||
179,
|
||||
184,
|
||||
185,
|
||||
196,
|
||||
197,
|
||||
198,
|
||||
199,
|
||||
200,
|
||||
201,
|
||||
202,
|
||||
203,
|
||||
204,
|
||||
205,
|
||||
206,
|
||||
207,
|
||||
208,
|
||||
209,
|
||||
214,
|
||||
215,
|
||||
220,
|
||||
221
|
||||
];
|
||||
}
|
||||
} catch (error) {
|
||||
// Swallow - we only care if `supports-color` is available; it doesn't have to be.
|
||||
}
|
||||
|
||||
/**
|
||||
* Build up the default `inspectOpts` object from the environment variables.
|
||||
*
|
||||
* $ DEBUG_COLORS=no DEBUG_DEPTH=10 DEBUG_SHOW_HIDDEN=enabled node script.js
|
||||
*/
|
||||
|
||||
exports.inspectOpts = Object.keys(process.env).filter(key => {
|
||||
return /^debug_/i.test(key);
|
||||
}).reduce((obj, key) => {
|
||||
// Camel-case
|
||||
const prop = key
|
||||
.substring(6)
|
||||
.toLowerCase()
|
||||
.replace(/_([a-z])/g, (_, k) => {
|
||||
return k.toUpperCase();
|
||||
});
|
||||
|
||||
// Coerce string value into JS value
|
||||
let val = process.env[key];
|
||||
if (/^(yes|on|true|enabled)$/i.test(val)) {
|
||||
val = true;
|
||||
} else if (/^(no|off|false|disabled)$/i.test(val)) {
|
||||
val = false;
|
||||
} else if (val === 'null') {
|
||||
val = null;
|
||||
} else {
|
||||
val = Number(val);
|
||||
}
|
||||
|
||||
obj[prop] = val;
|
||||
return obj;
|
||||
}, {});
|
||||
|
||||
/**
|
||||
* Is stdout a TTY? Colored output is enabled when `true`.
|
||||
*/
|
||||
|
||||
function useColors() {
|
||||
return 'colors' in exports.inspectOpts ?
|
||||
Boolean(exports.inspectOpts.colors) :
|
||||
tty.isatty(process.stderr.fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds ANSI color escape codes if enabled.
|
||||
*
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function formatArgs(args) {
|
||||
const {namespace: name, useColors} = this;
|
||||
|
||||
if (useColors) {
|
||||
const c = this.color;
|
||||
const colorCode = '\u001B[3' + (c < 8 ? c : '8;5;' + c);
|
||||
const prefix = ` ${colorCode};1m${name} \u001B[0m`;
|
||||
|
||||
args[0] = prefix + args[0].split('\n').join('\n' + prefix);
|
||||
args.push(colorCode + 'm+' + module.exports.humanize(this.diff) + '\u001B[0m');
|
||||
} else {
|
||||
args[0] = getDate() + name + ' ' + args[0];
|
||||
}
|
||||
}
|
||||
|
||||
function getDate() {
|
||||
if (exports.inspectOpts.hideDate) {
|
||||
return '';
|
||||
}
|
||||
return new Date().toISOString() + ' ';
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes `util.format()` with the specified arguments and writes to stderr.
|
||||
*/
|
||||
|
||||
function log(...args) {
|
||||
return process.stderr.write(util.format(...args) + '\n');
|
||||
}
|
||||
|
||||
/**
|
||||
* Save `namespaces`.
|
||||
*
|
||||
* @param {String} namespaces
|
||||
* @api private
|
||||
*/
|
||||
function save(namespaces) {
|
||||
if (namespaces) {
|
||||
process.env.DEBUG = namespaces;
|
||||
} else {
|
||||
// If you set a process.env field to null or undefined, it gets cast to the
|
||||
// string 'null' or 'undefined'. Just delete instead.
|
||||
delete process.env.DEBUG;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Load `namespaces`.
|
||||
*
|
||||
* @return {String} returns the previously persisted debug modes
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function load() {
|
||||
return process.env.DEBUG;
|
||||
}
|
||||
|
||||
/**
|
||||
* Init logic for `debug` instances.
|
||||
*
|
||||
* Create a new `inspectOpts` object in case `useColors` is set
|
||||
* differently for a particular `debug` instance.
|
||||
*/
|
||||
|
||||
function init(debug) {
|
||||
debug.inspectOpts = {};
|
||||
|
||||
const keys = Object.keys(exports.inspectOpts);
|
||||
for (let i = 0; i < keys.length; i++) {
|
||||
debug.inspectOpts[keys[i]] = exports.inspectOpts[keys[i]];
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = require('./common')(exports);
|
||||
|
||||
const {formatters} = module.exports;
|
||||
|
||||
/**
|
||||
* Map %o to `util.inspect()`, all on a single line.
|
||||
*/
|
||||
|
||||
formatters.o = function (v) {
|
||||
this.inspectOpts.colors = this.useColors;
|
||||
return util.inspect(v, this.inspectOpts)
|
||||
.split('\n')
|
||||
.map(str => str.trim())
|
||||
.join(' ');
|
||||
};
|
||||
|
||||
/**
|
||||
* Map %O to `util.inspect()`, allowing multiple lines if needed.
|
||||
*/
|
||||
|
||||
formatters.O = function (v) {
|
||||
this.inspectOpts.colors = this.useColors;
|
||||
return util.inspect(v, this.inspectOpts);
|
||||
};
|
|
@ -0,0 +1,162 @@
|
|||
/**
|
||||
* Helpers.
|
||||
*/
|
||||
|
||||
var s = 1000;
|
||||
var m = s * 60;
|
||||
var h = m * 60;
|
||||
var d = h * 24;
|
||||
var w = d * 7;
|
||||
var y = d * 365.25;
|
||||
|
||||
/**
|
||||
* Parse or format the given `val`.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `long` verbose formatting [false]
|
||||
*
|
||||
* @param {String|Number} val
|
||||
* @param {Object} [options]
|
||||
* @throws {Error} throw an error if val is not a non-empty string or a number
|
||||
* @return {String|Number}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
module.exports = function(val, options) {
|
||||
options = options || {};
|
||||
var type = typeof val;
|
||||
if (type === 'string' && val.length > 0) {
|
||||
return parse(val);
|
||||
} else if (type === 'number' && isFinite(val)) {
|
||||
return options.long ? fmtLong(val) : fmtShort(val);
|
||||
}
|
||||
throw new Error(
|
||||
'val is not a non-empty string or a valid number. val=' +
|
||||
JSON.stringify(val)
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse the given `str` and return milliseconds.
|
||||
*
|
||||
* @param {String} str
|
||||
* @return {Number}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function parse(str) {
|
||||
str = String(str);
|
||||
if (str.length > 100) {
|
||||
return;
|
||||
}
|
||||
var match = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(
|
||||
str
|
||||
);
|
||||
if (!match) {
|
||||
return;
|
||||
}
|
||||
var n = parseFloat(match[1]);
|
||||
var type = (match[2] || 'ms').toLowerCase();
|
||||
switch (type) {
|
||||
case 'years':
|
||||
case 'year':
|
||||
case 'yrs':
|
||||
case 'yr':
|
||||
case 'y':
|
||||
return n * y;
|
||||
case 'weeks':
|
||||
case 'week':
|
||||
case 'w':
|
||||
return n * w;
|
||||
case 'days':
|
||||
case 'day':
|
||||
case 'd':
|
||||
return n * d;
|
||||
case 'hours':
|
||||
case 'hour':
|
||||
case 'hrs':
|
||||
case 'hr':
|
||||
case 'h':
|
||||
return n * h;
|
||||
case 'minutes':
|
||||
case 'minute':
|
||||
case 'mins':
|
||||
case 'min':
|
||||
case 'm':
|
||||
return n * m;
|
||||
case 'seconds':
|
||||
case 'second':
|
||||
case 'secs':
|
||||
case 'sec':
|
||||
case 's':
|
||||
return n * s;
|
||||
case 'milliseconds':
|
||||
case 'millisecond':
|
||||
case 'msecs':
|
||||
case 'msec':
|
||||
case 'ms':
|
||||
return n;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Short format for `ms`.
|
||||
*
|
||||
* @param {Number} ms
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function fmtShort(ms) {
|
||||
var msAbs = Math.abs(ms);
|
||||
if (msAbs >= d) {
|
||||
return Math.round(ms / d) + 'd';
|
||||
}
|
||||
if (msAbs >= h) {
|
||||
return Math.round(ms / h) + 'h';
|
||||
}
|
||||
if (msAbs >= m) {
|
||||
return Math.round(ms / m) + 'm';
|
||||
}
|
||||
if (msAbs >= s) {
|
||||
return Math.round(ms / s) + 's';
|
||||
}
|
||||
return ms + 'ms';
|
||||
}
|
||||
|
||||
/**
|
||||
* Long format for `ms`.
|
||||
*
|
||||
* @param {Number} ms
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function fmtLong(ms) {
|
||||
var msAbs = Math.abs(ms);
|
||||
if (msAbs >= d) {
|
||||
return plural(ms, msAbs, d, 'day');
|
||||
}
|
||||
if (msAbs >= h) {
|
||||
return plural(ms, msAbs, h, 'hour');
|
||||
}
|
||||
if (msAbs >= m) {
|
||||
return plural(ms, msAbs, m, 'minute');
|
||||
}
|
||||
if (msAbs >= s) {
|
||||
return plural(ms, msAbs, s, 'second');
|
||||
}
|
||||
return ms + ' ms';
|
||||
}
|
||||
|
||||
/**
|
||||
* Pluralization helper.
|
||||
*/
|
||||
|
||||
function plural(ms, msAbs, n, name) {
|
||||
var isPlural = msAbs >= n * 1.5;
|
||||
return Math.round(ms / n) + ' ' + name + (isPlural ? 's' : '');
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Zeit, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"name": "ms",
|
||||
"version": "2.1.2",
|
||||
"description": "Tiny millisecond conversion utility",
|
||||
"repository": "zeit/ms",
|
||||
"main": "./index",
|
||||
"files": [
|
||||
"index.js"
|
||||
],
|
||||
"scripts": {
|
||||
"precommit": "lint-staged",
|
||||
"lint": "eslint lib/* bin/*",
|
||||
"test": "mocha tests.js"
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "eslint:recommended",
|
||||
"env": {
|
||||
"node": true,
|
||||
"es6": true
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.js": [
|
||||
"npm run lint",
|
||||
"prettier --single-quote --write",
|
||||
"git add"
|
||||
]
|
||||
},
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"eslint": "4.12.1",
|
||||
"expect.js": "0.3.1",
|
||||
"husky": "0.14.3",
|
||||
"lint-staged": "5.0.0",
|
||||
"mocha": "4.0.1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
# ms
|
||||
|
||||
[![Build Status](https://travis-ci.org/zeit/ms.svg?branch=master)](https://travis-ci.org/zeit/ms)
|
||||
[![Join the community on Spectrum](https://withspectrum.github.io/badge/badge.svg)](https://spectrum.chat/zeit)
|
||||
|
||||
Use this package to easily convert various time formats to milliseconds.
|
||||
|
||||
## Examples
|
||||
|
||||
```js
|
||||
ms('2 days') // 172800000
|
||||
ms('1d') // 86400000
|
||||
ms('10h') // 36000000
|
||||
ms('2.5 hrs') // 9000000
|
||||
ms('2h') // 7200000
|
||||
ms('1m') // 60000
|
||||
ms('5s') // 5000
|
||||
ms('1y') // 31557600000
|
||||
ms('100') // 100
|
||||
ms('-3 days') // -259200000
|
||||
ms('-1h') // -3600000
|
||||
ms('-200') // -200
|
||||
```
|
||||
|
||||
### Convert from Milliseconds
|
||||
|
||||
```js
|
||||
ms(60000) // "1m"
|
||||
ms(2 * 60000) // "2m"
|
||||
ms(-3 * 60000) // "-3m"
|
||||
ms(ms('10 hours')) // "10h"
|
||||
```
|
||||
|
||||
### Time Format Written-Out
|
||||
|
||||
```js
|
||||
ms(60000, { long: true }) // "1 minute"
|
||||
ms(2 * 60000, { long: true }) // "2 minutes"
|
||||
ms(-3 * 60000, { long: true }) // "-3 minutes"
|
||||
ms(ms('10 hours'), { long: true }) // "10 hours"
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- Works both in [Node.js](https://nodejs.org) and in the browser
|
||||
- If a number is supplied to `ms`, a string with a unit is returned
|
||||
- If a string that contains the number is supplied, it returns it as a number (e.g.: it returns `100` for `'100'`)
|
||||
- If you pass a string with a number and a valid unit, the number of equivalent milliseconds is returned
|
||||
|
||||
## Related Packages
|
||||
|
||||
- [ms.macro](https://github.com/knpwrs/ms.macro) - Run `ms` as a macro at build-time.
|
||||
|
||||
## Caught a Bug?
|
||||
|
||||
1. [Fork](https://help.github.com/articles/fork-a-repo/) this repository to your own GitHub account and then [clone](https://help.github.com/articles/cloning-a-repository/) it to your local device
|
||||
2. Link the package to the global module directory: `npm link`
|
||||
3. Within the module you want to test your local development instance of ms, just link it to the dependencies: `npm link ms`. Instead of the default one from npm, Node.js will now use your clone of ms!
|
||||
|
||||
As always, you can run the tests using: `npm test`
|
|
@ -0,0 +1,44 @@
|
|||
{
|
||||
"name": "memorystore",
|
||||
"version": "1.6.7",
|
||||
"description": "express-session full featured MemoryStore layer without leaks!",
|
||||
"main": "index.js",
|
||||
"types": "index.d.ts",
|
||||
"scripts": {
|
||||
"test": "mocha --check-leaks --bail --no-exit test/"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/roccomuso/memorystore.git"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
},
|
||||
"keywords": [
|
||||
"express-session",
|
||||
"session",
|
||||
"memory",
|
||||
"store",
|
||||
"memorystore",
|
||||
"noleak",
|
||||
"ram"
|
||||
],
|
||||
"author": "Rocco Musolino (roccomuso)",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/roccomuso/memorystore/issues"
|
||||
},
|
||||
"homepage": "https://github.com/roccomuso/memorystore#readme",
|
||||
"dependencies": {
|
||||
"debug": "^4.3.0",
|
||||
"lru-cache": "^4.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "9.2.0"
|
||||
},
|
||||
"standard": {
|
||||
"env": [
|
||||
"mocha"
|
||||
]
|
||||
}
|
||||
}
|
|
@ -0,0 +1,304 @@
|
|||
var assert = require('assert')
|
||||
|
||||
// express-session way
|
||||
var MemoryStore = require('../')({Store: function () {}})
|
||||
var session = {MemoryStore: MemoryStore}
|
||||
|
||||
describe('MemoryStore', function (done) {
|
||||
afterEach(function () {
|
||||
// runs after each test in this block
|
||||
this.store.stopInterval()
|
||||
})
|
||||
|
||||
it('constructor should use default options', function (done) {
|
||||
this.store = new session.MemoryStore()
|
||||
var store = this.store
|
||||
assert.ok(store.options, 'should have an option object')
|
||||
assert.equal(store.options.max, Infinity, 'max option should be Infinity')
|
||||
assert.equal(store.options.checkPeriod, undefined, 'checkPeriod undefined by default')
|
||||
assert.ok(store.store, 'should have the LRU cache store')
|
||||
assert.equal(store._checkInterval, undefined, 'should not have the pruning loop')
|
||||
done()
|
||||
})
|
||||
|
||||
it('should set options', function (done) {
|
||||
this.store = new session.MemoryStore({
|
||||
max: 10,
|
||||
checkPeriod: 10 * 1000,
|
||||
ttl: 36000,
|
||||
dispose: null,
|
||||
stale: true
|
||||
})
|
||||
var store = this.store
|
||||
assert.equal(store.options.max, 10, 'should set the max option')
|
||||
assert.equal(store.options.checkPeriod, 10 * 1000, 'should set checkPeriod')
|
||||
assert.equal(store.options.ttl, 36000, 'should set the TTL')
|
||||
assert.equal(store.options.dispose, null, 'should set dispose')
|
||||
assert.equal(store.options.stale, true, 'should set stale')
|
||||
done()
|
||||
})
|
||||
|
||||
it('should not set the interval to check for expired entries by default', function (done) {
|
||||
this.store = new session.MemoryStore()
|
||||
var store = this.store
|
||||
assert.equal(store._checkInterval, undefined, 'should not exists')
|
||||
done()
|
||||
})
|
||||
|
||||
it('should only contain 10 items', function (done) {
|
||||
this.store = new session.MemoryStore({max: 10})
|
||||
var store = this.store
|
||||
|
||||
for (var i = 0; i < 15; i++) {
|
||||
store.set(i, {cookie: { expires: new Date((new Date()).valueOf() + 60 * 10 * 1000) }})
|
||||
}
|
||||
|
||||
store.length(function (err, length) {
|
||||
if (err) return done(err)
|
||||
assert.equal(length, 10)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should delete the first item', function (done) {
|
||||
this.store = new session.MemoryStore({max: 10})
|
||||
var store = this.store
|
||||
|
||||
for (var i = 0; i < 15; i++) {
|
||||
store.set(i, {cookie: { expires: new Date((new Date()).valueOf() + 60 * 10 * 1000) }})
|
||||
}
|
||||
|
||||
store.destroy(14)
|
||||
|
||||
store.length(function (err, length) {
|
||||
if (err) return done(err)
|
||||
assert.equal(length, 9)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should delete the last item', function (done) {
|
||||
this.store = new session.MemoryStore({max: 10})
|
||||
var store = this.store
|
||||
|
||||
for (var i = 0; i < 10; i++) {
|
||||
store.set(i, {cookie: { expires: new Date((new Date()).valueOf() + 60 * 10 * 1000) }})
|
||||
}
|
||||
|
||||
store.destroy(0)
|
||||
store.destroy(1)
|
||||
|
||||
store.length(function (err, length) {
|
||||
if (err) return done(err)
|
||||
assert.equal(length, 8)
|
||||
})
|
||||
|
||||
for (i = 10; i < 12; i++) {
|
||||
store.set(i, {cookie: { expires: new Date((new Date()).valueOf() + 60 * 10 * 1000) }})
|
||||
}
|
||||
|
||||
store.length(function (err, length) {
|
||||
if (err) return done(err)
|
||||
assert.equal(length, 10)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should set and get a sample entry', function (done) {
|
||||
this.store = new session.MemoryStore()
|
||||
var store = this.store
|
||||
|
||||
store.set('hello', {cookie: {}, sample: true})
|
||||
store.get('hello', function (err, val) {
|
||||
if (err) return done(err)
|
||||
assert.equal(val.sample, true, 'set and got expected value')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should set TTL from cookie.maxAge', function (done) {
|
||||
this.store = new session.MemoryStore()
|
||||
var store = this.store
|
||||
|
||||
store.set('hello', {cookie: {maxAge: 400}, sample: true})
|
||||
store.get('hello', function (err, val) {
|
||||
if (err) return done(err)
|
||||
assert.equal(val.sample, true, 'entry should be valid')
|
||||
})
|
||||
setTimeout(function () {
|
||||
store.get('hello', function (err, val) {
|
||||
if (err) return done(err)
|
||||
assert.equal(val, undefined, 'entry should be expired')
|
||||
done()
|
||||
})
|
||||
}, 500)
|
||||
})
|
||||
|
||||
it('should not get empty entry', function (done) {
|
||||
this.store = new session.MemoryStore()
|
||||
var store = this.store
|
||||
|
||||
store.get('', function (err, val) {
|
||||
if (err) return done(err)
|
||||
assert.equal(val, undefined)
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should not get a deleted entry', function (done) {
|
||||
this.store = new session.MemoryStore()
|
||||
var store = this.store
|
||||
|
||||
store.set('foo', {cookie: {}})
|
||||
store.get('foo', function (err, val) {
|
||||
if (err) return done(err)
|
||||
assert.ok(val, 'entry exists')
|
||||
store.destroy('foo')
|
||||
store.get('foo', function (err, val) {
|
||||
if (err) return done(err)
|
||||
assert.equal(val, undefined, 'entry actually deleted')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('should not get an expired entry', function (done) {
|
||||
this.store = new session.MemoryStore()
|
||||
var store = this.store
|
||||
|
||||
store.set('hello', {cookie: {maxAge: 200}, sample: true})
|
||||
setTimeout(function () {
|
||||
store.get('hello', function (err, val) {
|
||||
if (err) return done(err)
|
||||
assert.equal(val, undefined, 'entry should be expired')
|
||||
done()
|
||||
})
|
||||
}, 300)
|
||||
})
|
||||
|
||||
it('should enable automatic prune for expired entries', function (done) {
|
||||
this.store = new session.MemoryStore({checkPeriod: 300})
|
||||
var store = this.store
|
||||
|
||||
store.set('foo', {cookie: {maxAge: 150}})
|
||||
store.set('bar', {cookie: {maxAge: 150}})
|
||||
store.length(function (err, count) {
|
||||
if (err) return done(err)
|
||||
assert.equal(count, 2, 'should count 2 entries')
|
||||
})
|
||||
setTimeout(function () {
|
||||
store.length(function (err, count) {
|
||||
if (err) return done(err)
|
||||
assert.equal(count, 0, 'expired entries should be pruned')
|
||||
done()
|
||||
})
|
||||
}, 500)
|
||||
})
|
||||
|
||||
it('automatic check for expired entries should be disabled', function (done) {
|
||||
this.store = new session.MemoryStore()
|
||||
var store = this.store
|
||||
|
||||
store.set('foo', {cookie: {maxAge: 150}})
|
||||
store.set('bar', {cookie: {maxAge: 150}})
|
||||
store.length(function (err, count) {
|
||||
if (err) return done(err)
|
||||
assert.equal(count, 2, 'should count 2 entries')
|
||||
})
|
||||
setTimeout(function () {
|
||||
store.length(function (err, count) {
|
||||
if (err) return done(err)
|
||||
assert.equal(count, 2, 'expired entries should not be pruned')
|
||||
done()
|
||||
})
|
||||
}, 500)
|
||||
})
|
||||
|
||||
it('should touch a given entry', function (done) {
|
||||
this.store = new session.MemoryStore()
|
||||
var store = this.store
|
||||
|
||||
store.set('hei', {cookie: {maxAge: 50}})
|
||||
store.touch('hei', {cookie: {maxAge: 300}})
|
||||
setTimeout(function () {
|
||||
store.get('hei', function (err, val) {
|
||||
if (err) return done(err)
|
||||
assert.ok(val, 'entry should be touched')
|
||||
done()
|
||||
})
|
||||
}, 200)
|
||||
})
|
||||
|
||||
it('should fetch all entries Ids', function (done) {
|
||||
this.store = new session.MemoryStore()
|
||||
var store = this.store
|
||||
|
||||
var k = 10
|
||||
var i = 0
|
||||
for (i = 0; i < k; i++) { store.set('sess' + i, {cookie: {maxAge: 1000}}) }
|
||||
|
||||
store.ids(function (err, ids) {
|
||||
if (err) return done(err)
|
||||
assert.ok(Array.isArray(ids), 'ids should be an Array')
|
||||
i = 10
|
||||
ids.forEach(function (sid) {
|
||||
assert.equal(sid, 'sess' + (--i), 'got expected key')
|
||||
})
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should fetch all entries values', function (done) {
|
||||
this.store = new session.MemoryStore()
|
||||
var store = this.store
|
||||
|
||||
var k = 10
|
||||
var i = 0
|
||||
for (i = 0; i < k; i++) { store.set('sess-' + i, {cookie: {maxAge: 1000}, i: i}) }
|
||||
|
||||
store.all(function (err, all) {
|
||||
if (err) return done(err)
|
||||
assert.equal(typeof all, 'object', 'all should be an Object')
|
||||
Object.keys(all).forEach(function (sid) {
|
||||
var v = sid.split('-')[1]
|
||||
assert.equal(all[sid].i, v, 'got expected value')
|
||||
})
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should count all entries in the store', function (done) {
|
||||
this.store = new session.MemoryStore()
|
||||
var store = this.store
|
||||
|
||||
var k = 10
|
||||
var i = 0
|
||||
for (i = 0; i < k; i++) { store.set(i, {cookie: {maxAge: 1000}}) }
|
||||
|
||||
store.length(function (err, n) {
|
||||
if (err) return done(err)
|
||||
assert.equal(n, k, 'Got expected lenght')
|
||||
done()
|
||||
})
|
||||
})
|
||||
|
||||
it('should delete all entries from the store', function (done) {
|
||||
this.store = new session.MemoryStore()
|
||||
var store = this.store
|
||||
|
||||
var k = 10
|
||||
var i = 0
|
||||
for (i = 0; i < k; i++) { store.set(i, {cookie: {maxAge: 1000}}) }
|
||||
|
||||
store.length(function (err, n) {
|
||||
if (err) return done(err)
|
||||
assert.equal(n, k, 'store is not empty')
|
||||
})
|
||||
store.clear()
|
||||
store.length(function (err, n) {
|
||||
if (err) return done(err)
|
||||
assert.equal(n, 0, 'store should be empty')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
|
@ -0,0 +1,21 @@
|
|||
1.0.2 / 2019-02-21
|
||||
==================
|
||||
|
||||
* Fix `res.writeHead` patch missing return value
|
||||
|
||||
1.0.1 / 2015-09-29
|
||||
==================
|
||||
|
||||
* perf: enable strict mode
|
||||
|
||||
1.0.0 / 2014-08-10
|
||||
==================
|
||||
|
||||
* Honor `res.statusCode` change in `listener`
|
||||
* Move to `jshttp` organization
|
||||
* Prevent `arguments`-related de-opt
|
||||
|
||||
0.0.0 / 2014-05-13
|
||||
==================
|
||||
|
||||
* Initial implementation
|
|
@ -0,0 +1,22 @@
|
|||
(The MIT License)
|
||||
|
||||
Copyright (c) 2014 Douglas Christopher Wilson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
'Software'), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
||||
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
||||
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
||||
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,81 @@
|
|||
# on-headers
|
||||
|
||||
[![NPM Version][npm-version-image]][npm-url]
|
||||
[![NPM Downloads][npm-downloads-image]][npm-url]
|
||||
[![Node.js Version][node-version-image]][node-version-url]
|
||||
[![Build Status][travis-image]][travis-url]
|
||||
[![Test Coverage][coveralls-image]][coveralls-url]
|
||||
|
||||
Execute a listener when a response is about to write headers.
|
||||
|
||||
## Installation
|
||||
|
||||
This is a [Node.js](https://nodejs.org/en/) module available through the
|
||||
[npm registry](https://www.npmjs.com/). Installation is done using the
|
||||
[`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
|
||||
|
||||
```sh
|
||||
$ npm install on-headers
|
||||
```
|
||||
|
||||
## API
|
||||
|
||||
<!-- eslint-disable no-unused-vars -->
|
||||
|
||||
```js
|
||||
var onHeaders = require('on-headers')
|
||||
```
|
||||
|
||||
### onHeaders(res, listener)
|
||||
|
||||
This will add the listener `listener` to fire when headers are emitted for `res`.
|
||||
The listener is passed the `response` object as it's context (`this`). Headers are
|
||||
considered to be emitted only once, right before they are sent to the client.
|
||||
|
||||
When this is called multiple times on the same `res`, the `listener`s are fired
|
||||
in the reverse order they were added.
|
||||
|
||||
## Examples
|
||||
|
||||
```js
|
||||
var http = require('http')
|
||||
var onHeaders = require('on-headers')
|
||||
|
||||
http
|
||||
.createServer(onRequest)
|
||||
.listen(3000)
|
||||
|
||||
function addPoweredBy () {
|
||||
// set if not set by end of request
|
||||
if (!this.getHeader('X-Powered-By')) {
|
||||
this.setHeader('X-Powered-By', 'Node.js')
|
||||
}
|
||||
}
|
||||
|
||||
function onRequest (req, res) {
|
||||
onHeaders(res, addPoweredBy)
|
||||
|
||||
res.setHeader('Content-Type', 'text/plain')
|
||||
res.end('hello!')
|
||||
}
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
```sh
|
||||
$ npm test
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
[MIT](LICENSE)
|
||||
|
||||
[coveralls-image]: https://badgen.net/coveralls/c/github/jshttp/on-headers/master
|
||||
[coveralls-url]: https://coveralls.io/r/jshttp/on-headers?branch=master
|
||||
[node-version-image]: https://badgen.net/npm/node/on-headers
|
||||
[node-version-url]: https://nodejs.org/en/download
|
||||
[npm-downloads-image]: https://badgen.net/npm/dm/on-headers
|
||||
[npm-url]: https://npmjs.org/package/on-headers
|
||||
[npm-version-image]: https://badgen.net/npm/v/on-headers
|
||||
[travis-image]: https://badgen.net/travis/jshttp/on-headers/master
|
||||
[travis-url]: https://travis-ci.org/jshttp/on-headers
|
|
@ -0,0 +1,132 @@
|
|||
/*!
|
||||
* on-headers
|
||||
* Copyright(c) 2014 Douglas Christopher Wilson
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
'use strict'
|
||||
|
||||
/**
|
||||
* Module exports.
|
||||
* @public
|
||||
*/
|
||||
|
||||
module.exports = onHeaders
|
||||
|
||||
/**
|
||||
* Create a replacement writeHead method.
|
||||
*
|
||||
* @param {function} prevWriteHead
|
||||
* @param {function} listener
|
||||
* @private
|
||||
*/
|
||||
|
||||
function createWriteHead (prevWriteHead, listener) {
|
||||
var fired = false
|
||||
|
||||
// return function with core name and argument list
|
||||
return function writeHead (statusCode) {
|
||||
// set headers from arguments
|
||||
var args = setWriteHeadHeaders.apply(this, arguments)
|
||||
|
||||
// fire listener
|
||||
if (!fired) {
|
||||
fired = true
|
||||
listener.call(this)
|
||||
|
||||
// pass-along an updated status code
|
||||
if (typeof args[0] === 'number' && this.statusCode !== args[0]) {
|
||||
args[0] = this.statusCode
|
||||
args.length = 1
|
||||
}
|
||||
}
|
||||
|
||||
return prevWriteHead.apply(this, args)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a listener when a response is about to write headers.
|
||||
*
|
||||
* @param {object} res
|
||||
* @return {function} listener
|
||||
* @public
|
||||
*/
|
||||
|
||||
function onHeaders (res, listener) {
|
||||
if (!res) {
|
||||
throw new TypeError('argument res is required')
|
||||
}
|
||||
|
||||
if (typeof listener !== 'function') {
|
||||
throw new TypeError('argument listener must be a function')
|
||||
}
|
||||
|
||||
res.writeHead = createWriteHead(res.writeHead, listener)
|
||||
}
|
||||
|
||||
/**
|
||||
* Set headers contained in array on the response object.
|
||||
*
|
||||
* @param {object} res
|
||||
* @param {array} headers
|
||||
* @private
|
||||
*/
|
||||
|
||||
function setHeadersFromArray (res, headers) {
|
||||
for (var i = 0; i < headers.length; i++) {
|
||||
res.setHeader(headers[i][0], headers[i][1])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set headers contained in object on the response object.
|
||||
*
|
||||
* @param {object} res
|
||||
* @param {object} headers
|
||||
* @private
|
||||
*/
|
||||
|
||||
function setHeadersFromObject (res, headers) {
|
||||
var keys = Object.keys(headers)
|
||||
for (var i = 0; i < keys.length; i++) {
|
||||
var k = keys[i]
|
||||
if (k) res.setHeader(k, headers[k])
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set headers and other properties on the response object.
|
||||
*
|
||||
* @param {number} statusCode
|
||||
* @private
|
||||
*/
|
||||
|
||||
function setWriteHeadHeaders (statusCode) {
|
||||
var length = arguments.length
|
||||
var headerIndex = length > 1 && typeof arguments[1] === 'string'
|
||||
? 2
|
||||
: 1
|
||||
|
||||
var headers = length >= headerIndex + 1
|
||||
? arguments[headerIndex]
|
||||
: undefined
|
||||
|
||||
this.statusCode = statusCode
|
||||
|
||||
if (Array.isArray(headers)) {
|
||||
// handle array case
|
||||
setHeadersFromArray(this, headers)
|
||||
} else if (headers) {
|
||||
// handle object case
|
||||
setHeadersFromObject(this, headers)
|
||||
}
|
||||
|
||||
// copy leading arguments
|
||||
var args = new Array(Math.min(length, headerIndex))
|
||||
for (var i = 0; i < args.length; i++) {
|
||||
args[i] = arguments[i]
|
||||
}
|
||||
|
||||
return args
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
{
|
||||
"name": "on-headers",
|
||||
"description": "Execute a listener when a response is about to write headers",
|
||||
"version": "1.0.2",
|
||||
"author": "Douglas Christopher Wilson <doug@somethingdoug.com>",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"event",
|
||||
"headers",
|
||||
"http",
|
||||
"onheaders"
|
||||
],
|
||||
"repository": "jshttp/on-headers",
|
||||
"devDependencies": {
|
||||
"eslint": "5.14.1",
|
||||
"eslint-config-standard": "12.0.0",
|
||||
"eslint-plugin-import": "2.16.0",
|
||||
"eslint-plugin-markdown": "1.0.0",
|
||||
"eslint-plugin-node": "8.0.1",
|
||||
"eslint-plugin-promise": "4.0.1",
|
||||
"eslint-plugin-standard": "4.0.0",
|
||||
"istanbul": "0.4.5",
|
||||
"mocha": "6.0.1",
|
||||
"supertest": "3.4.2"
|
||||
},
|
||||
"files": [
|
||||
"LICENSE",
|
||||
"HISTORY.md",
|
||||
"README.md",
|
||||
"index.js"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">= 0.8"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint --plugin markdown --ext js,md .",
|
||||
"test": "mocha --reporter spec --bail --check-leaks test/",
|
||||
"test-cov": "istanbul cover node_modules/mocha/bin/_mocha -- --reporter dot --check-leaks test/",
|
||||
"test-travis": "istanbul cover node_modules/mocha/bin/_mocha --report lcovonly -- --reporter spec --check-leaks test/",
|
||||
"version": "node scripts/version-history.js && git add HISTORY.md"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
language: node_js
|
||||
|
||||
node_js: "10"
|
||||
matrix:
|
||||
include:
|
||||
- node_js: "4"
|
||||
- node_js: "6"
|
||||
- node_js: "8"
|
|
@ -0,0 +1,87 @@
|
|||
node-packet-reader
|
||||
==================
|
||||
|
||||
Handy little well tested module for reading length-prefixed binary packets.
|
||||
|
||||
Since buffers come off a socket in randomly sized chunks you can't expect them to cleanly
|
||||
break on packet boundaries. This module allows you to push buffers in and read
|
||||
full packets out the other side, so you can get to parsing right away and not have
|
||||
to manage concatenating partial buffers and searching through them for packets.
|
||||
|
||||
## install
|
||||
|
||||
` $ npm install packet-reader `
|
||||
|
||||
## example
|
||||
|
||||
```js
|
||||
var Reader = require('packet-reader')
|
||||
|
||||
var reader = new Reader()
|
||||
//assuming you have a socket emitting `data` events
|
||||
socket.on('data', function(buffer) {
|
||||
reader.addChunk(buffer)
|
||||
var packet = reader.read()
|
||||
while(packet) {
|
||||
//do something with fully parsed packet
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
here's a more full featured example:
|
||||
|
||||
let's assume our "packet" for our protocol is 32-bit Big Endian length-prefixed strings
|
||||
so a "hello world" packet would look something like [length, string]
|
||||
`[0, 0, 0 0x0B, h, e, l, l, o, w, o, r, l, d]`
|
||||
|
||||
```js
|
||||
var Transform = require('stream').Transform
|
||||
var Reader = require('packet-reader')
|
||||
var reader = new Reader()
|
||||
var parser = new Transform()
|
||||
parser._transform = function(chunk, encoding, cb) {
|
||||
reader.addChunk(chunk)
|
||||
var packet = reader.read()
|
||||
while(packet) {
|
||||
this.push(packet.toString('utf8'))
|
||||
packet = reader.read()
|
||||
}
|
||||
cb()
|
||||
}
|
||||
|
||||
var server = net.createServer(function(socket) {
|
||||
socket.pipe(parser).pipe(stdout)
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
There are a few config options for setting optional pre-length padding byte. Read the tests for details.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
Copyright 2015 Brian M. Carlson
|
||||
All rights reserved.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person
|
||||
obtaining a copy of this software and associated documentation
|
||||
files (the "Software"), to deal in the Software without
|
||||
restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following
|
||||
conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,65 @@
|
|||
var assert = require('assert')
|
||||
|
||||
var Reader = module.exports = function(options) {
|
||||
//TODO - remove for version 1.0
|
||||
if(typeof options == 'number') {
|
||||
options = { headerSize: options }
|
||||
}
|
||||
options = options || {}
|
||||
this.offset = 0
|
||||
this.lastChunk = false
|
||||
this.chunk = null
|
||||
this.chunkLength = 0
|
||||
this.headerSize = options.headerSize || 0
|
||||
this.lengthPadding = options.lengthPadding || 0
|
||||
this.header = null
|
||||
assert(this.headerSize < 2, 'pre-length header of more than 1 byte length not currently supported')
|
||||
}
|
||||
|
||||
Reader.prototype.addChunk = function(chunk) {
|
||||
if (!this.chunk || this.offset === this.chunkLength) {
|
||||
this.chunk = chunk
|
||||
this.chunkLength = chunk.length
|
||||
this.offset = 0
|
||||
return
|
||||
}
|
||||
|
||||
var newChunkLength = chunk.length
|
||||
var newLength = this.chunkLength + newChunkLength
|
||||
|
||||
if (newLength > this.chunk.length) {
|
||||
var newBufferLength = this.chunk.length * 2
|
||||
while (newLength >= newBufferLength) {
|
||||
newBufferLength *= 2
|
||||
}
|
||||
var newBuffer = Buffer.alloc(newBufferLength)
|
||||
this.chunk.copy(newBuffer)
|
||||
this.chunk = newBuffer
|
||||
}
|
||||
chunk.copy(this.chunk, this.chunkLength)
|
||||
this.chunkLength = newLength
|
||||
}
|
||||
|
||||
Reader.prototype.read = function() {
|
||||
if(this.chunkLength < (this.headerSize + 4 + this.offset)) {
|
||||
return false
|
||||
}
|
||||
|
||||
if(this.headerSize) {
|
||||
this.header = this.chunk[this.offset]
|
||||
}
|
||||
|
||||
//read length of next item
|
||||
var length = this.chunk.readUInt32BE(this.offset + this.headerSize) + this.lengthPadding
|
||||
|
||||
//next item spans more chunks than we have
|
||||
var remaining = this.chunkLength - (this.offset + 4 + this.headerSize)
|
||||
if(length > remaining) {
|
||||
return false
|
||||
}
|
||||
|
||||
this.offset += (this.headerSize + 4)
|
||||
var result = this.chunk.slice(this.offset, this.offset + length)
|
||||
this.offset += length
|
||||
return result
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"name": "packet-reader",
|
||||
"version": "1.0.0",
|
||||
"description": "Read binary packets...",
|
||||
"main": "index.js",
|
||||
"directories": {
|
||||
"test": "test"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/brianc/node-packet-reader.git"
|
||||
},
|
||||
"author": "Brian M. Carlson",
|
||||
"license": "MIT",
|
||||
"bugs": {
|
||||
"url": "https://github.com/brianc/node-packet-reader/issues"
|
||||
},
|
||||
"homepage": "https://github.com/brianc/node-packet-reader",
|
||||
"devDependencies": {
|
||||
"mocha": "~1.21.5"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,148 @@
|
|||
var assert = require('assert')
|
||||
var Reader = require('../')
|
||||
describe('packet-reader', function() {
|
||||
beforeEach(function() {
|
||||
this.reader = new Reader(1)
|
||||
})
|
||||
|
||||
it('reads perfect 1 length buffer', function() {
|
||||
this.reader.addChunk(Buffer.from([0, 0, 0, 0, 1, 1]))
|
||||
var result = this.reader.read()
|
||||
assert.equal(result.length, 1)
|
||||
assert.equal(result[0], 1)
|
||||
assert.strictEqual(false, this.reader.read())
|
||||
})
|
||||
|
||||
it('reads perfect longer buffer', function() {
|
||||
this.reader.addChunk(Buffer.from([0, 0, 0, 0, 4, 1, 2, 3, 4]))
|
||||
var result = this.reader.read()
|
||||
assert.equal(result.length, 4)
|
||||
assert.strictEqual(false, this.reader.read())
|
||||
})
|
||||
|
||||
it('reads two parts', function() {
|
||||
this.reader.addChunk(Buffer.from([0, 0, 0, 0, 1]))
|
||||
var result = this.reader.read()
|
||||
assert.strictEqual(false, result)
|
||||
this.reader.addChunk(Buffer.from([2]))
|
||||
var result = this.reader.read()
|
||||
assert.equal(result.length, 1, 'should return 1 length buffer')
|
||||
assert.equal(result[0], 2)
|
||||
assert.strictEqual(this.reader.read(), false)
|
||||
})
|
||||
|
||||
it('reads multi-part', function() {
|
||||
this.reader.addChunk(Buffer.from([0, 0, 0, 0, 16]))
|
||||
assert.equal(false, this.reader.read())
|
||||
this.reader.addChunk(Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]))
|
||||
assert.equal(false, this.reader.read())
|
||||
this.reader.addChunk(Buffer.from([9, 10, 11, 12, 13, 14, 15, 16]))
|
||||
var result = this.reader.read()
|
||||
assert.equal(result.length, 16)
|
||||
})
|
||||
|
||||
it('resets internal buffer at end of packet', function() {
|
||||
this.reader.addChunk(Buffer.from([0, 0, 0, 0, 16]))
|
||||
this.reader.addChunk(Buffer.from([1, 2, 3, 4, 5, 6, 7, 8]))
|
||||
this.reader.addChunk(Buffer.from([9, 10, 11, 12, 13, 14, 15, 16]))
|
||||
var result = this.reader.read()
|
||||
assert.equal(result.length, 16)
|
||||
|
||||
var newChunk = Buffer.from([0, 0, 0, 0, 16])
|
||||
this.reader.addChunk(newChunk)
|
||||
assert.equal(this.reader.offset, 0, 'should have been reset to 0.')
|
||||
assert.strictEqual(this.reader.chunk, newChunk)
|
||||
})
|
||||
|
||||
it('reads multiple messages from single chunk', function() {
|
||||
this.reader.addChunk(Buffer.from([0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 2, 1, 2]))
|
||||
var result = this.reader.read()
|
||||
assert.equal(result.length, 1, 'should have 1 length buffer')
|
||||
assert.equal(result[0], 1)
|
||||
var result = this.reader.read()
|
||||
assert.equal(result.length, 2, 'should have 2 length buffer but was ' + result.length)
|
||||
assert.equal(result[0], 1)
|
||||
assert.equal(result[1], 2)
|
||||
assert.strictEqual(false, this.reader.read())
|
||||
})
|
||||
|
||||
it('reads 1 and a split', function() {
|
||||
this.reader.addChunk(Buffer.from([0, 0, 0, 0, 1, 1, 0, 0]))//, 0, 0, 2, 1, 2]))
|
||||
var result = this.reader.read()
|
||||
assert.equal(result.length, 1, 'should have 1 length buffer')
|
||||
assert.equal(result[0], 1)
|
||||
var result = this.reader.read()
|
||||
assert.strictEqual(result, false)
|
||||
|
||||
this.reader.addChunk(Buffer.from([0, 0, 2, 1, 2]))
|
||||
var result = this.reader.read()
|
||||
assert.equal(result.length, 2, 'should have 2 length buffer but was ' + result.length)
|
||||
assert.equal(result[0], 1)
|
||||
assert.equal(result[1], 2)
|
||||
assert.strictEqual(false, this.reader.read())
|
||||
})
|
||||
})
|
||||
|
||||
describe('variable length header', function() {
|
||||
beforeEach(function() {
|
||||
this.reader = new Reader()
|
||||
})
|
||||
|
||||
it('reads double message buffers', function() {
|
||||
this.reader.addChunk(Buffer.from([
|
||||
0, 0, 0, 1, 1,
|
||||
0, 0, 0, 2, 1, 2]))
|
||||
var result = this.reader.read()
|
||||
assert(result)
|
||||
assert.equal(result.length, 1)
|
||||
assert.equal(result[0], 1)
|
||||
result = this.reader.read()
|
||||
assert(result)
|
||||
assert.equal(result.length, 2)
|
||||
assert.equal(result[0], 1)
|
||||
assert.equal(result[1], 2)
|
||||
assert.strictEqual(this.reader.read(), false)
|
||||
})
|
||||
})
|
||||
|
||||
describe('1 length code', function() {
|
||||
beforeEach(function() {
|
||||
this.reader = new Reader(1)
|
||||
})
|
||||
|
||||
it('reads code', function() {
|
||||
this.reader.addChunk(Buffer.from([9, 0, 0, 0, 1, 1]))
|
||||
var result = this.reader.read()
|
||||
assert(result)
|
||||
assert.equal(this.reader.header, 9)
|
||||
assert.equal(result.length, 1)
|
||||
assert.equal(result[0], 1)
|
||||
})
|
||||
|
||||
it('is set on uncompleted read', function() {
|
||||
assert.equal(this.reader.header, null)
|
||||
this.reader.addChunk(Buffer.from([2, 0, 0, 0, 1]))
|
||||
assert.strictEqual(this.reader.read(), false)
|
||||
assert.equal(this.reader.header, 2)
|
||||
})
|
||||
})
|
||||
|
||||
describe('postgres style packet', function() {
|
||||
beforeEach(function() {
|
||||
this.reader = new Reader({
|
||||
headerSize: 1,
|
||||
lengthPadding: -4
|
||||
})
|
||||
})
|
||||
|
||||
it('reads with padded length', function() {
|
||||
this.reader.addChunk(Buffer.from([1, 0, 0, 0, 8, 0, 0, 2, 0]))
|
||||
var result = this.reader.read()
|
||||
assert(result)
|
||||
assert.equal(result.length, 4)
|
||||
assert.equal(result[0], 0)
|
||||
assert.equal(result[1], 0)
|
||||
assert.equal(result[2], 2)
|
||||
assert.equal(result[3], 0)
|
||||
})
|
||||
})
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2010 - 2021 Brian Carlson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
|
@ -0,0 +1,33 @@
|
|||
# pg-cloudflare
|
||||
|
||||
A socket implementation that can run on Cloudflare Workers using native TCP connections.
|
||||
|
||||
## install
|
||||
|
||||
```
|
||||
npm i --save-dev pg-cloudflare
|
||||
```
|
||||
|
||||
### license
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2023 Brian M. Carlson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -0,0 +1,2 @@
|
|||
declare const _default: {};
|
||||
export default _default;
|
|
@ -0,0 +1,4 @@
|
|||
// This is an empty module that is served up when outside of a workerd environment
|
||||
// See the `exports` field in package.json
|
||||
export default {};
|
||||
//# sourceMappingURL=empty.js.map
|
|
@ -0,0 +1 @@
|
|||
{"version":3,"file":"empty.js","sourceRoot":"","sources":["../src/empty.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,0CAA0C;AAC1C,eAAe,EAAE,CAAA"}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue