@dusted/anqst 0.1.1 → 0.1.3

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.
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.ANQST_LAYOUT_VERSION = exports.ANQST_GENERATED_DIRNAME = exports.ANQST_ROOT_DIRNAME = void 0;
7
+ exports.normalizeSlashes = normalizeSlashes;
8
+ exports.anqstRootDir = anqstRootDir;
9
+ exports.anqstGeneratedRootDir = anqstGeneratedRootDir;
10
+ exports.anqstDebugIntermediateRootDir = anqstDebugIntermediateRootDir;
11
+ exports.anqstSpecFileName = anqstSpecFileName;
12
+ exports.anqstSettingsFileName = anqstSettingsFileName;
13
+ exports.anqstSettingsRelativePath = anqstSettingsRelativePath;
14
+ exports.generatedFrontendDirName = generatedFrontendDirName;
15
+ exports.generatedNodeExpressDirName = generatedNodeExpressDirName;
16
+ exports.generatedQtWidgetDirName = generatedQtWidgetDirName;
17
+ exports.resolveGeneratedLayoutPaths = resolveGeneratedLayoutPaths;
18
+ exports.toProjectRelative = toProjectRelative;
19
+ const node_path_1 = __importDefault(require("node:path"));
20
+ exports.ANQST_ROOT_DIRNAME = "AnQst";
21
+ exports.ANQST_GENERATED_DIRNAME = "generated";
22
+ exports.ANQST_LAYOUT_VERSION = 2;
23
+ function normalizeSlashes(inputPath) {
24
+ return inputPath.split(node_path_1.default.sep).join("/");
25
+ }
26
+ function anqstRootDir(cwd) {
27
+ return node_path_1.default.join(cwd, exports.ANQST_ROOT_DIRNAME);
28
+ }
29
+ function anqstGeneratedRootDir(cwd) {
30
+ return node_path_1.default.join(anqstRootDir(cwd), exports.ANQST_GENERATED_DIRNAME);
31
+ }
32
+ function anqstDebugIntermediateRootDir(cwd) {
33
+ return node_path_1.default.join(anqstGeneratedRootDir(cwd), "debug", "intermediate");
34
+ }
35
+ function anqstSpecFileName(widgetName) {
36
+ return `${widgetName}.AnQst.d.ts`;
37
+ }
38
+ function anqstSettingsFileName(widgetName) {
39
+ return `${widgetName}.settings.json`;
40
+ }
41
+ function anqstSettingsRelativePath(widgetName) {
42
+ return `./${exports.ANQST_ROOT_DIRNAME}/${anqstSettingsFileName(widgetName)}`;
43
+ }
44
+ function generatedFrontendDirName(widgetName) {
45
+ return `${widgetName}_Angular`;
46
+ }
47
+ function generatedNodeExpressDirName(widgetName) {
48
+ return `${widgetName}_anQst`;
49
+ }
50
+ function generatedQtWidgetDirName(widgetName) {
51
+ return `${widgetName}_widget`;
52
+ }
53
+ function resolveGeneratedLayoutPaths(cwd, widgetName) {
54
+ const generatedRoot = anqstGeneratedRootDir(cwd);
55
+ const cppQtWidgetRoot = node_path_1.default.join(generatedRoot, "backend", "cpp", "qt", generatedQtWidgetDirName(widgetName));
56
+ const designerPluginRoot = node_path_1.default.join(cppQtWidgetRoot, "designerPlugin");
57
+ return {
58
+ generatedRoot,
59
+ frontendRoot: node_path_1.default.join(generatedRoot, "frontend", generatedFrontendDirName(widgetName)),
60
+ nodeExpressRoot: node_path_1.default.join(generatedRoot, "backend", "node", "express", generatedNodeExpressDirName(widgetName)),
61
+ cppCmakeRoot: node_path_1.default.join(generatedRoot, "backend", "cpp", "cmake"),
62
+ cppQtWidgetRoot,
63
+ designerPluginRoot,
64
+ designerPluginBuildRoot: node_path_1.default.join(designerPluginRoot, "build"),
65
+ debugIntermediateRoot: anqstDebugIntermediateRootDir(cwd)
66
+ };
67
+ }
68
+ function toProjectRelative(cwd, absolutePath) {
69
+ return normalizeSlashes(node_path_1.default.relative(cwd, absolutePath));
70
+ }
@@ -8,6 +8,9 @@ const node_fs_1 = __importDefault(require("node:fs"));
8
8
  const node_path_1 = __importDefault(require("node:path"));
9
9
  const typescript_1 = __importDefault(require("typescript"));
10
10
  const errors_1 = require("./errors");
11
+ const program_1 = require("./program");
12
+ const typegraph_1 = require("./typegraph");
13
+ const debug_dump_1 = require("./debug-dump");
11
14
  function locFromNode(source, node) {
12
15
  const lc = source.getLineAndCharacterOfPosition(node.getStart(source));
13
16
  return {
@@ -59,19 +62,116 @@ function parseMemberKindFromAnQstType(typeNode) {
59
62
  const arg = typeNode.typeArguments?.[0];
60
63
  return { kind, payload: arg ? arg.getText() : null };
61
64
  }
62
- function parseServiceMember(source, member) {
65
+ const DEFAULT_MEMBER_TIMEOUT_MS = 120000;
66
+ const MAX_MEMBER_TIMEOUT_MS = 2147483647;
67
+ function parseMemberKindWithConfig(typeNode) {
68
+ if (!typescript_1.default.isTypeReferenceNode(typeNode))
69
+ return null;
70
+ const typeName = qNameToText(typeNode.typeName);
71
+ if (!typeName.startsWith("AnQst."))
72
+ return null;
73
+ const kind = typeName.slice("AnQst.".length);
74
+ if (!["Call", "Slot", "Emitter", "Output", "Input"].includes(kind))
75
+ return null;
76
+ const typeArgs = typeNode.typeArguments ?? [];
77
+ if (kind === "Emitter") {
78
+ return {
79
+ kind,
80
+ payload: null,
81
+ configTypeNode: typeArgs[0] ?? null
82
+ };
83
+ }
84
+ return {
85
+ kind,
86
+ payload: typeArgs[0] ? typeArgs[0].getText() : null,
87
+ configTypeNode: kind === "Call" ? (typeArgs[1] ?? null) : null
88
+ };
89
+ }
90
+ function parseNumericLiteralType(node) {
91
+ if (!typescript_1.default.isLiteralTypeNode(node))
92
+ return null;
93
+ if (typescript_1.default.isNumericLiteral(node.literal)) {
94
+ return Number(node.literal.text);
95
+ }
96
+ if (typescript_1.default.isPrefixUnaryExpression(node.literal)
97
+ && node.literal.operator === typescript_1.default.SyntaxKind.MinusToken
98
+ && typescript_1.default.isNumericLiteral(node.literal.operand)) {
99
+ return -Number(node.literal.operand.text);
100
+ }
101
+ return null;
102
+ }
103
+ function resolveMemberTimeoutMs(source, serviceName, memberName, kind, configTypeNode, warnings, memberLoc) {
104
+ if (kind !== "Call")
105
+ return DEFAULT_MEMBER_TIMEOUT_MS;
106
+ if (!configTypeNode)
107
+ return DEFAULT_MEMBER_TIMEOUT_MS;
108
+ if (!typescript_1.default.isTypeLiteralNode(configTypeNode)) {
109
+ throw new errors_1.VerifyError(`${kind} config for '${memberName}' must be an inline object literal type.`, locFromNode(source, configTypeNode));
110
+ }
111
+ let timeoutSeconds = null;
112
+ let timeoutMilliseconds = null;
113
+ const memberPath = `${serviceName}.${memberName}`;
114
+ for (const prop of configTypeNode.members) {
115
+ if (!typescript_1.default.isPropertySignature(prop) || !prop.name) {
116
+ throw new errors_1.VerifyError(`${kind} config for '${memberName}' only supports named properties.`, locFromNode(source, prop));
117
+ }
118
+ if (!typescript_1.default.isIdentifier(prop.name)) {
119
+ throw new errors_1.VerifyError(`${kind} config for '${memberName}' only supports identifier keys.`, locFromNode(source, prop.name));
120
+ }
121
+ const key = prop.name.text;
122
+ if (!prop.type) {
123
+ throw new errors_1.VerifyError(`${kind} config key '${key}' in '${memberName}' must declare a numeric literal value.`, locFromNode(source, prop));
124
+ }
125
+ if (key !== "timeoutSeconds" && key !== "timeoutMilliseconds") {
126
+ warnings.push({
127
+ severity: "warn",
128
+ message: `Unknown ${kind} config key '${key}' ignored for '${memberPath}'.`,
129
+ loc: locFromNode(source, prop.name),
130
+ memberPath
131
+ });
132
+ continue;
133
+ }
134
+ const numericValue = parseNumericLiteralType(prop.type);
135
+ if (numericValue === null || !Number.isInteger(numericValue)) {
136
+ throw new errors_1.VerifyError(`${kind} config key '${key}' in '${memberName}' must be an integer literal >= 0.`, locFromNode(source, prop.type));
137
+ }
138
+ if (numericValue < 0) {
139
+ throw new errors_1.VerifyError(`${kind} config key '${key}' in '${memberName}' must be >= 0.`, locFromNode(source, prop.type));
140
+ }
141
+ if (key === "timeoutSeconds")
142
+ timeoutSeconds = numericValue;
143
+ if (key === "timeoutMilliseconds")
144
+ timeoutMilliseconds = numericValue;
145
+ }
146
+ if (timeoutSeconds !== null && timeoutMilliseconds !== null) {
147
+ throw new errors_1.VerifyError(`${kind} config for '${memberName}' must specify only one of 'timeoutSeconds' or 'timeoutMilliseconds'.`, memberLoc);
148
+ }
149
+ const effectiveMs = timeoutMilliseconds !== null
150
+ ? timeoutMilliseconds
151
+ : timeoutSeconds !== null
152
+ ? timeoutSeconds * 1000
153
+ : DEFAULT_MEMBER_TIMEOUT_MS;
154
+ if (effectiveMs > MAX_MEMBER_TIMEOUT_MS) {
155
+ throw new errors_1.VerifyError(`${kind} timeout for '${memberName}' exceeds max supported value (${MAX_MEMBER_TIMEOUT_MS} ms).`, memberLoc);
156
+ }
157
+ return effectiveMs;
158
+ }
159
+ function parseServiceMember(source, serviceName, member, warnings) {
63
160
  if (typescript_1.default.isMethodSignature(member)) {
64
161
  if (member.questionToken)
65
162
  throw new errors_1.VerifyError("Optional service methods are not allowed.", locFromNode(source, member));
66
163
  const returnType = member.type;
67
164
  if (!returnType)
68
165
  throw new errors_1.VerifyError("Service method must declare return type.", locFromNode(source, member));
69
- const parsed = parseMemberKindFromAnQstType(returnType);
166
+ const parsed = parseMemberKindWithConfig(returnType);
70
167
  if (!parsed)
71
168
  throw new errors_1.VerifyError(`Unsupported service method return type '${returnType.getText()}'.`, locFromNode(source, member));
72
169
  if (parsed.kind === "Input" || parsed.kind === "Output") {
73
170
  throw new errors_1.VerifyError(`${parsed.kind} must be declared as property, not method.`, locFromNode(source, member));
74
171
  }
172
+ if (parsed.kind === "Emitter" && parsed.configTypeNode !== null) {
173
+ throw new errors_1.VerifyError(`Emitter '${member.name.getText(source)}' does not support config parameters; use plain AnQst.Emitter.`, locFromNode(source, parsed.configTypeNode));
174
+ }
75
175
  if (!member.name || !typescript_1.default.isIdentifier(member.name)) {
76
176
  throw new errors_1.VerifyError("Only identifier service method names are supported.", locFromNode(source, member));
77
177
  }
@@ -84,11 +184,13 @@ function parseServiceMember(source, member) {
84
184
  throw new errors_1.VerifyError("Service parameters must declare type.", locFromNode(source, param));
85
185
  return { name: param.name.text, typeText: param.type.getText() };
86
186
  });
187
+ const timeoutMs = resolveMemberTimeoutMs(source, serviceName, member.name.text, parsed.kind, parsed.configTypeNode, warnings, locFromNode(source, member));
87
188
  return {
88
189
  kind: parsed.kind,
89
190
  name: member.name.text,
90
191
  payloadTypeText: parsed.payload,
91
192
  parameters,
193
+ timeoutMs,
92
194
  loc: locFromNode(source, member)
93
195
  };
94
196
  }
@@ -97,7 +199,7 @@ function parseServiceMember(source, member) {
97
199
  throw new errors_1.VerifyError("Service property must declare type.", locFromNode(source, member));
98
200
  if (member.questionToken)
99
201
  throw new errors_1.VerifyError("Optional service properties are not allowed.", locFromNode(source, member));
100
- const parsed = parseMemberKindFromAnQstType(member.type);
202
+ const parsed = parseMemberKindWithConfig(member.type);
101
203
  if (!parsed)
102
204
  throw new errors_1.VerifyError(`Unsupported service property type '${member.type.getText()}'.`, locFromNode(source, member));
103
205
  if (parsed.kind !== "Input" && parsed.kind !== "Output") {
@@ -106,11 +208,13 @@ function parseServiceMember(source, member) {
106
208
  if (!member.name || !typescript_1.default.isIdentifier(member.name)) {
107
209
  throw new errors_1.VerifyError("Only identifier service property names are supported.", locFromNode(source, member));
108
210
  }
211
+ const timeoutMs = resolveMemberTimeoutMs(source, serviceName, member.name.text, parsed.kind, parsed.configTypeNode, warnings, locFromNode(source, member));
109
212
  return {
110
213
  kind: parsed.kind,
111
214
  name: member.name.text,
112
215
  payloadTypeText: parsed.payload,
113
216
  parameters: [],
217
+ timeoutMs,
114
218
  loc: locFromNode(source, member)
115
219
  };
116
220
  }
@@ -207,7 +311,7 @@ function serviceBaseType(iface) {
207
311
  }
208
312
  return null;
209
313
  }
210
- function parseSpecFile(specFilePath) {
314
+ function parseSpecFileAst(specFilePath) {
211
315
  if (!node_fs_1.default.existsSync(specFilePath))
212
316
  throw new errors_1.VerifyError(`Spec file does not exist: ${specFilePath}`);
213
317
  const text = node_fs_1.default.readFileSync(specFilePath, "utf8");
@@ -225,12 +329,13 @@ function parseSpecFile(specFilePath) {
225
329
  throw new errors_1.VerifyError("Namespace body must be a block.", locFromNode(source, ns));
226
330
  const services = [];
227
331
  const namespaceTypeDecls = [];
332
+ const warnings = [];
228
333
  let supportsDevelopmentModeTransport = false;
229
334
  for (const stmt of ns.body.statements) {
230
335
  if (typescript_1.default.isInterfaceDeclaration(stmt)) {
231
336
  const baseType = serviceBaseType(stmt);
232
337
  if (baseType !== null) {
233
- const members = stmt.members.map((member) => parseServiceMember(source, member));
338
+ const members = stmt.members.map((member) => parseServiceMember(source, stmt.name.text, member, warnings));
234
339
  if (baseType === "AngularHTTPBaseServerClass") {
235
340
  supportsDevelopmentModeTransport = true;
236
341
  }
@@ -254,6 +359,19 @@ function parseSpecFile(specFilePath) {
254
359
  namespaceTypeDecls,
255
360
  importedTypeDecls: importInfo.importedTypeDecls,
256
361
  importedTypeSymbols: importInfo.importedTypeSymbols,
257
- specImports: importInfo.specImports
362
+ specImports: importInfo.specImports,
363
+ warnings
258
364
  };
259
365
  }
366
+ function parseSpecFile(specFilePath) {
367
+ (0, program_1.createTscProgramContext)(specFilePath);
368
+ const parsed = parseSpecFileAst(specFilePath);
369
+ if ((0, debug_dump_1.isDebugEnabled)()) {
370
+ (0, debug_dump_1.writeDebugFile)(process.cwd(), "anqstmodel/parsed-before-typegraph.txt", `${(0, debug_dump_1.inspectText)(parsed)}\n`);
371
+ }
372
+ const normalized = (0, typegraph_1.applyResolvedTypeGraph)(parsed);
373
+ if ((0, debug_dump_1.isDebugEnabled)()) {
374
+ (0, debug_dump_1.writeDebugFile)(process.cwd(), "anqstmodel/parsed-after-typegraph.txt", `${(0, debug_dump_1.inspectText)(normalized)}\n`);
375
+ }
376
+ return normalized;
377
+ }
@@ -9,7 +9,7 @@ exports.getProgramDiagnostics = getProgramDiagnostics;
9
9
  const node_fs_1 = __importDefault(require("node:fs"));
10
10
  const node_path_1 = __importDefault(require("node:path"));
11
11
  const typescript_1 = __importDefault(require("typescript"));
12
- const errors_1 = require("../../errors");
12
+ const errors_1 = require("./errors");
13
13
  const debug_dump_1 = require("./debug-dump");
14
14
  const contextBySpecPath = new Map();
15
15
  function diagnosticText(diagnostic) {