@rvoh/psychic-workers 2.0.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/src/background/BaseBackgroundedModel.js +2 -2
- package/dist/cjs/src/background/BaseBackgroundedService.js +2 -2
- package/dist/cjs/src/background/BaseScheduledService.js +2 -2
- package/dist/cjs/src/background/index.js +0 -2
- package/dist/cjs/src/cli/ASTBuilder.js +176 -0
- package/dist/cjs/src/cli/ASTWorkersSchemaBuilder.js +60 -0
- package/dist/cjs/src/cli/PsychicTypesDeprecation.js +67 -0
- package/dist/cjs/src/psychic-app-workers/index.js +19 -8
- package/dist/esm/src/background/BaseBackgroundedModel.js +2 -2
- package/dist/esm/src/background/BaseBackgroundedService.js +2 -2
- package/dist/esm/src/background/BaseScheduledService.js +2 -2
- package/dist/esm/src/background/index.js +0 -2
- package/dist/esm/src/cli/ASTBuilder.js +176 -0
- package/dist/esm/src/cli/ASTWorkersSchemaBuilder.js +60 -0
- package/dist/esm/src/cli/PsychicTypesDeprecation.js +67 -0
- package/dist/esm/src/psychic-app-workers/index.js +19 -8
- package/dist/types/src/background/BaseBackgroundedModel.d.ts +1 -1
- package/dist/types/src/background/BaseBackgroundedService.d.ts +1 -1
- package/dist/types/src/background/BaseScheduledService.d.ts +1 -1
- package/dist/types/src/cli/ASTBuilder.d.ts +89 -0
- package/dist/types/src/cli/ASTWorkersSchemaBuilder.d.ts +28 -0
- package/dist/types/src/cli/PsychicTypesDeprecation.d.ts +14 -0
- package/dist/types/src/psychic-app-workers/index.d.ts +10 -2
- package/dist/types/src/types/background.d.ts +2 -2
- package/package.json +2 -2
|
@@ -92,8 +92,8 @@ export default class BaseBackgroundedModel extends Dream {
|
|
|
92
92
|
* in psychic-workers.
|
|
93
93
|
*/
|
|
94
94
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
95
|
-
get
|
|
96
|
-
throw new Error('Must define
|
|
95
|
+
get psychicWorkerTypes() {
|
|
96
|
+
throw new Error('Must define psychicWorkerTypes getter in ApplicationBackgroundedModel class within your application');
|
|
97
97
|
}
|
|
98
98
|
/**
|
|
99
99
|
* runs the specified method in a background queue, driven by BullMQ,
|
|
@@ -104,7 +104,7 @@ export default class BaseBackgroundedService {
|
|
|
104
104
|
* in psychic-workers.
|
|
105
105
|
*/
|
|
106
106
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
107
|
-
get
|
|
108
|
-
throw new Error('Must define
|
|
107
|
+
get psychicWorkerTypes() {
|
|
108
|
+
throw new Error('Must define psychicWorkerTypes getter in ApplicationBackgroundedService class within your application');
|
|
109
109
|
}
|
|
110
110
|
}
|
|
@@ -73,7 +73,7 @@ export default class BaseScheduledService {
|
|
|
73
73
|
* in psychic-workers.
|
|
74
74
|
*/
|
|
75
75
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
76
|
-
get
|
|
77
|
-
throw new Error('Must define
|
|
76
|
+
get psychicWorkerTypes() {
|
|
77
|
+
throw new Error('Must define psychicWorkerTypes getter in ApplicationScheduledService class within your application');
|
|
78
78
|
}
|
|
79
79
|
}
|
|
@@ -526,8 +526,6 @@ export class Background {
|
|
|
526
526
|
if (!queueInstance)
|
|
527
527
|
throw new Error(`missing queue: ${jobConfig?.queue?.toString() || 'N/A'}`);
|
|
528
528
|
const jobOptions = {};
|
|
529
|
-
if (jobId)
|
|
530
|
-
jobOptions.jobId = jobId;
|
|
531
529
|
if (delay)
|
|
532
530
|
jobOptions.delay = delay;
|
|
533
531
|
if (delay && jobId) {
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { DreamApp } from '@rvoh/dream';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import ts from 'typescript';
|
|
4
|
+
import PsychicAppWorkers from '../psychic-app-workers/index.js';
|
|
5
|
+
const f = ts.factory;
|
|
6
|
+
/**
|
|
7
|
+
* @internal
|
|
8
|
+
*
|
|
9
|
+
* This is a base class, which is inherited by the ASTSchemaBuilder,
|
|
10
|
+
* the ASTKyselyCodegenEnhancer, and the ASTGlobalSchemaBuilder,
|
|
11
|
+
* each of which is responsible for building up the output of the various
|
|
12
|
+
* type files consumed by dream internally.
|
|
13
|
+
*
|
|
14
|
+
* This base class is just a container for common methods used by all
|
|
15
|
+
* classes.
|
|
16
|
+
*/
|
|
17
|
+
export default class ASTBuilder {
|
|
18
|
+
/**
|
|
19
|
+
* @internal
|
|
20
|
+
*
|
|
21
|
+
* builds a new line, useful for injecting new lines into AST statements
|
|
22
|
+
*/
|
|
23
|
+
newLine() {
|
|
24
|
+
return f.createIdentifier('\n');
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* @internal
|
|
28
|
+
*
|
|
29
|
+
* given an interface declaration, it will extrace the relevant property statement
|
|
30
|
+
* by the given property name.
|
|
31
|
+
*/
|
|
32
|
+
getPropertyFromInterface(interfaceNode, propertyName) {
|
|
33
|
+
for (const member of interfaceNode.members) {
|
|
34
|
+
if (ts.isPropertySignature(member)) {
|
|
35
|
+
if (ts.isIdentifier(member.name) && member.name.text === propertyName) {
|
|
36
|
+
return member;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* @internal
|
|
44
|
+
*
|
|
45
|
+
* returns an array of string type literals which were extracted from
|
|
46
|
+
* either a type or type union, depending on what is provided
|
|
47
|
+
* for the typeAlias. this allows you to safely and easily collect
|
|
48
|
+
* an array of types given an alias
|
|
49
|
+
*/
|
|
50
|
+
extractStringLiteralTypeNodesFromTypeOrUnion(typeAlias) {
|
|
51
|
+
const literals = [];
|
|
52
|
+
if (ts.isUnionTypeNode(typeAlias.type)) {
|
|
53
|
+
typeAlias.type.types.forEach(typeNode => {
|
|
54
|
+
if (ts.isLiteralTypeNode(typeNode) && ts.isStringLiteral(typeNode.literal)) {
|
|
55
|
+
literals.push(typeNode);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
else if (ts.isLiteralTypeNode(typeAlias.type) && ts.isStringLiteral(typeAlias.type.literal)) {
|
|
60
|
+
literals.push(typeAlias.type);
|
|
61
|
+
}
|
|
62
|
+
return literals;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* @internal
|
|
66
|
+
*
|
|
67
|
+
* returns an array of type literals which were extracted from
|
|
68
|
+
* either a type or type union, depending on what is provided
|
|
69
|
+
* for the typeAlias. this allows you to safely and easily collect
|
|
70
|
+
* an array of types given an alias
|
|
71
|
+
*/
|
|
72
|
+
extractTypeNodesFromTypeOrUnion(typeAlias) {
|
|
73
|
+
const literals = [];
|
|
74
|
+
if (typeAlias.type && ts.isUnionTypeNode(typeAlias.type)) {
|
|
75
|
+
typeAlias.type.types.forEach(typeNode => {
|
|
76
|
+
literals.push(typeNode);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
else if (typeAlias.type) {
|
|
80
|
+
literals.push(typeAlias.type);
|
|
81
|
+
}
|
|
82
|
+
return literals;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* @internal
|
|
86
|
+
*
|
|
87
|
+
* returns the provided node iff
|
|
88
|
+
* a.) the node is an exported type alias
|
|
89
|
+
* b.) the exported name matches the provided name (or else there was no name provided)
|
|
90
|
+
*
|
|
91
|
+
* otherwise, returns null
|
|
92
|
+
*/
|
|
93
|
+
exportedTypeAliasOrNull(node, exportName) {
|
|
94
|
+
if (ts.isTypeAliasDeclaration(node) &&
|
|
95
|
+
node?.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword) &&
|
|
96
|
+
(!exportName ? true : node.name.text === exportName))
|
|
97
|
+
return node;
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* @internal
|
|
102
|
+
*
|
|
103
|
+
* returns the provided node iff
|
|
104
|
+
* a.) the node is an exported interface
|
|
105
|
+
* b.) the exported name matches the provided name (or else there was no name provided)
|
|
106
|
+
*
|
|
107
|
+
* otherwise, returns null
|
|
108
|
+
*/
|
|
109
|
+
exportedInterfaceOrNull(node, exportName) {
|
|
110
|
+
if (ts.isInterfaceDeclaration(node) &&
|
|
111
|
+
node?.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword) &&
|
|
112
|
+
(!exportName ? true : node.name.text === exportName))
|
|
113
|
+
return node;
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* @internal
|
|
118
|
+
*
|
|
119
|
+
* returns the path to the dream.globals.ts file
|
|
120
|
+
*/
|
|
121
|
+
workersSchemaPath() {
|
|
122
|
+
const workersApp = PsychicAppWorkers.getOrFail();
|
|
123
|
+
return path.join(workersApp.psychicApp.apiRoot, DreamApp.getOrFail().paths.types, 'workers.ts');
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* @internal
|
|
127
|
+
*
|
|
128
|
+
* safely runs prettier against the provided output. If prettier
|
|
129
|
+
* is not installed, then the original output is returned
|
|
130
|
+
*/
|
|
131
|
+
async prettier(output) {
|
|
132
|
+
try {
|
|
133
|
+
// dynamically, safely bring in prettier.
|
|
134
|
+
// ini the event that it fails, we will return the
|
|
135
|
+
// original output, unformatted, since prettier
|
|
136
|
+
// is technically not a real dependency of dream,
|
|
137
|
+
// though psychic and dream apps are provisioned
|
|
138
|
+
// with prettier by default, so this should usually work
|
|
139
|
+
const prettier = (await import('prettier')).default;
|
|
140
|
+
const results = await prettier.format(output, {
|
|
141
|
+
parser: 'typescript',
|
|
142
|
+
semi: false,
|
|
143
|
+
singleQuote: true,
|
|
144
|
+
tabWidth: 2,
|
|
145
|
+
lineWidth: 80,
|
|
146
|
+
});
|
|
147
|
+
return typeof results === 'string' ? results : output;
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// intentional noop, we don't want to raise if prettier
|
|
151
|
+
// fails, since it is possible for the end user to not
|
|
152
|
+
// want to use prettier, and it is not a required peer
|
|
153
|
+
// dependency of dream
|
|
154
|
+
return output;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* @internal
|
|
159
|
+
*
|
|
160
|
+
* given a type node, it will send back the first found generic
|
|
161
|
+
* provided to that type.
|
|
162
|
+
*/
|
|
163
|
+
getFirstGenericType(node) {
|
|
164
|
+
if (ts.isTypeReferenceNode(node)) {
|
|
165
|
+
if (node.typeArguments && node.typeArguments.length > 0) {
|
|
166
|
+
return node.typeArguments[0];
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
else if (ts.isCallExpression(node)) {
|
|
170
|
+
if (node.typeArguments && node.typeArguments.length > 0) {
|
|
171
|
+
return node.typeArguments[0];
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { CliFileWriter, DreamCLI } from '@rvoh/dream/system';
|
|
2
|
+
import ts from 'typescript';
|
|
3
|
+
import background from '../background/index.js';
|
|
4
|
+
import ASTBuilder from './ASTBuilder.js';
|
|
5
|
+
const f = ts.factory;
|
|
6
|
+
/**
|
|
7
|
+
* Responsible for building dream globals, which can be found at
|
|
8
|
+
* types/dream.globals.ts.
|
|
9
|
+
*
|
|
10
|
+
* This class leverages internal AST building mechanisms built into
|
|
11
|
+
* typescript to manually build up object literals and interfaces
|
|
12
|
+
* for our app to consume.
|
|
13
|
+
*/
|
|
14
|
+
export default class ASTWorkersSchemaBuilder extends ASTBuilder {
|
|
15
|
+
async build() {
|
|
16
|
+
const logger = DreamCLI.logger;
|
|
17
|
+
const sourceFile = ts.createSourceFile('', '', ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
|
|
18
|
+
await logger.logProgress('[psychic workers] building workers types', async () => {
|
|
19
|
+
const output = await this.prettier(this.printStatements(this.buildWorkersTypeConfigConst(), sourceFile));
|
|
20
|
+
await CliFileWriter.write(this.workersSchemaPath(), output);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* @internal
|
|
25
|
+
*
|
|
26
|
+
* builds up the `export const globalTypeConfig = ...` statement within the types/workers.ts
|
|
27
|
+
* file. It does this by leveraging low-level AST utils built into typescript
|
|
28
|
+
* to manually build up an object literal, cast it as a const, and write it to
|
|
29
|
+
* an exported variable.
|
|
30
|
+
*/
|
|
31
|
+
buildWorkersTypeConfigConst() {
|
|
32
|
+
background.connect();
|
|
33
|
+
const globalTypeConfigObjectLiteral = f.createObjectLiteralExpression([
|
|
34
|
+
f.createPropertyAssignment(f.createIdentifier('workstreamNames'), f.createArrayLiteralExpression(background['workstreamNames'].map(key => f.createStringLiteral(key)))),
|
|
35
|
+
f.createPropertyAssignment(f.createIdentifier('queueGroupMap'), f.createObjectLiteralExpression(Object.keys(background['groupNames']).map(key => f.createPropertyAssignment(f.createStringLiteral(key), f.createArrayLiteralExpression(background['groupNames'][key].map(str => f.createStringLiteral(str))))), true)),
|
|
36
|
+
], true);
|
|
37
|
+
// add "as const" to the end of the schema object we
|
|
38
|
+
// have built before returning it
|
|
39
|
+
const constAssertion = f.createAsExpression(globalTypeConfigObjectLiteral, f.createKeywordTypeNode(ts.SyntaxKind.ConstKeyword));
|
|
40
|
+
const psychicWorkerTypesObjectLiteralConst = f.createVariableStatement(undefined, f.createVariableDeclarationList([
|
|
41
|
+
f.createVariableDeclaration(f.createIdentifier('psychicWorkerTypes'), undefined, undefined, constAssertion),
|
|
42
|
+
], ts.NodeFlags.Const));
|
|
43
|
+
const defaultExportIdentifier = f.createIdentifier('psychicWorkerTypes');
|
|
44
|
+
const exportDefaultStatement = f.createExportDefault(defaultExportIdentifier);
|
|
45
|
+
return [psychicWorkerTypesObjectLiteralConst, this.newLine(), exportDefaultStatement];
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* @internal
|
|
49
|
+
*
|
|
50
|
+
* writes the compiled statements to string.
|
|
51
|
+
*
|
|
52
|
+
*/
|
|
53
|
+
printStatements(statements, sourceFile) {
|
|
54
|
+
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed, omitTrailingSemicolon: true });
|
|
55
|
+
const result = printer.printList(ts.ListFormat.SourceFileStatements, f.createNodeArray(statements), sourceFile);
|
|
56
|
+
// TODO: add autogenerate disclaimer
|
|
57
|
+
return `\
|
|
58
|
+
${result}`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { DreamApp } from '@rvoh/dream';
|
|
2
|
+
import { DreamCLI } from '@rvoh/dream/system';
|
|
3
|
+
import * as fs from 'node:fs/promises';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import PsychicAppWorkers from '../psychic-app-workers/index.js';
|
|
6
|
+
/**
|
|
7
|
+
* Originally, psychic-workers tapped into the types produced by psychic,
|
|
8
|
+
* modifying the psychicTypes to include type configurations for workers
|
|
9
|
+
* as well. Since Psychic no longer supports this method of augmenting
|
|
10
|
+
* types, psychic-workers has been refactored to produce its own types
|
|
11
|
+
* file.
|
|
12
|
+
*
|
|
13
|
+
* This service is responsible for identifying applications that are still
|
|
14
|
+
* reliant on the types produced by psychic, and refactoring them so that their
|
|
15
|
+
* imports are now in the correct places.
|
|
16
|
+
*/
|
|
17
|
+
export default class PsychicTypesDeprecation {
|
|
18
|
+
async deprecate() {
|
|
19
|
+
const workersApp = PsychicAppWorkers.getOrFail();
|
|
20
|
+
if (workersApp.bypassDeprecationChecks)
|
|
21
|
+
return;
|
|
22
|
+
const files = [
|
|
23
|
+
path.join(process.cwd(), DreamApp.system.dreamPath('models'), 'ApplicationBackgroundedModel.ts'),
|
|
24
|
+
path.join(process.cwd(), DreamApp.system.dreamPath('models'), '..', 'services', 'ApplicationBackgroundedService.ts'),
|
|
25
|
+
path.join(process.cwd(), DreamApp.system.dreamPath('models'), '..', 'services', 'ApplicationScheduledService.ts'),
|
|
26
|
+
];
|
|
27
|
+
try {
|
|
28
|
+
for (const file of files) {
|
|
29
|
+
const fileContent = (await fs.readFile(file)).toString();
|
|
30
|
+
if (fileContent.includes('psychicTypes')) {
|
|
31
|
+
await DreamCLI.logger.logProgress(`[psychic workers] patching deprecated types for ${file.split(path.sep).at(-1)}`, async () => {
|
|
32
|
+
await fs.writeFile(file, fileContent
|
|
33
|
+
.replace(/psychicTypes/g, 'psychicWorkerTypes')
|
|
34
|
+
.replace(/types\/psychic\.js/, 'types/workers.js'));
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
console.error(err);
|
|
41
|
+
console.log(`
|
|
42
|
+
ATTENTION:
|
|
43
|
+
|
|
44
|
+
The psychic-workers package now requires a new configuration in order to continue providing types
|
|
45
|
+
in the modern psychic ecosystem. We attempted to automatically fix this for you, but something went
|
|
46
|
+
wrong. Please locate the following files, and ensure that they no longer provide the "psychicTypes" getter.
|
|
47
|
+
they should instead provide a psychicWorkerTypes getter in its place, which brings in types that
|
|
48
|
+
are now located in the newly-generated "types/workers.ts" file.
|
|
49
|
+
|
|
50
|
+
For the ApplicationBackgroundedModel.ts, ApplicationScheduledService.ts, and ApplicationBackgroundedService.ts
|
|
51
|
+
files in your system, ensure that their "psychicTypes" getter is replaced with the "psychicWorkerTypes"
|
|
52
|
+
getter, like so:
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
import { psychicWorkerTypes } from '@src/types/workers.js'
|
|
56
|
+
|
|
57
|
+
export default class ApplicationBackgroundedModel extends BaseBackgroundedModel {
|
|
58
|
+
...
|
|
59
|
+
|
|
60
|
+
public override get psychicWorkerTypes() {
|
|
61
|
+
return psychicWorkerTypes
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import { Queue, Worker } from 'bullmq';
|
|
2
|
-
import { cachePsychicWorkersApp, getCachedPsychicWorkersAppOrFail } from './cache.js';
|
|
3
2
|
import background from '../background/index.js';
|
|
3
|
+
import ASTWorkersSchemaBuilder from '../cli/ASTWorkersSchemaBuilder.js';
|
|
4
|
+
import PsychicTypesDeprecation from '../cli/PsychicTypesDeprecation.js';
|
|
5
|
+
import { cachePsychicWorkersApp, getCachedPsychicWorkersAppOrFail } from './cache.js';
|
|
4
6
|
export default class PsychicAppWorkers {
|
|
5
7
|
static async init(psychicApp, cb) {
|
|
6
8
|
const psychicWorkersApp = new PsychicAppWorkers(psychicApp);
|
|
7
9
|
await cb(psychicWorkersApp);
|
|
8
|
-
psychicApp.on('cli:sync', () => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
workstreamNames: [...background['workstreamNames']],
|
|
12
|
-
queueGroupMap: { ...background['groupNames'] },
|
|
13
|
-
};
|
|
14
|
-
return output;
|
|
10
|
+
psychicApp.on('cli:sync', async () => {
|
|
11
|
+
await new ASTWorkersSchemaBuilder().build();
|
|
12
|
+
await new PsychicTypesDeprecation().deprecate();
|
|
15
13
|
});
|
|
16
14
|
psychicApp.on('server:shutdown', async () => {
|
|
17
15
|
await background.closeAllRedisConnections();
|
|
@@ -56,6 +54,16 @@ export default class PsychicAppWorkers {
|
|
|
56
54
|
return this._testInvocation;
|
|
57
55
|
}
|
|
58
56
|
_testInvocation = 'automatic';
|
|
57
|
+
/**
|
|
58
|
+
* if set to true, it will bypass deprecation checks that run
|
|
59
|
+
* during the sync hook. Defaults to false, we only recommend
|
|
60
|
+
* overriding this if you are having issues with the deprecation
|
|
61
|
+
* check.
|
|
62
|
+
*/
|
|
63
|
+
get bypassDeprecationChecks() {
|
|
64
|
+
return this._bypassDeprecationChecks;
|
|
65
|
+
}
|
|
66
|
+
_bypassDeprecationChecks = false;
|
|
59
67
|
_hooks = {
|
|
60
68
|
workerShutdown: [],
|
|
61
69
|
};
|
|
@@ -87,6 +95,9 @@ export default class PsychicAppWorkers {
|
|
|
87
95
|
case 'testInvocation':
|
|
88
96
|
this._testInvocation = value;
|
|
89
97
|
break;
|
|
98
|
+
case 'bypassDeprecationChecks':
|
|
99
|
+
this._bypassDeprecationChecks = value;
|
|
100
|
+
break;
|
|
90
101
|
default:
|
|
91
102
|
throw new Error(`Unhandled option type passed to PsychicWorkersApp#set: ${option}`);
|
|
92
103
|
}
|
|
@@ -92,8 +92,8 @@ export default class BaseBackgroundedModel extends Dream {
|
|
|
92
92
|
* in psychic-workers.
|
|
93
93
|
*/
|
|
94
94
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
95
|
-
get
|
|
96
|
-
throw new Error('Must define
|
|
95
|
+
get psychicWorkerTypes() {
|
|
96
|
+
throw new Error('Must define psychicWorkerTypes getter in ApplicationBackgroundedModel class within your application');
|
|
97
97
|
}
|
|
98
98
|
/**
|
|
99
99
|
* runs the specified method in a background queue, driven by BullMQ,
|
|
@@ -104,7 +104,7 @@ export default class BaseBackgroundedService {
|
|
|
104
104
|
* in psychic-workers.
|
|
105
105
|
*/
|
|
106
106
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
107
|
-
get
|
|
108
|
-
throw new Error('Must define
|
|
107
|
+
get psychicWorkerTypes() {
|
|
108
|
+
throw new Error('Must define psychicWorkerTypes getter in ApplicationBackgroundedService class within your application');
|
|
109
109
|
}
|
|
110
110
|
}
|
|
@@ -73,7 +73,7 @@ export default class BaseScheduledService {
|
|
|
73
73
|
* in psychic-workers.
|
|
74
74
|
*/
|
|
75
75
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
76
|
-
get
|
|
77
|
-
throw new Error('Must define
|
|
76
|
+
get psychicWorkerTypes() {
|
|
77
|
+
throw new Error('Must define psychicWorkerTypes getter in ApplicationScheduledService class within your application');
|
|
78
78
|
}
|
|
79
79
|
}
|
|
@@ -526,8 +526,6 @@ export class Background {
|
|
|
526
526
|
if (!queueInstance)
|
|
527
527
|
throw new Error(`missing queue: ${jobConfig?.queue?.toString() || 'N/A'}`);
|
|
528
528
|
const jobOptions = {};
|
|
529
|
-
if (jobId)
|
|
530
|
-
jobOptions.jobId = jobId;
|
|
531
529
|
if (delay)
|
|
532
530
|
jobOptions.delay = delay;
|
|
533
531
|
if (delay && jobId) {
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { DreamApp } from '@rvoh/dream';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
import ts from 'typescript';
|
|
4
|
+
import PsychicAppWorkers from '../psychic-app-workers/index.js';
|
|
5
|
+
const f = ts.factory;
|
|
6
|
+
/**
|
|
7
|
+
* @internal
|
|
8
|
+
*
|
|
9
|
+
* This is a base class, which is inherited by the ASTSchemaBuilder,
|
|
10
|
+
* the ASTKyselyCodegenEnhancer, and the ASTGlobalSchemaBuilder,
|
|
11
|
+
* each of which is responsible for building up the output of the various
|
|
12
|
+
* type files consumed by dream internally.
|
|
13
|
+
*
|
|
14
|
+
* This base class is just a container for common methods used by all
|
|
15
|
+
* classes.
|
|
16
|
+
*/
|
|
17
|
+
export default class ASTBuilder {
|
|
18
|
+
/**
|
|
19
|
+
* @internal
|
|
20
|
+
*
|
|
21
|
+
* builds a new line, useful for injecting new lines into AST statements
|
|
22
|
+
*/
|
|
23
|
+
newLine() {
|
|
24
|
+
return f.createIdentifier('\n');
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* @internal
|
|
28
|
+
*
|
|
29
|
+
* given an interface declaration, it will extrace the relevant property statement
|
|
30
|
+
* by the given property name.
|
|
31
|
+
*/
|
|
32
|
+
getPropertyFromInterface(interfaceNode, propertyName) {
|
|
33
|
+
for (const member of interfaceNode.members) {
|
|
34
|
+
if (ts.isPropertySignature(member)) {
|
|
35
|
+
if (ts.isIdentifier(member.name) && member.name.text === propertyName) {
|
|
36
|
+
return member;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* @internal
|
|
44
|
+
*
|
|
45
|
+
* returns an array of string type literals which were extracted from
|
|
46
|
+
* either a type or type union, depending on what is provided
|
|
47
|
+
* for the typeAlias. this allows you to safely and easily collect
|
|
48
|
+
* an array of types given an alias
|
|
49
|
+
*/
|
|
50
|
+
extractStringLiteralTypeNodesFromTypeOrUnion(typeAlias) {
|
|
51
|
+
const literals = [];
|
|
52
|
+
if (ts.isUnionTypeNode(typeAlias.type)) {
|
|
53
|
+
typeAlias.type.types.forEach(typeNode => {
|
|
54
|
+
if (ts.isLiteralTypeNode(typeNode) && ts.isStringLiteral(typeNode.literal)) {
|
|
55
|
+
literals.push(typeNode);
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
else if (ts.isLiteralTypeNode(typeAlias.type) && ts.isStringLiteral(typeAlias.type.literal)) {
|
|
60
|
+
literals.push(typeAlias.type);
|
|
61
|
+
}
|
|
62
|
+
return literals;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* @internal
|
|
66
|
+
*
|
|
67
|
+
* returns an array of type literals which were extracted from
|
|
68
|
+
* either a type or type union, depending on what is provided
|
|
69
|
+
* for the typeAlias. this allows you to safely and easily collect
|
|
70
|
+
* an array of types given an alias
|
|
71
|
+
*/
|
|
72
|
+
extractTypeNodesFromTypeOrUnion(typeAlias) {
|
|
73
|
+
const literals = [];
|
|
74
|
+
if (typeAlias.type && ts.isUnionTypeNode(typeAlias.type)) {
|
|
75
|
+
typeAlias.type.types.forEach(typeNode => {
|
|
76
|
+
literals.push(typeNode);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
else if (typeAlias.type) {
|
|
80
|
+
literals.push(typeAlias.type);
|
|
81
|
+
}
|
|
82
|
+
return literals;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* @internal
|
|
86
|
+
*
|
|
87
|
+
* returns the provided node iff
|
|
88
|
+
* a.) the node is an exported type alias
|
|
89
|
+
* b.) the exported name matches the provided name (or else there was no name provided)
|
|
90
|
+
*
|
|
91
|
+
* otherwise, returns null
|
|
92
|
+
*/
|
|
93
|
+
exportedTypeAliasOrNull(node, exportName) {
|
|
94
|
+
if (ts.isTypeAliasDeclaration(node) &&
|
|
95
|
+
node?.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword) &&
|
|
96
|
+
(!exportName ? true : node.name.text === exportName))
|
|
97
|
+
return node;
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* @internal
|
|
102
|
+
*
|
|
103
|
+
* returns the provided node iff
|
|
104
|
+
* a.) the node is an exported interface
|
|
105
|
+
* b.) the exported name matches the provided name (or else there was no name provided)
|
|
106
|
+
*
|
|
107
|
+
* otherwise, returns null
|
|
108
|
+
*/
|
|
109
|
+
exportedInterfaceOrNull(node, exportName) {
|
|
110
|
+
if (ts.isInterfaceDeclaration(node) &&
|
|
111
|
+
node?.modifiers?.some(m => m.kind === ts.SyntaxKind.ExportKeyword) &&
|
|
112
|
+
(!exportName ? true : node.name.text === exportName))
|
|
113
|
+
return node;
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* @internal
|
|
118
|
+
*
|
|
119
|
+
* returns the path to the dream.globals.ts file
|
|
120
|
+
*/
|
|
121
|
+
workersSchemaPath() {
|
|
122
|
+
const workersApp = PsychicAppWorkers.getOrFail();
|
|
123
|
+
return path.join(workersApp.psychicApp.apiRoot, DreamApp.getOrFail().paths.types, 'workers.ts');
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* @internal
|
|
127
|
+
*
|
|
128
|
+
* safely runs prettier against the provided output. If prettier
|
|
129
|
+
* is not installed, then the original output is returned
|
|
130
|
+
*/
|
|
131
|
+
async prettier(output) {
|
|
132
|
+
try {
|
|
133
|
+
// dynamically, safely bring in prettier.
|
|
134
|
+
// ini the event that it fails, we will return the
|
|
135
|
+
// original output, unformatted, since prettier
|
|
136
|
+
// is technically not a real dependency of dream,
|
|
137
|
+
// though psychic and dream apps are provisioned
|
|
138
|
+
// with prettier by default, so this should usually work
|
|
139
|
+
const prettier = (await import('prettier')).default;
|
|
140
|
+
const results = await prettier.format(output, {
|
|
141
|
+
parser: 'typescript',
|
|
142
|
+
semi: false,
|
|
143
|
+
singleQuote: true,
|
|
144
|
+
tabWidth: 2,
|
|
145
|
+
lineWidth: 80,
|
|
146
|
+
});
|
|
147
|
+
return typeof results === 'string' ? results : output;
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
// intentional noop, we don't want to raise if prettier
|
|
151
|
+
// fails, since it is possible for the end user to not
|
|
152
|
+
// want to use prettier, and it is not a required peer
|
|
153
|
+
// dependency of dream
|
|
154
|
+
return output;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* @internal
|
|
159
|
+
*
|
|
160
|
+
* given a type node, it will send back the first found generic
|
|
161
|
+
* provided to that type.
|
|
162
|
+
*/
|
|
163
|
+
getFirstGenericType(node) {
|
|
164
|
+
if (ts.isTypeReferenceNode(node)) {
|
|
165
|
+
if (node.typeArguments && node.typeArguments.length > 0) {
|
|
166
|
+
return node.typeArguments[0];
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
else if (ts.isCallExpression(node)) {
|
|
170
|
+
if (node.typeArguments && node.typeArguments.length > 0) {
|
|
171
|
+
return node.typeArguments[0];
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { CliFileWriter, DreamCLI } from '@rvoh/dream/system';
|
|
2
|
+
import ts from 'typescript';
|
|
3
|
+
import background from '../background/index.js';
|
|
4
|
+
import ASTBuilder from './ASTBuilder.js';
|
|
5
|
+
const f = ts.factory;
|
|
6
|
+
/**
|
|
7
|
+
* Responsible for building dream globals, which can be found at
|
|
8
|
+
* types/dream.globals.ts.
|
|
9
|
+
*
|
|
10
|
+
* This class leverages internal AST building mechanisms built into
|
|
11
|
+
* typescript to manually build up object literals and interfaces
|
|
12
|
+
* for our app to consume.
|
|
13
|
+
*/
|
|
14
|
+
export default class ASTWorkersSchemaBuilder extends ASTBuilder {
|
|
15
|
+
async build() {
|
|
16
|
+
const logger = DreamCLI.logger;
|
|
17
|
+
const sourceFile = ts.createSourceFile('', '', ts.ScriptTarget.Latest, false, ts.ScriptKind.TS);
|
|
18
|
+
await logger.logProgress('[psychic workers] building workers types', async () => {
|
|
19
|
+
const output = await this.prettier(this.printStatements(this.buildWorkersTypeConfigConst(), sourceFile));
|
|
20
|
+
await CliFileWriter.write(this.workersSchemaPath(), output);
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* @internal
|
|
25
|
+
*
|
|
26
|
+
* builds up the `export const globalTypeConfig = ...` statement within the types/workers.ts
|
|
27
|
+
* file. It does this by leveraging low-level AST utils built into typescript
|
|
28
|
+
* to manually build up an object literal, cast it as a const, and write it to
|
|
29
|
+
* an exported variable.
|
|
30
|
+
*/
|
|
31
|
+
buildWorkersTypeConfigConst() {
|
|
32
|
+
background.connect();
|
|
33
|
+
const globalTypeConfigObjectLiteral = f.createObjectLiteralExpression([
|
|
34
|
+
f.createPropertyAssignment(f.createIdentifier('workstreamNames'), f.createArrayLiteralExpression(background['workstreamNames'].map(key => f.createStringLiteral(key)))),
|
|
35
|
+
f.createPropertyAssignment(f.createIdentifier('queueGroupMap'), f.createObjectLiteralExpression(Object.keys(background['groupNames']).map(key => f.createPropertyAssignment(f.createStringLiteral(key), f.createArrayLiteralExpression(background['groupNames'][key].map(str => f.createStringLiteral(str))))), true)),
|
|
36
|
+
], true);
|
|
37
|
+
// add "as const" to the end of the schema object we
|
|
38
|
+
// have built before returning it
|
|
39
|
+
const constAssertion = f.createAsExpression(globalTypeConfigObjectLiteral, f.createKeywordTypeNode(ts.SyntaxKind.ConstKeyword));
|
|
40
|
+
const psychicWorkerTypesObjectLiteralConst = f.createVariableStatement(undefined, f.createVariableDeclarationList([
|
|
41
|
+
f.createVariableDeclaration(f.createIdentifier('psychicWorkerTypes'), undefined, undefined, constAssertion),
|
|
42
|
+
], ts.NodeFlags.Const));
|
|
43
|
+
const defaultExportIdentifier = f.createIdentifier('psychicWorkerTypes');
|
|
44
|
+
const exportDefaultStatement = f.createExportDefault(defaultExportIdentifier);
|
|
45
|
+
return [psychicWorkerTypesObjectLiteralConst, this.newLine(), exportDefaultStatement];
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* @internal
|
|
49
|
+
*
|
|
50
|
+
* writes the compiled statements to string.
|
|
51
|
+
*
|
|
52
|
+
*/
|
|
53
|
+
printStatements(statements, sourceFile) {
|
|
54
|
+
const printer = ts.createPrinter({ newLine: ts.NewLineKind.LineFeed, omitTrailingSemicolon: true });
|
|
55
|
+
const result = printer.printList(ts.ListFormat.SourceFileStatements, f.createNodeArray(statements), sourceFile);
|
|
56
|
+
// TODO: add autogenerate disclaimer
|
|
57
|
+
return `\
|
|
58
|
+
${result}`;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { DreamApp } from '@rvoh/dream';
|
|
2
|
+
import { DreamCLI } from '@rvoh/dream/system';
|
|
3
|
+
import * as fs from 'node:fs/promises';
|
|
4
|
+
import * as path from 'node:path';
|
|
5
|
+
import PsychicAppWorkers from '../psychic-app-workers/index.js';
|
|
6
|
+
/**
|
|
7
|
+
* Originally, psychic-workers tapped into the types produced by psychic,
|
|
8
|
+
* modifying the psychicTypes to include type configurations for workers
|
|
9
|
+
* as well. Since Psychic no longer supports this method of augmenting
|
|
10
|
+
* types, psychic-workers has been refactored to produce its own types
|
|
11
|
+
* file.
|
|
12
|
+
*
|
|
13
|
+
* This service is responsible for identifying applications that are still
|
|
14
|
+
* reliant on the types produced by psychic, and refactoring them so that their
|
|
15
|
+
* imports are now in the correct places.
|
|
16
|
+
*/
|
|
17
|
+
export default class PsychicTypesDeprecation {
|
|
18
|
+
async deprecate() {
|
|
19
|
+
const workersApp = PsychicAppWorkers.getOrFail();
|
|
20
|
+
if (workersApp.bypassDeprecationChecks)
|
|
21
|
+
return;
|
|
22
|
+
const files = [
|
|
23
|
+
path.join(process.cwd(), DreamApp.system.dreamPath('models'), 'ApplicationBackgroundedModel.ts'),
|
|
24
|
+
path.join(process.cwd(), DreamApp.system.dreamPath('models'), '..', 'services', 'ApplicationBackgroundedService.ts'),
|
|
25
|
+
path.join(process.cwd(), DreamApp.system.dreamPath('models'), '..', 'services', 'ApplicationScheduledService.ts'),
|
|
26
|
+
];
|
|
27
|
+
try {
|
|
28
|
+
for (const file of files) {
|
|
29
|
+
const fileContent = (await fs.readFile(file)).toString();
|
|
30
|
+
if (fileContent.includes('psychicTypes')) {
|
|
31
|
+
await DreamCLI.logger.logProgress(`[psychic workers] patching deprecated types for ${file.split(path.sep).at(-1)}`, async () => {
|
|
32
|
+
await fs.writeFile(file, fileContent
|
|
33
|
+
.replace(/psychicTypes/g, 'psychicWorkerTypes')
|
|
34
|
+
.replace(/types\/psychic\.js/, 'types/workers.js'));
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
catch (err) {
|
|
40
|
+
console.error(err);
|
|
41
|
+
console.log(`
|
|
42
|
+
ATTENTION:
|
|
43
|
+
|
|
44
|
+
The psychic-workers package now requires a new configuration in order to continue providing types
|
|
45
|
+
in the modern psychic ecosystem. We attempted to automatically fix this for you, but something went
|
|
46
|
+
wrong. Please locate the following files, and ensure that they no longer provide the "psychicTypes" getter.
|
|
47
|
+
they should instead provide a psychicWorkerTypes getter in its place, which brings in types that
|
|
48
|
+
are now located in the newly-generated "types/workers.ts" file.
|
|
49
|
+
|
|
50
|
+
For the ApplicationBackgroundedModel.ts, ApplicationScheduledService.ts, and ApplicationBackgroundedService.ts
|
|
51
|
+
files in your system, ensure that their "psychicTypes" getter is replaced with the "psychicWorkerTypes"
|
|
52
|
+
getter, like so:
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
import { psychicWorkerTypes } from '@src/types/workers.js'
|
|
56
|
+
|
|
57
|
+
export default class ApplicationBackgroundedModel extends BaseBackgroundedModel {
|
|
58
|
+
...
|
|
59
|
+
|
|
60
|
+
public override get psychicWorkerTypes() {
|
|
61
|
+
return psychicWorkerTypes
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -1,17 +1,15 @@
|
|
|
1
1
|
import { Queue, Worker } from 'bullmq';
|
|
2
|
-
import { cachePsychicWorkersApp, getCachedPsychicWorkersAppOrFail } from './cache.js';
|
|
3
2
|
import background from '../background/index.js';
|
|
3
|
+
import ASTWorkersSchemaBuilder from '../cli/ASTWorkersSchemaBuilder.js';
|
|
4
|
+
import PsychicTypesDeprecation from '../cli/PsychicTypesDeprecation.js';
|
|
5
|
+
import { cachePsychicWorkersApp, getCachedPsychicWorkersAppOrFail } from './cache.js';
|
|
4
6
|
export default class PsychicAppWorkers {
|
|
5
7
|
static async init(psychicApp, cb) {
|
|
6
8
|
const psychicWorkersApp = new PsychicAppWorkers(psychicApp);
|
|
7
9
|
await cb(psychicWorkersApp);
|
|
8
|
-
psychicApp.on('cli:sync', () => {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
workstreamNames: [...background['workstreamNames']],
|
|
12
|
-
queueGroupMap: { ...background['groupNames'] },
|
|
13
|
-
};
|
|
14
|
-
return output;
|
|
10
|
+
psychicApp.on('cli:sync', async () => {
|
|
11
|
+
await new ASTWorkersSchemaBuilder().build();
|
|
12
|
+
await new PsychicTypesDeprecation().deprecate();
|
|
15
13
|
});
|
|
16
14
|
psychicApp.on('server:shutdown', async () => {
|
|
17
15
|
await background.closeAllRedisConnections();
|
|
@@ -56,6 +54,16 @@ export default class PsychicAppWorkers {
|
|
|
56
54
|
return this._testInvocation;
|
|
57
55
|
}
|
|
58
56
|
_testInvocation = 'automatic';
|
|
57
|
+
/**
|
|
58
|
+
* if set to true, it will bypass deprecation checks that run
|
|
59
|
+
* during the sync hook. Defaults to false, we only recommend
|
|
60
|
+
* overriding this if you are having issues with the deprecation
|
|
61
|
+
* check.
|
|
62
|
+
*/
|
|
63
|
+
get bypassDeprecationChecks() {
|
|
64
|
+
return this._bypassDeprecationChecks;
|
|
65
|
+
}
|
|
66
|
+
_bypassDeprecationChecks = false;
|
|
59
67
|
_hooks = {
|
|
60
68
|
workerShutdown: [],
|
|
61
69
|
};
|
|
@@ -87,6 +95,9 @@ export default class PsychicAppWorkers {
|
|
|
87
95
|
case 'testInvocation':
|
|
88
96
|
this._testInvocation = value;
|
|
89
97
|
break;
|
|
98
|
+
case 'bypassDeprecationChecks':
|
|
99
|
+
this._bypassDeprecationChecks = value;
|
|
100
|
+
break;
|
|
90
101
|
default:
|
|
91
102
|
throw new Error(`Unhandled option type passed to PsychicWorkersApp#set: ${option}`);
|
|
92
103
|
}
|
|
@@ -71,7 +71,7 @@ export default class BaseBackgroundedModel extends Dream {
|
|
|
71
71
|
* metadata, which can be used to help provide types for the underlying methods
|
|
72
72
|
* in psychic-workers.
|
|
73
73
|
*/
|
|
74
|
-
get
|
|
74
|
+
get psychicWorkerTypes(): any;
|
|
75
75
|
/**
|
|
76
76
|
* runs the specified method in a background queue, driven by BullMQ,
|
|
77
77
|
* sending in the provided args.
|
|
@@ -79,7 +79,7 @@ export default class BaseBackgroundedService {
|
|
|
79
79
|
* metadata, which can be used to help provide types for the underlying methods
|
|
80
80
|
* in psychic-workers.
|
|
81
81
|
*/
|
|
82
|
-
get
|
|
82
|
+
get psychicWorkerTypes(): any;
|
|
83
83
|
}
|
|
84
84
|
export type PsychicBackgroundedServiceStaticMethods<T extends typeof BaseBackgroundedService> = Exclude<FunctionPropertyNames<Required<T>>, FunctionPropertyNames<typeof BaseBackgroundedService>>;
|
|
85
85
|
export type PsychicBackgroundedServiceInstanceMethods<T extends BaseBackgroundedService> = Exclude<FunctionPropertyNames<Required<T>>, FunctionPropertyNames<BaseBackgroundedService>>;
|
|
@@ -57,6 +57,6 @@ export default class BaseScheduledService {
|
|
|
57
57
|
* metadata, which can be used to help provide types for the underlying methods
|
|
58
58
|
* in psychic-workers.
|
|
59
59
|
*/
|
|
60
|
-
get
|
|
60
|
+
get psychicWorkerTypes(): any;
|
|
61
61
|
}
|
|
62
62
|
export type PsychicScheduledServiceStaticMethods<T extends typeof BaseScheduledService> = Exclude<FunctionPropertyNames<Required<T>>, FunctionPropertyNames<typeof BaseScheduledService>>;
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
/**
|
|
3
|
+
* @internal
|
|
4
|
+
*
|
|
5
|
+
* This is a base class, which is inherited by the ASTSchemaBuilder,
|
|
6
|
+
* the ASTKyselyCodegenEnhancer, and the ASTGlobalSchemaBuilder,
|
|
7
|
+
* each of which is responsible for building up the output of the various
|
|
8
|
+
* type files consumed by dream internally.
|
|
9
|
+
*
|
|
10
|
+
* This base class is just a container for common methods used by all
|
|
11
|
+
* classes.
|
|
12
|
+
*/
|
|
13
|
+
export default class ASTBuilder {
|
|
14
|
+
/**
|
|
15
|
+
* @internal
|
|
16
|
+
*
|
|
17
|
+
* builds a new line, useful for injecting new lines into AST statements
|
|
18
|
+
*/
|
|
19
|
+
protected newLine(): ts.Identifier;
|
|
20
|
+
/**
|
|
21
|
+
* @internal
|
|
22
|
+
*
|
|
23
|
+
* given an interface declaration, it will extrace the relevant property statement
|
|
24
|
+
* by the given property name.
|
|
25
|
+
*/
|
|
26
|
+
protected getPropertyFromInterface(interfaceNode: ts.InterfaceDeclaration, propertyName: string): ts.PropertySignature | null;
|
|
27
|
+
/**
|
|
28
|
+
* @internal
|
|
29
|
+
*
|
|
30
|
+
* returns an array of string type literals which were extracted from
|
|
31
|
+
* either a type or type union, depending on what is provided
|
|
32
|
+
* for the typeAlias. this allows you to safely and easily collect
|
|
33
|
+
* an array of types given an alias
|
|
34
|
+
*/
|
|
35
|
+
protected extractStringLiteralTypeNodesFromTypeOrUnion(typeAlias: ts.TypeAliasDeclaration): (ts.LiteralTypeNode & {
|
|
36
|
+
literal: {
|
|
37
|
+
text: string;
|
|
38
|
+
};
|
|
39
|
+
})[];
|
|
40
|
+
/**
|
|
41
|
+
* @internal
|
|
42
|
+
*
|
|
43
|
+
* returns an array of type literals which were extracted from
|
|
44
|
+
* either a type or type union, depending on what is provided
|
|
45
|
+
* for the typeAlias. this allows you to safely and easily collect
|
|
46
|
+
* an array of types given an alias
|
|
47
|
+
*/
|
|
48
|
+
protected extractTypeNodesFromTypeOrUnion(typeAlias: ts.TypeAliasDeclaration | ts.PropertySignature): ts.TypeNode[];
|
|
49
|
+
/**
|
|
50
|
+
* @internal
|
|
51
|
+
*
|
|
52
|
+
* returns the provided node iff
|
|
53
|
+
* a.) the node is an exported type alias
|
|
54
|
+
* b.) the exported name matches the provided name (or else there was no name provided)
|
|
55
|
+
*
|
|
56
|
+
* otherwise, returns null
|
|
57
|
+
*/
|
|
58
|
+
protected exportedTypeAliasOrNull(node: ts.Node, exportName?: string): ts.TypeAliasDeclaration | null;
|
|
59
|
+
/**
|
|
60
|
+
* @internal
|
|
61
|
+
*
|
|
62
|
+
* returns the provided node iff
|
|
63
|
+
* a.) the node is an exported interface
|
|
64
|
+
* b.) the exported name matches the provided name (or else there was no name provided)
|
|
65
|
+
*
|
|
66
|
+
* otherwise, returns null
|
|
67
|
+
*/
|
|
68
|
+
protected exportedInterfaceOrNull(node: ts.Node, exportName?: string): ts.InterfaceDeclaration | null;
|
|
69
|
+
/**
|
|
70
|
+
* @internal
|
|
71
|
+
*
|
|
72
|
+
* returns the path to the dream.globals.ts file
|
|
73
|
+
*/
|
|
74
|
+
protected workersSchemaPath(): string;
|
|
75
|
+
/**
|
|
76
|
+
* @internal
|
|
77
|
+
*
|
|
78
|
+
* safely runs prettier against the provided output. If prettier
|
|
79
|
+
* is not installed, then the original output is returned
|
|
80
|
+
*/
|
|
81
|
+
protected prettier(output: string): Promise<string>;
|
|
82
|
+
/**
|
|
83
|
+
* @internal
|
|
84
|
+
*
|
|
85
|
+
* given a type node, it will send back the first found generic
|
|
86
|
+
* provided to that type.
|
|
87
|
+
*/
|
|
88
|
+
protected getFirstGenericType(node: ts.Node): ts.TypeNode | null;
|
|
89
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import ASTBuilder from './ASTBuilder.js';
|
|
2
|
+
/**
|
|
3
|
+
* Responsible for building dream globals, which can be found at
|
|
4
|
+
* types/dream.globals.ts.
|
|
5
|
+
*
|
|
6
|
+
* This class leverages internal AST building mechanisms built into
|
|
7
|
+
* typescript to manually build up object literals and interfaces
|
|
8
|
+
* for our app to consume.
|
|
9
|
+
*/
|
|
10
|
+
export default class ASTWorkersSchemaBuilder extends ASTBuilder {
|
|
11
|
+
build(): Promise<void>;
|
|
12
|
+
/**
|
|
13
|
+
* @internal
|
|
14
|
+
*
|
|
15
|
+
* builds up the `export const globalTypeConfig = ...` statement within the types/workers.ts
|
|
16
|
+
* file. It does this by leveraging low-level AST utils built into typescript
|
|
17
|
+
* to manually build up an object literal, cast it as a const, and write it to
|
|
18
|
+
* an exported variable.
|
|
19
|
+
*/
|
|
20
|
+
private buildWorkersTypeConfigConst;
|
|
21
|
+
/**
|
|
22
|
+
* @internal
|
|
23
|
+
*
|
|
24
|
+
* writes the compiled statements to string.
|
|
25
|
+
*
|
|
26
|
+
*/
|
|
27
|
+
private printStatements;
|
|
28
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Originally, psychic-workers tapped into the types produced by psychic,
|
|
3
|
+
* modifying the psychicTypes to include type configurations for workers
|
|
4
|
+
* as well. Since Psychic no longer supports this method of augmenting
|
|
5
|
+
* types, psychic-workers has been refactored to produce its own types
|
|
6
|
+
* file.
|
|
7
|
+
*
|
|
8
|
+
* This service is responsible for identifying applications that are still
|
|
9
|
+
* reliant on the types produced by psychic, and refactoring them so that their
|
|
10
|
+
* imports are now in the correct places.
|
|
11
|
+
*/
|
|
12
|
+
export default class PsychicTypesDeprecation {
|
|
13
|
+
deprecate(): Promise<void>;
|
|
14
|
+
}
|
|
@@ -30,16 +30,24 @@ export default class PsychicAppWorkers {
|
|
|
30
30
|
*/
|
|
31
31
|
get testInvocation(): PsychicWorkersAppTestInvocationType;
|
|
32
32
|
private _testInvocation;
|
|
33
|
+
/**
|
|
34
|
+
* if set to true, it will bypass deprecation checks that run
|
|
35
|
+
* during the sync hook. Defaults to false, we only recommend
|
|
36
|
+
* overriding this if you are having issues with the deprecation
|
|
37
|
+
* check.
|
|
38
|
+
*/
|
|
39
|
+
get bypassDeprecationChecks(): boolean;
|
|
40
|
+
private _bypassDeprecationChecks;
|
|
33
41
|
private _hooks;
|
|
34
42
|
get hooks(): PsychicWorkersAppHooks;
|
|
35
43
|
on<T extends PsychicWorkersHookEventType>(hookEventType: T, cb: T extends 'workers:shutdown' ? () => void | Promise<void> : never): void;
|
|
36
|
-
set<Opt extends PsychicWorkersAppOption>(option: Opt, value: Opt extends 'background' ? PsychicBackgroundOptions : Opt extends 'testInvocation' ? PsychicWorkersAppTestInvocationType : unknown): void;
|
|
44
|
+
set<Opt extends PsychicWorkersAppOption>(option: Opt, value: Opt extends 'background' ? PsychicBackgroundOptions : Opt extends 'testInvocation' ? PsychicWorkersAppTestInvocationType : Opt extends 'bypassDeprecationChecks' ? boolean : unknown): void;
|
|
37
45
|
}
|
|
38
46
|
export interface PsychicWorkersTypeSync {
|
|
39
47
|
workstreamNames: string[];
|
|
40
48
|
queueGroupMap: Record<string, string[]>;
|
|
41
49
|
}
|
|
42
|
-
export type PsychicWorkersAppOption = 'background' | 'testInvocation';
|
|
50
|
+
export type PsychicWorkersAppOption = 'background' | 'testInvocation' | 'bypassDeprecationChecks';
|
|
43
51
|
export type PsychicWorkersAppTestInvocationType = 'automatic' | 'manual';
|
|
44
52
|
export type PsychicWorkersHookEventType = 'workers:shutdown';
|
|
45
53
|
export interface PsychicWorkersAppHooks {
|
|
@@ -49,9 +49,9 @@ interface BaseBackgroundJobConfig {
|
|
|
49
49
|
priority?: BackgroundQueuePriority;
|
|
50
50
|
}
|
|
51
51
|
export interface WorkstreamBackgroundJobConfig<T extends BaseScheduledService | BaseBackgroundedService> extends BaseBackgroundJobConfig {
|
|
52
|
-
workstream?: T['
|
|
52
|
+
workstream?: T['psychicWorkerTypes']['workstreamNames'][number];
|
|
53
53
|
}
|
|
54
|
-
export interface QueueBackgroundJobConfig<T extends BaseScheduledService | BaseBackgroundedService,
|
|
54
|
+
export interface QueueBackgroundJobConfig<T extends BaseScheduledService | BaseBackgroundedService, WorkerTypes extends T['psychicWorkerTypes'] = T['psychicWorkerTypes'], QueueGroupMap = WorkerTypes['queueGroupMap'], Queue extends keyof QueueGroupMap & string = keyof QueueGroupMap & string, Groups extends QueueGroupMap[Queue] = QueueGroupMap[Queue], GroupId = Groups[number & keyof Groups]> extends BaseBackgroundJobConfig {
|
|
55
55
|
groupId?: GroupId;
|
|
56
56
|
queue?: Queue;
|
|
57
57
|
}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@rvoh/psychic-workers",
|
|
4
4
|
"description": "Background job system for Psychic applications",
|
|
5
|
-
"version": "2.0
|
|
5
|
+
"version": "2.1.0",
|
|
6
6
|
"author": "RVO Health",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"supertest": "^7.1.4",
|
|
85
85
|
"tslib": "^2.7.0",
|
|
86
86
|
"tsx": "^4.20.6",
|
|
87
|
-
"typedoc": "^0.
|
|
87
|
+
"typedoc": "^0.28.15",
|
|
88
88
|
"typescript": "^5.8.2",
|
|
89
89
|
"typescript-eslint": "=7.18.0",
|
|
90
90
|
"vitest": "^4.0.10"
|