Voeg thematische kleurplaten en creatieve niveaus toe (v0.4.65-beta) #3
8 changed files with 615 additions and 40 deletions
2
VERSION
2
VERSION
|
|
@ -1 +1 @@
|
|||
0.3.80-beta
|
||||
0.3.81-beta
|
||||
|
|
|
|||
400
package-lock.json
generated
400
package-lock.json
generated
|
|
@ -8,6 +8,7 @@
|
|||
"name": "teach",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@fastify/compress": "^8.3.1",
|
||||
"@fastify/cookie": "^11.1.1",
|
||||
"@fastify/helmet": "^13.1.0",
|
||||
"@fastify/rate-limit": "^11.1.0",
|
||||
|
|
@ -56,6 +57,48 @@
|
|||
"fast-uri": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@fastify/compress": {
|
||||
"version": "8.3.1",
|
||||
"resolved": "https://registry.npmjs.org/@fastify/compress/-/compress-8.3.1.tgz",
|
||||
"integrity": "sha512-BUpItLr6MUX9e9ukg5Y6xekyA/7pBFG8QWtFCrUDm9ctoBc3R2/nA16yOaOWtVoccpXGjdDEYA/MxAb5+8cxag==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fastify"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@fastify/accept-negotiator": "^2.0.0",
|
||||
"fastify-plugin": "^5.0.0",
|
||||
"mime-db": "^1.52.0",
|
||||
"minipass": "^7.0.4",
|
||||
"peek-stream": "^1.1.3",
|
||||
"pump": "^3.0.0",
|
||||
"pumpify": "^2.0.1",
|
||||
"readable-stream": "^4.5.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@fastify/compress/node_modules/fastify-plugin": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fastify-plugin/-/fastify-plugin-5.1.0.tgz",
|
||||
"integrity": "sha512-FAIDA8eovSt5qcDgcBvDuX/v0Cjz0ohGhENZ/wpc3y+oZCY2afZ9Baqql3g/lC+OHRnciQol4ww7tuthOb9idw==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/fastify"
|
||||
},
|
||||
{
|
||||
"type": "opencollective",
|
||||
"url": "https://opencollective.com/fastify"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@fastify/cookie": {
|
||||
"version": "11.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@fastify/cookie/-/cookie-11.1.1.tgz",
|
||||
|
|
@ -283,6 +326,18 @@
|
|||
"integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/abort-controller": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
|
||||
"integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"event-target-shim": "^5.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.5"
|
||||
}
|
||||
},
|
||||
"node_modules/abstract-logging": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/abstract-logging/-/abstract-logging-2.0.1.tgz",
|
||||
|
|
@ -360,6 +415,26 @@
|
|||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/base64-js": {
|
||||
"version": "1.5.1",
|
||||
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
|
||||
"integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/brace-expansion": {
|
||||
"version": "5.0.7",
|
||||
"resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.7.tgz",
|
||||
|
|
@ -372,6 +447,36 @@
|
|||
"node": "18 || 20 || >=22"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer": {
|
||||
"version": "6.0.3",
|
||||
"resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
|
||||
"integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"base64-js": "^1.3.1",
|
||||
"ieee754": "^1.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/buffer-from": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
|
||||
"integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/content-disposition": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-2.0.1.tgz",
|
||||
|
|
@ -398,6 +503,12 @@
|
|||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/core-util-is": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
|
||||
"integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
|
|
@ -416,12 +527,81 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/duplexify": {
|
||||
"version": "3.7.1",
|
||||
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-3.7.1.tgz",
|
||||
"integrity": "sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"end-of-stream": "^1.0.0",
|
||||
"inherits": "^2.0.1",
|
||||
"readable-stream": "^2.0.0",
|
||||
"stream-shift": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/duplexify/node_modules/readable-stream": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/duplexify/node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/duplexify/node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/end-of-stream": {
|
||||
"version": "1.4.5",
|
||||
"resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
|
||||
"integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"once": "^1.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-html": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
|
||||
"integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/event-target-shim": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
|
||||
"integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/events": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
|
||||
"integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.8.x"
|
||||
}
|
||||
},
|
||||
"node_modules/fast-decode-uri-component": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz",
|
||||
|
|
@ -604,6 +784,26 @@
|
|||
"url": "https://opencollective.com/express"
|
||||
}
|
||||
},
|
||||
"node_modules/ieee754": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||
"integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/inherits": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||
|
|
@ -619,6 +819,12 @@
|
|||
"node": ">= 10"
|
||||
}
|
||||
},
|
||||
"node_modules/isarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
|
||||
"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json-schema-ref-resolver": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-ref-resolver/-/json-schema-ref-resolver-3.0.0.tgz",
|
||||
|
|
@ -702,6 +908,15 @@
|
|||
"node": ">=10.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.54.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz",
|
||||
"integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/minimatch": {
|
||||
"version": "10.2.5",
|
||||
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz",
|
||||
|
|
@ -735,6 +950,15 @@
|
|||
"node": ">=14.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
"integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"wrappy": "1"
|
||||
}
|
||||
},
|
||||
"node_modules/path-scurry": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz",
|
||||
|
|
@ -751,6 +975,17 @@
|
|||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/peek-stream": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/peek-stream/-/peek-stream-1.1.3.tgz",
|
||||
"integrity": "sha512-FhJ+YbOSBb9/rIl2ZeE/QHEsWn7PqNYt8ARAY3kIgNGOk13g9FGyIY6JIl/xB/3TFRVoTv5as0l11weORrTekA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"buffer-from": "^1.0.0",
|
||||
"duplexify": "^3.5.0",
|
||||
"through2": "^2.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/pg": {
|
||||
"version": "8.22.0",
|
||||
"resolved": "https://registry.npmjs.org/pg/-/pg-8.22.0.tgz",
|
||||
|
|
@ -916,6 +1151,21 @@
|
|||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/process": {
|
||||
"version": "0.11.10",
|
||||
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
|
||||
"integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/process-nextick-args": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
|
||||
"integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/process-warning": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz",
|
||||
|
|
@ -932,12 +1182,75 @@
|
|||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pump": {
|
||||
"version": "3.0.4",
|
||||
"resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
|
||||
"integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"end-of-stream": "^1.1.0",
|
||||
"once": "^1.3.1"
|
||||
}
|
||||
},
|
||||
"node_modules/pumpify": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/pumpify/-/pumpify-2.0.1.tgz",
|
||||
"integrity": "sha512-m7KOje7jZxrmutanlkS1daj1dS6z6BgslzOXmcSEpIlCxM3VJH7lG5QLeck/6hgF6F4crFf01UtQmNsJfweTAw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"duplexify": "^4.1.1",
|
||||
"inherits": "^2.0.3",
|
||||
"pump": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pumpify/node_modules/duplexify": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz",
|
||||
"integrity": "sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"end-of-stream": "^1.4.1",
|
||||
"inherits": "^2.0.3",
|
||||
"readable-stream": "^3.1.1",
|
||||
"stream-shift": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/pumpify/node_modules/readable-stream": {
|
||||
"version": "3.6.2",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
|
||||
"integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"inherits": "^2.0.3",
|
||||
"string_decoder": "^1.1.1",
|
||||
"util-deprecate": "^1.0.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/quick-format-unescaped": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz",
|
||||
"integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/readable-stream": {
|
||||
"version": "4.7.0",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
|
||||
"integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"abort-controller": "^3.0.0",
|
||||
"buffer": "^6.0.3",
|
||||
"events": "^3.3.0",
|
||||
"process": "^0.11.10",
|
||||
"string_decoder": "^1.3.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/real-require": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz",
|
||||
|
|
@ -981,6 +1294,26 @@
|
|||
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/feross"
|
||||
},
|
||||
{
|
||||
"type": "patreon",
|
||||
"url": "https://www.patreon.com/feross"
|
||||
},
|
||||
{
|
||||
"type": "consulting",
|
||||
"url": "https://feross.org/support"
|
||||
}
|
||||
],
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/safe-regex2": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.1.1.tgz",
|
||||
|
|
@ -1079,6 +1412,21 @@
|
|||
"node": ">= 0.8"
|
||||
}
|
||||
},
|
||||
"node_modules/stream-shift": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/stream-shift/-/stream-shift-1.0.3.tgz",
|
||||
"integrity": "sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/string_decoder": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
|
||||
"integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/thread-stream": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-4.2.0.tgz",
|
||||
|
|
@ -1097,6 +1445,46 @@
|
|||
"integrity": "sha512-P4nbQYQfePJxRSmY+v/KINxVucm4NF3p3s7pJveMTtom52FR4YGltUQLB8idDXwDDWW+eYrWDFbuzUnjoWHF7g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/through2": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
|
||||
"integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"readable-stream": "~2.3.6",
|
||||
"xtend": "~4.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/through2/node_modules/readable-stream": {
|
||||
"version": "2.3.8",
|
||||
"resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
|
||||
"integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"core-util-is": "~1.0.0",
|
||||
"inherits": "~2.0.3",
|
||||
"isarray": "~1.0.0",
|
||||
"process-nextick-args": "~2.0.0",
|
||||
"safe-buffer": "~5.1.1",
|
||||
"string_decoder": "~1.1.1",
|
||||
"util-deprecate": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/through2/node_modules/safe-buffer": {
|
||||
"version": "5.1.2",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/through2/node_modules/string_decoder": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
|
||||
"integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/toad-cache": {
|
||||
"version": "3.7.4",
|
||||
"resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.4.tgz",
|
||||
|
|
@ -1115,6 +1503,18 @@
|
|||
"node": ">=0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/util-deprecate": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
|
||||
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/wrappy": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
|
||||
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/xtend": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@
|
|||
"test": "node --test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@fastify/compress": "^8.3.1",
|
||||
"@fastify/cookie": "^11.1.1",
|
||||
"@fastify/helmet": "^13.1.0",
|
||||
"@fastify/rate-limit": "^11.1.0",
|
||||
|
|
|
|||
|
|
@ -4,10 +4,46 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>teachus.today</title>
|
||||
<link rel="icon" type="image/svg+xml" href="img/logo.svg">
|
||||
<link rel="icon" type="image/svg+xml" href="img/logo.svg?v=__V__">
|
||||
<link rel="preload" href="fonts/quicksand.woff2" as="font" type="font/woff2" crossorigin>
|
||||
<link rel="stylesheet" href="css/teach.css">
|
||||
<link rel="stylesheet" href="css/image-catalog.css">
|
||||
<link rel="stylesheet" href="css/teach.css?v=__V__">
|
||||
<link rel="stylesheet" href="css/image-catalog.css?v=__V__">
|
||||
<!-- alle app-scripts: defer = parallel downloaden, uitvoeren in dézelfde
|
||||
volgorde na het parsen (de volgorde is dwingend: core levert T()/api(),
|
||||
de widgets leveren mount-functies aan REGISTRY in board.js, app.js start).
|
||||
?v=__V__ wordt server-side vervangen door de app-versie (cache-busting;
|
||||
zie src/frontend.js) zodat js/css een jaar immutable gecachet mag worden. -->
|
||||
<script src="js/core.js?v=__V__" defer></script>
|
||||
<script src="js/permissions.js?v=__V__" defer></script>
|
||||
<script src="js/settings.js?v=__V__" defer></script>
|
||||
<script src="js/data.js?v=__V__" defer></script>
|
||||
<script src="js/explorer.js?v=__V__" defer></script>
|
||||
<script src="js/picker.js?v=__V__" defer></script>
|
||||
<script src="js/widgets/letters.js?v=__V__" defer></script>
|
||||
<script src="js/widgets/write.js?v=__V__" defer></script>
|
||||
<script src="js/widgets/math.js?v=__V__" defer></script>
|
||||
<script src="js/widgets/media.js?v=__V__" defer></script>
|
||||
<script src="js/widgets/notes.js?v=__V__" defer></script>
|
||||
<script src="js/widgets/timer.js?v=__V__" defer></script>
|
||||
<script src="js/widgets/flits.js?v=__V__" defer></script>
|
||||
<script src="js/widgets/hak.js?v=__V__" defer></script>
|
||||
<script src="js/widgets/anchor.js?v=__V__" defer></script>
|
||||
<script src="js/widgets/ball.js?v=__V__" defer></script>
|
||||
<script src="js/widgets/dice.js?v=__V__" defer></script>
|
||||
<script src="js/widgets/light.js?v=__V__" defer></script>
|
||||
<script src="js/widgets/names.js?v=__V__" defer></script>
|
||||
<script src="js/widgets/mind.js?v=__V__" defer></script>
|
||||
<script src="js/widgets/birthday.js?v=__V__" defer></script>
|
||||
<script src="js/widgets/numberline.js?v=__V__" defer></script>
|
||||
<script src="js/widgets/sentence.js?v=__V__" defer></script>
|
||||
<script src="js/widgets/learning.js?v=__V__" defer></script>
|
||||
<script src="js/widgets/world.js?v=__V__" defer></script>
|
||||
<script src="js/board.js?v=__V__" defer></script>
|
||||
<script src="js/pupil.js?v=__V__" defer></script>
|
||||
<script src="js/parent.js?v=__V__" defer></script>
|
||||
<script src="js/admin.js?v=__V__" defer></script>
|
||||
<script src="js/image-catalog.js?v=__V__" defer></script>
|
||||
<script src="js/app.js?v=__V__" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
|
|
@ -260,36 +296,5 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<script src="js/core.js"></script>
|
||||
<script src="js/permissions.js"></script>
|
||||
<script src="js/settings.js"></script>
|
||||
<script src="js/data.js"></script>
|
||||
<script src="js/explorer.js"></script>
|
||||
<script src="js/picker.js"></script>
|
||||
<script src="js/widgets/letters.js"></script>
|
||||
<script src="js/widgets/write.js"></script>
|
||||
<script src="js/widgets/math.js"></script>
|
||||
<script src="js/widgets/media.js"></script>
|
||||
<script src="js/widgets/notes.js"></script>
|
||||
<script src="js/widgets/timer.js"></script>
|
||||
<script src="js/widgets/flits.js"></script>
|
||||
<script src="js/widgets/hak.js"></script>
|
||||
<script src="js/widgets/anchor.js"></script>
|
||||
<script src="js/widgets/ball.js"></script>
|
||||
<script src="js/widgets/dice.js"></script>
|
||||
<script src="js/widgets/light.js"></script>
|
||||
<script src="js/widgets/names.js"></script>
|
||||
<script src="js/widgets/mind.js"></script>
|
||||
<script src="js/widgets/birthday.js"></script>
|
||||
<script src="js/widgets/numberline.js"></script>
|
||||
<script src="js/widgets/sentence.js"></script>
|
||||
<script src="js/widgets/learning.js"></script>
|
||||
<script src="js/widgets/world.js"></script>
|
||||
<script src="js/board.js"></script>
|
||||
<script src="js/pupil.js"></script>
|
||||
<script src="js/parent.js"></script>
|
||||
<script src="js/admin.js"></script>
|
||||
<script src="js/image-catalog.js"></script>
|
||||
<script src="js/app.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"use strict";
|
||||
/* version — shown until /api/version resolves (or if the fetch fails, e.g. offline).
|
||||
Kept in sync by hand with the VERSION file at the repo root on every release. */
|
||||
const VERSION = "0.3.80-beta";
|
||||
const VERSION = "0.3.81-beta";
|
||||
(function(){
|
||||
const tag = document.getElementById("verTag");
|
||||
tag.textContent = "v"+VERSION;
|
||||
|
|
|
|||
44
src/frontend.js
Normal file
44
src/frontend.js
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
// teach - statische frontend met cache-beleid en cache-busting.
|
||||
//
|
||||
// index.html wordt bij boot één keer gelezen; elke __V__-placeholder in de
|
||||
// asset-URL's (?v=__V__) wordt vervangen door de app-versie en de pagina wordt
|
||||
// daarna uit het geheugen geserveerd met "no-cache" (altijd hervalideren).
|
||||
// De js/css-bestanden zelf dragen daardoor een geversioneerde URL en mogen
|
||||
// een jaar lang "immutable" gecachet worden: een nieuwe release geeft nieuwe
|
||||
// URL's, herhaalbezoek binnen dezelfde release kost vrijwel geen bytes.
|
||||
// Fonts en afbeeldingen worden zonder versie geladen (CSS url()/JS-paden) en
|
||||
// krijgen dus een kortere maxAge mét de zwakke etag als 304-vangnet.
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { join } from 'node:path';
|
||||
import fastifyStatic from '@fastify/static';
|
||||
|
||||
export function resolveAppVersion(env = process.env) {
|
||||
const v = env.APP_VERSION;
|
||||
// zonder echte versie (lokaal ontwikkelen) nooit een stale cache riskeren
|
||||
return v && v !== 'dev' ? v : `dev-${Date.now()}`;
|
||||
}
|
||||
|
||||
export async function registerFrontend(app, { root, version }) {
|
||||
const html = (await readFile(join(root, 'index.html'), 'utf8'))
|
||||
.replaceAll('__V__', encodeURIComponent(version));
|
||||
const sendIndex = (_req, reply) => reply
|
||||
.type('text/html; charset=utf-8')
|
||||
.header('cache-control', 'no-cache')
|
||||
.send(html);
|
||||
app.get('/', sendIndex);
|
||||
app.get('/index.html', sendIndex);
|
||||
|
||||
await app.register(fastifyStatic, {
|
||||
root,
|
||||
index: false, // index.html gaat via de in-memory route hierboven
|
||||
setHeaders(reply, path) {
|
||||
if (/[\\/](js|css)[\\/]/.test(path)) {
|
||||
reply.header('cache-control', 'public, max-age=31536000, immutable');
|
||||
} else if (/[\\/]fonts[\\/]/.test(path)) {
|
||||
reply.header('cache-control', 'public, max-age=604800');
|
||||
} else {
|
||||
reply.header('cache-control', 'public, max-age=86400');
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
|
@ -1,13 +1,15 @@
|
|||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname, join } from 'node:path';
|
||||
import zlib from 'node:zlib';
|
||||
import Fastify from 'fastify';
|
||||
import fastifyStatic from '@fastify/static';
|
||||
import fastifyCookie from '@fastify/cookie';
|
||||
import fastifyCompress from '@fastify/compress';
|
||||
import fastifyHelmet from '@fastify/helmet';
|
||||
import fastifyRateLimit from '@fastify/rate-limit';
|
||||
import pg from 'pg';
|
||||
import api, { bootstrapSuper } from './api.js';
|
||||
import imageApi from './images.js';
|
||||
import { registerFrontend, resolveAppVersion } from './frontend.js';
|
||||
import { runMigrations } from './migrate.js';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
|
@ -78,16 +80,30 @@ app.addHook('onSend', async (_req, reply) => {
|
|||
reply.header('Permissions-Policy', 'camera=(), microphone=(), geolocation=(), payment=(), usb=(), fullscreen=(self)');
|
||||
});
|
||||
|
||||
// --- Compressie ---------------------------------------------------------------
|
||||
// Vóór de API- en frontend-registraties: de onSend-hook van compress geldt
|
||||
// alleen voor routes die daarna worden toegevoegd. Brotli op quality 4:
|
||||
// vergelijkbare ratio als gzip-9 tegen een fractie van de CPU (belangrijk voor
|
||||
// grote dynamische antwoorden zoals /me/data).
|
||||
await app.register(fastifyCompress, {
|
||||
global: true,
|
||||
encodings: ['br', 'gzip'],
|
||||
threshold: 1024,
|
||||
brotliOptions: { params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 4 } },
|
||||
zlibOptions: { level: 6 },
|
||||
});
|
||||
|
||||
// --- API ---------------------------------------------------------------------
|
||||
// Inloggen, rollen, gebruikersbeheer en per-gebruiker data. Zie src/api.js.
|
||||
app.register(api, { prefix: '/api' });
|
||||
app.register(imageApi, { prefix: '/api' });
|
||||
|
||||
// --- Static frontend ---------------------------------------------------------
|
||||
// Serveert public/index.html (de digibord-app) op /
|
||||
app.register(fastifyStatic, {
|
||||
// index.html met versie-injectie (cache-busting) + cache-headers per prefix;
|
||||
// zie src/frontend.js voor het beleid.
|
||||
await registerFrontend(app, {
|
||||
root: join(__dirname, '..', 'public'),
|
||||
index: ['index.html'],
|
||||
version: resolveAppVersion(),
|
||||
});
|
||||
|
||||
// --- Start -------------------------------------------------------------------
|
||||
|
|
|
|||
109
test/frontend.test.js
Normal file
109
test/frontend.test.js
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import zlib from 'node:zlib';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname, join } from 'node:path';
|
||||
import Fastify from 'fastify';
|
||||
import fastifyCompress from '@fastify/compress';
|
||||
import { registerFrontend, resolveAppVersion } from '../src/frontend.js';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const publicRoot = join(__dirname, '..', 'public');
|
||||
|
||||
// Zelfde volgorde als src/server.js: compress vóór de frontend-registratie.
|
||||
async function makeApp(version = '1.2.3-test') {
|
||||
const app = Fastify();
|
||||
await app.register(fastifyCompress, {
|
||||
global: true,
|
||||
encodings: ['br', 'gzip'],
|
||||
threshold: 1024,
|
||||
brotliOptions: { params: { [zlib.constants.BROTLI_PARAM_QUALITY]: 4 } },
|
||||
zlibOptions: { level: 6 },
|
||||
});
|
||||
await registerFrontend(app, { root: publicRoot, version });
|
||||
await app.ready();
|
||||
return app;
|
||||
}
|
||||
|
||||
test('index.html wordt geserveerd met versie-injectie en no-cache', async () => {
|
||||
const app = await makeApp();
|
||||
const res = await app.inject({ url: '/' });
|
||||
assert.equal(res.statusCode, 200);
|
||||
assert.match(res.headers['content-type'], /text\/html/);
|
||||
assert.equal(res.headers['cache-control'], 'no-cache');
|
||||
assert.ok(res.payload.includes('?v=1.2.3-test'), 'versie hoort geïnjecteerd te zijn');
|
||||
assert.ok(!res.payload.includes('__V__'), 'geen placeholder meer in de uitvoer');
|
||||
const alias = await app.inject({ url: '/index.html' });
|
||||
assert.equal(alias.headers['cache-control'], 'no-cache');
|
||||
await app.close();
|
||||
});
|
||||
|
||||
test('js en css zijn een jaar immutable; fonts en img korter zonder immutable', async () => {
|
||||
const app = await makeApp();
|
||||
for (const url of ['/js/core.js', '/css/teach.css']) {
|
||||
const res = await app.inject({ url });
|
||||
assert.equal(res.statusCode, 200, url);
|
||||
assert.equal(res.headers['cache-control'], 'public, max-age=31536000, immutable', url);
|
||||
}
|
||||
const font = await app.inject({ url: '/fonts/quicksand.woff2' });
|
||||
assert.equal(font.headers['cache-control'], 'public, max-age=604800');
|
||||
assert.ok(font.headers.etag, 'etag als 304-vangnet voor ongeversioneerde assets');
|
||||
const img = await app.inject({ url: '/img/logo.svg' });
|
||||
assert.equal(img.headers['cache-control'], 'public, max-age=86400');
|
||||
await app.close();
|
||||
});
|
||||
|
||||
test('assets gaan gecomprimeerd over de lijn (gzip én brotli)', async () => {
|
||||
const app = await makeApp();
|
||||
const gz = await app.inject({ url: '/css/teach.css', headers: { 'accept-encoding': 'gzip' } });
|
||||
assert.equal(gz.headers['content-encoding'], 'gzip');
|
||||
assert.match(gz.headers.vary ?? '', /accept-encoding/i);
|
||||
const cssRaw = zlib.gunzipSync(gz.rawPayload).toString('utf8');
|
||||
assert.ok(cssRaw.includes('toolbar'), 'gzip-inhoud is geldige css');
|
||||
const br = await app.inject({ url: '/js/core.js', headers: { 'accept-encoding': 'br' } });
|
||||
assert.equal(br.headers['content-encoding'], 'br');
|
||||
const jsRaw = zlib.brotliDecompressSync(br.rawPayload).toString('utf8');
|
||||
assert.ok(jsRaw.includes('const VERSION'), 'brotli-inhoud is geldige js');
|
||||
assert.ok(br.rawPayload.length < jsRaw.length / 2, 'compressie levert echt winst op');
|
||||
await app.close();
|
||||
});
|
||||
|
||||
test('conditional GET levert 304 zonder body', async () => {
|
||||
const app = await makeApp();
|
||||
const first = await app.inject({ url: '/fonts/quicksand.woff2' });
|
||||
const res = await app.inject({ url: '/fonts/quicksand.woff2', headers: { 'if-none-match': first.headers.etag } });
|
||||
assert.equal(res.statusCode, 304);
|
||||
assert.equal(res.rawPayload.length, 0);
|
||||
await app.close();
|
||||
});
|
||||
|
||||
test('resolveAppVersion geeft de echte versie door en valt lokaal nooit stale terug', () => {
|
||||
assert.equal(resolveAppVersion({ APP_VERSION: '0.4.0-beta' }), '0.4.0-beta');
|
||||
const a = resolveAppVersion({});
|
||||
const b = resolveAppVersion({ APP_VERSION: 'dev' });
|
||||
assert.match(a, /^dev-\d+$/);
|
||||
assert.match(b, /^dev-\d+$/);
|
||||
});
|
||||
|
||||
test('index.html: elk script heeft defer + cache-busting, font-preload blijft ongeversioneerd', async () => {
|
||||
const [html, server] = await Promise.all([
|
||||
readFile(join(publicRoot, 'index.html'), 'utf8'),
|
||||
readFile(join(__dirname, '..', 'src', 'server.js'), 'utf8'),
|
||||
]);
|
||||
const scripts = [...html.matchAll(/<script [^>]*src="([^"]+)"[^>]*>/g)];
|
||||
assert.ok(scripts.length >= 30, 'alle app-scripts staan in index.html');
|
||||
for (const [tag, src] of scripts) {
|
||||
assert.match(src, /\?v=__V__$/, `script zonder cache-busting: ${src}`);
|
||||
assert.match(tag, / defer/, `script zonder defer: ${src}`);
|
||||
}
|
||||
assert.match(html, /href="css\/teach\.css\?v=__V__"/);
|
||||
/* de preload-URL moet byte-gelijk blijven aan de url() in teach.css,
|
||||
anders downloadt de browser het font dubbel */
|
||||
assert.match(html, /rel="preload" href="fonts\/quicksand\.woff2" /);
|
||||
/* hook-volgorde: compress moet vóór de api- en frontend-registraties staan */
|
||||
assert.ok(server.indexOf('fastifyCompress') < server.indexOf('register(api'),
|
||||
'compress hoort vóór de api-registratie');
|
||||
assert.ok(server.indexOf('fastifyCompress') < server.indexOf('registerFrontend(app'),
|
||||
'compress hoort vóór de frontend-registratie');
|
||||
});
|
||||
Loading…
Reference in a new issue