@lbu/store
Advanced tools
Comparing version 0.0.45 to 0.0.46
@@ -41,3 +41,3 @@ import { dirnameForModule } from "@lbu/stdlib"; | ||
export const migrations = dirnameForModule(import.meta) + "/migrations"; | ||
export const migrations = `${dirnameForModule(import.meta)}/migrations`; | ||
export { structure as storeStructure } from "./src/generated/structure.js"; |
{ | ||
"name": "@lbu/store", | ||
"version": "0.0.45", | ||
"version": "0.0.46", | ||
"description": "Postgres & S3-compatible wrappers for common things", | ||
"main": "./index.js", | ||
"exports": "./index.js", | ||
"types": "./index.d.ts", | ||
"type": "module", | ||
@@ -17,4 +18,5 @@ "keywords": [ | ||
"dependencies": { | ||
"@lbu/insight": "0.0.45", | ||
"@lbu/stdlib": "0.0.45", | ||
"@lbu/insight": "0.0.46", | ||
"@lbu/stdlib": "0.0.46", | ||
"@types/minio": "7.0.6", | ||
"mime-types": "2.1.27", | ||
@@ -24,9 +26,15 @@ "minio": "7.0.16", | ||
}, | ||
"author": { | ||
"name": "Dirk de Visser", | ||
"email": "dirkdev98@gmail.com" | ||
}, | ||
"maintainers": [ | ||
{ | ||
"name": "Dirk de Visser", | ||
"email": "dirkdev98@gmail.com" | ||
}, | ||
{ | ||
"name": "Daniël Hansen" | ||
} | ||
], | ||
"homepage": "https://lbu.lightbase.nl", | ||
"repository": { | ||
"type": "git", | ||
"url": "ssh://git@github.com/lightbasenl/lbu.git", | ||
"url": "https://github.com/lightbasenl/lbu.git", | ||
"directory": "packages/store" | ||
@@ -40,3 +48,3 @@ }, | ||
}, | ||
"gitHead": "2a7bd7e726fc2f6b5709d5b4f3edfc676bb36407" | ||
"gitHead": "6b9827983e55ade8f018d8d0c306a2fdbe6b94cb" | ||
} |
@@ -14,4 +14,4 @@ # @lbu/store | ||
- Script runner, can watch & reload almost anything (via nodemon) | ||
- Flexible code generators supporting routers, validators, api clients, mocks, | ||
CRUD queries and more in the future. | ||
- Flexible code generators supporting routers, validators, api clients, CRUD | ||
queries and more in the future. | ||
- Opinionated structured logging | ||
@@ -91,3 +91,2 @@ - Common Koa middleware wrapped in a single function | ||
- Typescript or JSDoc types | ||
- Generated mocks | ||
- An extendable set of types: | ||
@@ -112,4 +111,14 @@ - boolean, number, string; | ||
## Development | ||
## New features | ||
See [CONTRIBUTING.md](/contributing.md). | ||
New features added should fall under the following categories: | ||
- It improves the interface between api and client in some way. An example may | ||
be to support websockets in @lbu/code-gen | ||
- It improves the developer experience one way or another while developing an | ||
api For example the `lbu docker` commands or various utilities provided by | ||
@lbu/stdlib | ||
Although some parts heavily rely on conventions set by the packages, we | ||
currently aim not to be a framework. However, the idea of being a bit more of | ||
framework is not completely out of the door yet. |
@@ -10,25 +10,2 @@ import { once } from "events"; | ||
/** | ||
* @name FileCacheOptions | ||
* | ||
* @typedef {object} | ||
* @property {number} inMemoryThreshold Maximum byte size of a file to be stored in | ||
* memory | ||
* @property {string} cacheControlHeader Customize default Cache-Control header to give | ||
* back | ||
*/ | ||
/** | ||
* @name FileCache | ||
* | ||
* @class | ||
* A relatively simple local file cache implementation. | ||
* Supports saving files in memory and on local disk | ||
* Files#contentLength smaller than the provided threshold will be stored in memory. | ||
* A file will always be cached in full, and then the range requests will be evaluated | ||
* after The FileCache#clear does not remove files from disk, but will overwrite the | ||
* file when added to the cache again | ||
* | ||
* FileCache#getFileStream is compatible with `sendFile` in @lbu/server | ||
*/ | ||
export class FileCache { | ||
@@ -38,4 +15,2 @@ static fileCachePath = "/tmp"; | ||
/** | ||
* Create a new file cache | ||
* | ||
* @param {FileStoreContext} fileStore | ||
@@ -62,8 +37,2 @@ * @param {FileCacheOptions} [options] | ||
/** | ||
* Get a file(part) from the cache. | ||
* If the file(part) does not exist, it will try to fetch it from the FileStore | ||
* If the file store throws an error / it doesn't exist, the error is propagated to the | ||
* caller | ||
* | ||
* @public | ||
* @param {StoreFileStore} file | ||
@@ -89,11 +58,7 @@ * @param {number} [start] | ||
return this.cacheFileOnDisk(cacheKey, file.id, start, end); | ||
} else { | ||
return this.cacheFileInMemory(cacheKey, file.id, start, end); | ||
} | ||
return this.cacheFileInMemory(cacheKey, file.id, start, end); | ||
} | ||
/** | ||
* Remove a file from cache, but not from local disk | ||
* | ||
* @public | ||
* @param {string} fileId | ||
@@ -100,0 +65,0 @@ */ |
@@ -21,11 +21,2 @@ import { createReadStream } from "fs"; | ||
/** | ||
* @name FileStoreContext | ||
* | ||
* @typedef {object} | ||
* @property sql | ||
* @property {minio.Client} minio | ||
* @property {string} bucketName | ||
*/ | ||
/** | ||
* @param sql | ||
@@ -44,6 +35,2 @@ * @param {minio.Client} minio | ||
/** | ||
* Create or update a file. | ||
* If you pass in a non-existent id, the function will not error, but also not update the | ||
* file | ||
* | ||
* @param {FileStoreContext} fc | ||
@@ -116,5 +103,4 @@ * @param {StoreFileStoreInsertPartial_Input & { id?: string }} props | ||
return fc.minio.getPartialObject(fc.bucketName, id, start, size); | ||
} else { | ||
return fc.minio.getObject(fc.bucketName, id); | ||
} | ||
return fc.minio.getObject(fc.bucketName, id); | ||
} | ||
@@ -121,0 +107,0 @@ |
@@ -28,3 +28,3 @@ // Generated by @lbu/code-gen | ||
where.bucketNameLike ?? null | ||
}, NULL) IS NULL OR fs."bucketName" LIKE ${"%" + where.bucketNameLike + "%"}) | ||
}, NULL) IS NULL OR fs."bucketName" LIKE ${`%${where.bucketNameLike}%`}) | ||
`, | ||
@@ -51,5 +51,3 @@ | ||
where.bucketNameLike ?? null | ||
}, NULL) IS NULL OR fs."bucketName" LIKE ${ | ||
"%" + where.bucketNameLike + "%" | ||
}) | ||
}, NULL) IS NULL OR fs."bucketName" LIKE ${`%${where.bucketNameLike}%`}) | ||
`; | ||
@@ -77,3 +75,3 @@ return result?.[0]?.genCount ?? 0; | ||
where.bucketNameLike ?? null | ||
}, NULL) IS NULL OR fs."bucketName" LIKE ${"%" + where.bucketNameLike + "%"}) | ||
}, NULL) IS NULL OR fs."bucketName" LIKE ${`%${where.bucketNameLike}%`}) | ||
`, | ||
@@ -132,5 +130,3 @@ | ||
where.bucketNameLike ?? null | ||
}, NULL) IS NULL OR fs."bucketName" LIKE ${ | ||
"%" + where.bucketNameLike + "%" | ||
}) | ||
}, NULL) IS NULL OR fs."bucketName" LIKE ${`%${where.bucketNameLike}%`}) | ||
`; | ||
@@ -172,3 +168,13 @@ let query = `UPDATE "fileStore" fs SET `; | ||
query += `fs."id" `; | ||
query += `= ANY (ARRAY['${where.idIn.join("', '")}']::uuid[])`; | ||
query += `= ANY (ARRAY[`; | ||
let addOne = false; | ||
for (const value of where.idIn || []) { | ||
addOne = true; | ||
query += `$${idx++},`; | ||
argList.push(value); | ||
} | ||
query = `${query.substring( | ||
0, | ||
query.length - (addOne ? 1 : 0), | ||
)}]::uuid[])`; | ||
query += " AND "; | ||
@@ -214,3 +220,3 @@ } | ||
where.bucketNameLike ?? null | ||
}, NULL) IS NULL OR fs."bucketName" LIKE ${"%" + where.bucketNameLike + "%"}) | ||
}, NULL) IS NULL OR fs."bucketName" LIKE ${`%${where.bucketNameLike}%`}) | ||
GROUP BY fs.id`, | ||
@@ -357,3 +363,13 @@ | ||
query += `ss."id" `; | ||
query += `= ANY (ARRAY['${where.idIn.join("', '")}']::uuid[])`; | ||
query += `= ANY (ARRAY[`; | ||
let addOne = false; | ||
for (const value of where.idIn || []) { | ||
addOne = true; | ||
query += `$${idx++},`; | ||
argList.push(value); | ||
} | ||
query = `${query.substring( | ||
0, | ||
query.length - (addOne ? 1 : 0), | ||
)}]::uuid[])`; | ||
query += " AND "; | ||
@@ -439,5 +455,5 @@ } | ||
where.name ?? null | ||
}) AND (COALESCE(${where.nameLike ?? null}, NULL) IS NULL OR jq."name" LIKE ${ | ||
"%" + where.nameLike + "%" | ||
}) | ||
}) AND (COALESCE(${ | ||
where.nameLike ?? null | ||
}, NULL) IS NULL OR jq."name" LIKE ${`%${where.nameLike}%`}) | ||
`, | ||
@@ -464,3 +480,3 @@ | ||
where.nameLike ?? null | ||
}, NULL) IS NULL OR jq."name" LIKE ${"%" + where.nameLike + "%"}) | ||
}, NULL) IS NULL OR jq."name" LIKE ${`%${where.nameLike}%`}) | ||
`; | ||
@@ -484,5 +500,5 @@ return result?.[0]?.genCount ?? 0; | ||
where.name ?? null | ||
}) AND (COALESCE(${where.nameLike ?? null}, NULL) IS NULL OR jq."name" LIKE ${ | ||
"%" + where.nameLike + "%" | ||
}) | ||
}) AND (COALESCE(${ | ||
where.nameLike ?? null | ||
}, NULL) IS NULL OR jq."name" LIKE ${`%${where.nameLike}%`}) | ||
`, | ||
@@ -489,0 +505,0 @@ |
export const structureString = | ||
'{"store":{"fileStore":{"type":"object","group":"store","name":"fileStore","docString":"","isOptional":false,"disabled":{"validator":false,"mock":false},"validator":{"strict":false},"enableQueries":true,"queryOptions":{"withHistory":true},"keys":{"id":{"type":"uuid","docString":"","isOptional":false,"disabled":{"validator":false,"mock":false},"sql":{"searchable":true,"primary":true}},"bucketName":{"type":"string","docString":"","isOptional":false,"disabled":{"validator":false,"mock":false},"validator":{"convert":false,"trim":false,"lowerCase":false,"upperCase":false},"sql":{"searchable":true}},"contentLength":{"type":"number","docString":"","isOptional":false,"disabled":{"validator":false,"mock":false},"validator":{"convert":false,"integer":true}},"contentType":{"type":"string","docString":"","isOptional":false,"disabled":{"validator":false,"mock":false},"validator":{"convert":false,"trim":false,"lowerCase":false,"upperCase":false}},"filename":{"type":"string","docString":"","isOptional":false,"disabled":{"validator":false,"mock":false},"validator":{"convert":false,"trim":false,"lowerCase":false,"upperCase":false}}},"uniqueName":"StoreFileStore"},"sessionStore":{"type":"object","group":"store","name":"sessionStore","docString":"","isOptional":false,"disabled":{"validator":false,"mock":false},"validator":{"strict":false},"enableQueries":true,"queryOptions":{"withDates":true},"keys":{"id":{"type":"uuid","docString":"","isOptional":false,"disabled":{"validator":false,"mock":false},"sql":{"searchable":true,"primary":true}},"expires":{"type":"date","docString":"","isOptional":false,"disabled":{"validator":false,"mock":false},"sql":{"searchable":true}},"data":{"type":"any","docString":"","isOptional":true,"defaultValue":"{}","disabled":{"validator":false,"mock":false}}},"uniqueName":"StoreSessionStore"},"jobQueue":{"type":"object","group":"store","name":"jobQueue","docString":"","isOptional":false,"disabled":{"validator":false,"mock":false},"validator":{"strict":false},"enableQueries":true,"queryOptions":{"withDates":true},"keys":{"id":{"type":"number","docString":"","isOptional":false,"disabled":{"validator":false,"mock":false},"validator":{"convert":false,"integer":true},"sql":{"searchable":true,"primary":true}},"isComplete":{"type":"boolean","docString":"","isOptional":true,"defaultValue":"false","disabled":{"validator":false,"mock":false},"validator":{"convert":false},"sql":{"searchable":true}},"priority":{"type":"number","docString":"","isOptional":true,"defaultValue":"0","disabled":{"validator":false,"mock":false},"validator":{"convert":false,"integer":true}},"scheduledAt":{"type":"date","docString":"","isOptional":true,"defaultValue":"(new Date())","disabled":{"validator":false,"mock":false},"sql":{"searchable":true}},"name":{"type":"string","docString":"","isOptional":false,"disabled":{"validator":false,"mock":false},"validator":{"convert":false,"trim":false,"lowerCase":false,"upperCase":false},"sql":{"searchable":true}},"data":{"type":"any","docString":"","isOptional":true,"defaultValue":"{}","disabled":{"validator":false,"mock":false}}},"uniqueName":"StoreJobQueue"}}}'; | ||
'{"store":{"fileStore":{"type":"object","group":"store","name":"fileStore","docString":"","isOptional":false,"validator":{"strict":false},"enableQueries":true,"queryOptions":{"withHistory":true},"keys":{"id":{"type":"uuid","docString":"","isOptional":false,"sql":{"searchable":true,"primary":true}},"bucketName":{"type":"string","docString":"","isOptional":false,"validator":{"convert":false,"trim":false,"lowerCase":false,"upperCase":false},"sql":{"searchable":true}},"contentLength":{"type":"number","docString":"","isOptional":false,"validator":{"convert":false,"integer":true}},"contentType":{"type":"string","docString":"","isOptional":false,"validator":{"convert":false,"trim":false,"lowerCase":false,"upperCase":false}},"filename":{"type":"string","docString":"","isOptional":false,"validator":{"convert":false,"trim":false,"lowerCase":false,"upperCase":false}}},"uniqueName":"StoreFileStore"},"sessionStore":{"type":"object","group":"store","name":"sessionStore","docString":"","isOptional":false,"validator":{"strict":false},"enableQueries":true,"queryOptions":{"withDates":true},"keys":{"id":{"type":"uuid","docString":"","isOptional":false,"sql":{"searchable":true,"primary":true}},"expires":{"type":"date","docString":"","isOptional":false,"sql":{"searchable":true}},"data":{"type":"any","docString":"","isOptional":true,"defaultValue":"{}"}},"uniqueName":"StoreSessionStore"},"jobQueue":{"type":"object","group":"store","name":"jobQueue","docString":"","isOptional":false,"validator":{"strict":false},"enableQueries":true,"queryOptions":{"withDates":true},"keys":{"id":{"type":"number","docString":"","isOptional":false,"validator":{"convert":false,"integer":true},"sql":{"searchable":true,"primary":true}},"isComplete":{"type":"boolean","docString":"","isOptional":true,"defaultValue":"false","validator":{"convert":false},"sql":{"searchable":true}},"priority":{"type":"number","docString":"","isOptional":true,"defaultValue":"0","validator":{"convert":false,"integer":true}},"scheduledAt":{"type":"date","docString":"","isOptional":true,"defaultValue":"(new Date())","sql":{"searchable":true}},"name":{"type":"string","docString":"","isOptional":false,"validator":{"convert":false,"trim":false,"lowerCase":false,"upperCase":false},"sql":{"searchable":true}},"data":{"type":"any","docString":"","isOptional":true,"defaultValue":"{}"}},"uniqueName":"StoreJobQueue"}}}'; | ||
export const structure = JSON.parse(structureString); |
@@ -7,26 +7,2 @@ import { createHash } from "crypto"; | ||
/** | ||
* @name MigrateContext | ||
* | ||
* @typedef {object} | ||
* @property {MigrateFile[]} files | ||
* @property {string[]} namespaces | ||
* @property {object<string, string>} storedHashes | ||
* @property {Postgres} sql | ||
*/ | ||
/** | ||
* @name MigrateFile | ||
* | ||
* @typedef {object} | ||
* @property {string} namespace | ||
* @property {number} number | ||
* @property {boolean} repeatable | ||
* @property {string} name | ||
* @property {string} fullPath | ||
* @property {boolean} isMigrated | ||
* @property {string} source | ||
* @property {string} hash | ||
*/ | ||
/** | ||
* @param sql | ||
@@ -41,32 +17,44 @@ * @param migrationDirectory | ||
) { | ||
const migrations = await readMigrationsDir(migrationDirectory); | ||
try { | ||
const migrations = await readMigrationsDir(migrationDirectory); | ||
// Automatically add this package to the migrations | ||
if (migrations.namespaces.indexOf("@lbu/store") === -1) { | ||
migrations.namespaces.unshift("@lbu/store"); | ||
// Automatically add this package to the migrations | ||
if (migrations.namespaces.indexOf("@lbu/store") === -1) { | ||
migrations.namespaces.unshift("@lbu/store"); | ||
const { migrationFiles } = await readMigrationsDir( | ||
dirnameForModule(import.meta) + "/../migrations", | ||
"@lbu/store", | ||
migrations.namespaces, | ||
); | ||
const { migrationFiles } = await readMigrationsDir( | ||
`${dirnameForModule(import.meta)}/../migrations`, | ||
"@lbu/store", | ||
migrations.namespaces, | ||
); | ||
migrations.migrationFiles.push(...migrationFiles); | ||
} | ||
migrations.migrationFiles.push(...migrationFiles); | ||
} | ||
const mc = { | ||
files: sortMigrations(migrations.namespaces, migrations.migrationFiles), | ||
namespaces: migrations.namespaces, | ||
sql, | ||
storedHashes: {}, | ||
}; | ||
const mc = { | ||
files: sortMigrations(migrations.namespaces, migrations.migrationFiles), | ||
namespaces: migrations.namespaces, | ||
sql, | ||
storedHashes: {}, | ||
}; | ||
await acquireLock(sql); | ||
await syncWithSchemaState(mc); | ||
return mc; | ||
await Promise.race([ | ||
acquireLock(sql), | ||
new Promise((_, reject) => { | ||
setTimeout( | ||
() => reject(new Error("Could not acquire advisory lock")), | ||
2500, | ||
); | ||
}), | ||
]); | ||
await syncWithSchemaState(mc); | ||
return mc; | ||
} catch (error) { | ||
// Help user by dropping the sql connection so the application will exit | ||
sql?.end(); | ||
throw error; | ||
} | ||
} | ||
/** | ||
* Get a list of migrations to be applied | ||
* | ||
* @param {MigrateContext} mc | ||
@@ -92,6 +80,12 @@ * @returns {({name: string, number: number, repeatable: boolean}[])|boolean} | ||
export async function runMigrations(mc) { | ||
const migrationFiles = filterMigrationsToBeApplied(mc); | ||
try { | ||
const migrationFiles = filterMigrationsToBeApplied(mc); | ||
for (const migration of migrationFiles) { | ||
await runMigration(mc.sql, migration); | ||
for (const migration of migrationFiles) { | ||
await runMigration(mc.sql, migration); | ||
} | ||
} catch (error) { | ||
// Help user by dropping the sql connection so the application will exit | ||
mc?.sql?.end(); | ||
throw error; | ||
} | ||
@@ -98,0 +92,0 @@ } |
@@ -0,1 +1,2 @@ | ||
import { log } from "@lbu/insight"; | ||
import { storeQueries } from "./generated/queries.js"; | ||
@@ -57,45 +58,4 @@ | ||
/** | ||
* @name JobData | ||
* | ||
* Row data for a specific job | ||
* | ||
* @typedef {object} | ||
* @property {number} id | ||
* @property {Date} createdAt | ||
* @property {Date} scheduledAt | ||
* @property {string} name | ||
* @property {object} data | ||
*/ | ||
/** | ||
* @name JobInput | ||
* | ||
* @typedef {object} | ||
* @property {number} [priority=0] | ||
* @property {object} [data={}] | ||
* @property {Date} [scheduledAt] | ||
* @property {string} [name] | ||
*/ | ||
/** | ||
* @name JobQueueWorkerOptions | ||
* | ||
* @typedef {object} | ||
* @property {function(sql: *, data: JobData): (void|Promise<void>)} handler | ||
* @property {number} [pollInterval] Determine the poll interval in milliseconds if the | ||
* queue was empty. Defaults to 1500 ms | ||
* @property {number} [parallelCount] Set the amount of parallel jobs to process. | ||
* Defaults to 1. Make sure it is not higher than the amount of Postgres connections in | ||
* the pool | ||
*/ | ||
/** | ||
* @class | ||
* | ||
*/ | ||
export class JobQueueWorker { | ||
/** | ||
* Create a new JobQueueWorker | ||
* | ||
* @param sql | ||
@@ -131,7 +91,2 @@ * @param {string|JobQueueWorkerOptions} nameOrOptions | ||
/** | ||
* Start the JobQueueWorker | ||
* | ||
* @public | ||
*/ | ||
start() { | ||
@@ -152,8 +107,2 @@ if (this.isStarted) { | ||
/** | ||
* Stop the JobQueueWorker | ||
* Running jobs will continue to run, but no new jobs are fetched | ||
* | ||
* @public | ||
*/ | ||
stop() { | ||
@@ -170,5 +119,2 @@ if (!this.isStarted) { | ||
/** | ||
* Get the number of jobs that need to run | ||
* | ||
* @public | ||
* @returns {Promise<{pendingCount: number, scheduledCount: number}|undefined>} | ||
@@ -184,6 +130,2 @@ */ | ||
/** | ||
* Return the average time between scheduled and completed for jobs completed in the | ||
* provided time range in milliseconds | ||
* | ||
* @public | ||
* @param {Date} startDate | ||
@@ -289,3 +231,3 @@ * @param {Date} endDate | ||
.catch((e) => { | ||
console.error(e); | ||
log.error(e); | ||
}); // user should have handled error already, so ignore it | ||
@@ -292,0 +234,0 @@ } |
@@ -6,12 +6,3 @@ import { storeQueries } from "./generated/queries.js"; | ||
/** | ||
* @name SessionStore | ||
* | ||
* @typedef {object} | ||
* @property {function(string): Promise<object|boolean>} get | ||
* @property {function(string, object, number): Promise<void>} set | ||
* @property {function(string): Promise<void>} destroy | ||
*/ | ||
/** | ||
* | ||
* @param sql | ||
@@ -18,0 +9,0 @@ * @param {object} [options] |
No contributors or author data
MaintenancePackage does not specify a list of contributors or an author in package.json.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
76999
20
2188
122
6
2
+ Added@types/minio@7.0.6
+ Added@lbu/insight@0.0.46(transitive)
+ Added@lbu/stdlib@0.0.46(transitive)
+ Added@types/minio@7.0.6(transitive)
+ Added@types/node@14.0.26(transitive)
- Removed@lbu/insight@0.0.45(transitive)
- Removed@lbu/stdlib@0.0.45(transitive)
- Removed@types/node@14.0.23(transitive)
Updated@lbu/insight@0.0.46
Updated@lbu/stdlib@0.0.46