@amerilux/netsuite-api 0.6.0 → 0.6.2
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/README.md +3 -1
- package/dist-tooling/controllerReader.d.ts +2 -0
- package/dist-tooling/emit.d.ts +10 -0
- package/dist-tooling/emit.js +6 -2
- package/dist-tooling/generate.js +27 -9
- package/dist-tooling/jobReader.d.ts +6 -5
- package/dist-tooling/jobReader.js +46 -26
- package/dist-tooling/typesFileReader.js +3 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -232,6 +232,8 @@ export const { getInputData, map, summarize } = defineJob({
|
|
|
232
232
|
|
|
233
233
|
Each stage is then a file named after the stage NetSuite calls, holding the work and the shapes on its own boundary — open map.ts to see what the map stage does:
|
|
234
234
|
|
|
235
|
+
Only the result has to be a shape the client can carry, because it is the only one a browser is handed. The items a run is planned into are the stage's own working types and stay on the server, so they may be built on whatever the server has — a carrier's API request, a customer's configuration — and the generator does not ask them to be carryable.
|
|
236
|
+
|
|
235
237
|
```ts
|
|
236
238
|
// api/src/jobs/closeStaleOrders/getInputData.ts
|
|
237
239
|
import { listStaleOrders, type StaleOrder } from '../../services/staleOrderService';
|
|
@@ -242,7 +244,7 @@ export interface CloseStaleRequest { olderThanDays: number }
|
|
|
242
244
|
export const getInputDataFunction = (input: CloseStaleRequest): StaleOrder[] => listStaleOrders(input.olderThanDays);
|
|
243
245
|
```
|
|
244
246
|
|
|
245
|
-
A stage file exports the stage's name plus `Function`, because the definition file exports the plain names NetSuite looks for and the two would collide. A stage may also be written inline in the definition file, which suits a job whose stages are a line each — the cleanup job `npm run add:jobs` writes is one.
|
|
247
|
+
A stage file exports the stage's name plus `Function`, because the definition file exports the plain names NetSuite looks for and the two would collide. A stage file may name a shape another file of the job's folder declares — the value a map stage writes is declared in map.ts and named again in summarize.ts — and the generator reads it there. A stage may also be written inline in the definition file, which suits a job whose stages are a line each — the cleanup job `npm run add:jobs` writes is one.
|
|
246
248
|
|
|
247
249
|
The first parameter of `getInputData` is the run's input and the return type of `summarize` is its result: the generator reads the shapes from those two annotations wherever the stage is written, the way it reads an endpoint's request and response, and copies the shapes the result names into the browser's module whether the stage file declares them or takes them from a service. The values carried between stages are JSON, so each stage annotates what it expects (`values: number[]` on a reduce stage, `summary: JobSummary<Total>` on summarize) and the wrapper hands them back that way. Export the stages the job has, and `summarize` always: the run is closed there. A stage exported without being declared throws when NetSuite calls it, rather than quietly passing values through.
|
|
248
250
|
|
|
@@ -10,6 +10,8 @@ import ts from 'typescript';
|
|
|
10
10
|
export interface ControllerProblem {
|
|
11
11
|
filePath: string;
|
|
12
12
|
message: string;
|
|
13
|
+
/** The type the problem is about, when it is about one: what lets a job drop a problem its result never reaches. */
|
|
14
|
+
about?: string;
|
|
13
15
|
}
|
|
14
16
|
export interface EndpointSignature {
|
|
15
17
|
name: string;
|
package/dist-tooling/emit.d.ts
CHANGED
|
@@ -75,6 +75,16 @@ export declare function emitClientIndexModule(controllers: EmittedController[],
|
|
|
75
75
|
export declare function sortJobs(jobs: EmittedJob[]): EmittedJob[];
|
|
76
76
|
/** A job's module: the shapes a run is started with and ends in, and the types they are built from. */
|
|
77
77
|
export declare function emitJobModule({ contract, sourceLabel, inlinedTypes }: EmittedJob): string;
|
|
78
|
+
/**
|
|
79
|
+
* The declared shapes a type names, and the shapes those name in turn: what a module has to carry for the
|
|
80
|
+
* type to stand on its own. A name with no declaration here belongs to the package or to the language, and
|
|
81
|
+
* the type import that brought it is what carries it.
|
|
82
|
+
*
|
|
83
|
+
* Every name the walk reaches is in the answer, including one nothing declares: a caller asking whether a
|
|
84
|
+
* type matters to the wire needs the names that could not be resolved as much as the ones that could. What
|
|
85
|
+
* is emitted is filtered against declarations, so the unresolved names add nothing to a module.
|
|
86
|
+
*/
|
|
87
|
+
export declare function findTypesReachedBy(type: string, ownDeclarations: TypeDeclaration[], inlinedTypes: InlinedTypeSection[]): Set<string>;
|
|
78
88
|
/** The jobs index: every job module under the job's name, reached as `jobs.<name>` from the client index. */
|
|
79
89
|
export declare function emitJobsIndexModule(jobs: EmittedJob[], options: {
|
|
80
90
|
jobsLabel: string;
|
package/dist-tooling/emit.js
CHANGED
|
@@ -146,8 +146,12 @@ export function emitJobModule({ contract, sourceLabel, inlinedTypes }) {
|
|
|
146
146
|
* The declared shapes a type names, and the shapes those name in turn: what a module has to carry for the
|
|
147
147
|
* type to stand on its own. A name with no declaration here belongs to the package or to the language, and
|
|
148
148
|
* the type import that brought it is what carries it.
|
|
149
|
+
*
|
|
150
|
+
* Every name the walk reaches is in the answer, including one nothing declares: a caller asking whether a
|
|
151
|
+
* type matters to the wire needs the names that could not be resolved as much as the ones that could. What
|
|
152
|
+
* is emitted is filtered against declarations, so the unresolved names add nothing to a module.
|
|
149
153
|
*/
|
|
150
|
-
function findTypesReachedBy(type, ownDeclarations, inlinedTypes) {
|
|
154
|
+
export function findTypesReachedBy(type, ownDeclarations, inlinedTypes) {
|
|
151
155
|
const declarationsByName = new Map();
|
|
152
156
|
for (const section of inlinedTypes)
|
|
153
157
|
for (const declaration of section.declarations)
|
|
@@ -160,10 +164,10 @@ function findTypesReachedBy(type, ownDeclarations, inlinedTypes) {
|
|
|
160
164
|
const name = pending.shift();
|
|
161
165
|
if (carried.has(name))
|
|
162
166
|
continue;
|
|
167
|
+
carried.add(name);
|
|
163
168
|
const text = declarationsByName.get(name);
|
|
164
169
|
if (text === undefined)
|
|
165
170
|
continue;
|
|
166
|
-
carried.add(name);
|
|
167
171
|
pending.push(...readReferencedNames(text, 'declaration'));
|
|
168
172
|
}
|
|
169
173
|
return carried;
|
package/dist-tooling/generate.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as nodePath from 'node:path';
|
|
2
2
|
import { resolveInlineTypesFile, resolveJobRunFieldIds } from './config.js';
|
|
3
3
|
import { isControllerFileName, readControllerContract } from './controllerReader.js';
|
|
4
|
-
import { CLIENT_INDEX_FILE_NAME, JOBS_INDEX_FILE_NAME, controllerModuleFileName, emitClientIndexModule, emitControllerModule, emitJobModule, emitJobsIndexModule, emitScriptsModule, jobModuleFileName, sortControllers, sortJobs, } from './emit.js';
|
|
4
|
+
import { CLIENT_INDEX_FILE_NAME, JOBS_INDEX_FILE_NAME, controllerModuleFileName, emitClientIndexModule, emitControllerModule, emitJobModule, emitJobsIndexModule, findTypesReachedBy, emitScriptsModule, jobModuleFileName, sortControllers, sortJobs, } from './emit.js';
|
|
5
5
|
import { jobDefinitionFileName, readJobContract, readJobFolderName } from './jobReader.js';
|
|
6
6
|
import { toPosixPath } from './file-system.js';
|
|
7
7
|
import { readInlinableTypesFile, selectInlinedTypes } from './typesFileReader.js';
|
|
@@ -175,12 +175,12 @@ export function planClientGeneration({ config, fileSystem }) {
|
|
|
175
175
|
const jobsDirectory = resolve(config.jobs);
|
|
176
176
|
const emittedJobs = [];
|
|
177
177
|
/** Copies what one type import names into the job's sections, following imports between the files it reaches. */
|
|
178
|
-
function addInlinedTypes(sections, typeImport, fromLabel) {
|
|
178
|
+
function addInlinedTypes(sections, typeImport, fromLabel, sink) {
|
|
179
179
|
const file = readInlinable(typeImport.specifier);
|
|
180
180
|
if (!file || file === 'missing')
|
|
181
181
|
return;
|
|
182
182
|
const selected = selectInlinedTypes(file, typeImport.names, fromLabel, readInlinable);
|
|
183
|
-
|
|
183
|
+
sink.push(...selected.problems);
|
|
184
184
|
for (const selectedSection of selected.sections)
|
|
185
185
|
addDeclarations(sections, selectedSection.filePath, selectedSection.declarations);
|
|
186
186
|
}
|
|
@@ -223,14 +223,32 @@ export function planClientGeneration({ config, fileSystem }) {
|
|
|
223
223
|
problems.push({ filePath: jobLabel, message: `a controller is named '${contract.name}' too; their generated modules would be the same file.` });
|
|
224
224
|
}
|
|
225
225
|
const inlinedTypes = [];
|
|
226
|
-
//
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
226
|
+
// What the job's own files declare, then whatever they take from a service: both are the run's shapes.
|
|
227
|
+
const inliningProblems = [];
|
|
228
|
+
for (const folderFile of contract.folderFiles) {
|
|
229
|
+
addDeclarations(inlinedTypes, folderFile.filePath, folderFile.declarations);
|
|
230
|
+
for (const typeImport of folderFile.inlinedTypeImports)
|
|
231
|
+
addInlinedTypes(inlinedTypes, typeImport, folderFile.filePath, inliningProblems);
|
|
231
232
|
}
|
|
232
233
|
for (const typeImport of contract.inlinedTypeImports)
|
|
233
|
-
addInlinedTypes(inlinedTypes, typeImport, jobLabel);
|
|
234
|
+
addInlinedTypes(inlinedTypes, typeImport, jobLabel, inliningProblems);
|
|
235
|
+
// The result is the only shape of a run the client is given, so it is the only one that has to be
|
|
236
|
+
// carryable. A stage's working types stay on the server: the items a run is planned into may be built
|
|
237
|
+
// on a carrier's API request or a customer's configuration, and none of that is the client's business.
|
|
238
|
+
const carriedByResult = contract.resultType === undefined
|
|
239
|
+
? new Set()
|
|
240
|
+
: findTypesReachedBy(contract.resultType, contract.typeDeclarations, inlinedTypes);
|
|
241
|
+
// One mistake, reported once: the message names the file the shape lives in, so which stage file
|
|
242
|
+
// imported it adds nothing, and a shape three stages name would otherwise be reported three times.
|
|
243
|
+
const reportedMessages = new Set();
|
|
244
|
+
for (const problem of inliningProblems) {
|
|
245
|
+
if (problem.about !== undefined && !carriedByResult.has(problem.about))
|
|
246
|
+
continue;
|
|
247
|
+
if (reportedMessages.has(problem.message))
|
|
248
|
+
continue;
|
|
249
|
+
reportedMessages.add(problem.message);
|
|
250
|
+
problems.push(problem);
|
|
251
|
+
}
|
|
234
252
|
problems.push(...findDatesInJobInput(contract, inlinedTypes, jobLabel));
|
|
235
253
|
emittedJobs.push({ contract, sourceLabel: jobLabel, inlinedTypes });
|
|
236
254
|
}
|
|
@@ -32,10 +32,11 @@ export interface DeclaredJob {
|
|
|
32
32
|
parameters: JobParameterContract[];
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
|
-
* A file
|
|
36
|
-
* copied into the browser's module like a service's, because a run's shapes are written where
|
|
35
|
+
* A file of the job's own folder the reader had to read: a stage, or a file a stage takes a shape from. What
|
|
36
|
+
* it declares is copied into the browser's module like a service's, because a run's shapes are written where
|
|
37
|
+
* its stages are — the value a map stage writes is declared in map.ts and named again in summarize.ts.
|
|
37
38
|
*/
|
|
38
|
-
export interface
|
|
39
|
+
export interface JobFolderFile {
|
|
39
40
|
filePath: string;
|
|
40
41
|
declarations: TypeDeclaration[];
|
|
41
42
|
inlinedTypeImports: InlinedTypeImport[];
|
|
@@ -68,8 +69,8 @@ export interface JobContract {
|
|
|
68
69
|
stages: JobStageName[];
|
|
69
70
|
/** The stages the file exports as NetSuite entry points. */
|
|
70
71
|
exportedStages: string[];
|
|
71
|
-
/** The files the
|
|
72
|
-
|
|
72
|
+
/** The files of the job's folder that were read, in that order: its stages, and whatever they name. */
|
|
73
|
+
folderFiles: JobFolderFile[];
|
|
73
74
|
}
|
|
74
75
|
export interface JobReadResult {
|
|
75
76
|
contract?: JobContract;
|
|
@@ -133,7 +133,7 @@ function readJobDeclaration(declaration, jobName, filePath) {
|
|
|
133
133
|
* The exported type declarations of a file and the type imports it carries, for a job's definition file
|
|
134
134
|
* or for one of its stage files: both end up in the browser's module, so both are read the same way.
|
|
135
135
|
*/
|
|
136
|
-
function readFileTypes(sourceFile, filePath, options) {
|
|
136
|
+
function readFileTypes(sourceFile, filePath, options, folderFiles) {
|
|
137
137
|
const declarations = [];
|
|
138
138
|
const carriedTypeImports = [];
|
|
139
139
|
const inlinedTypeImports = [];
|
|
@@ -141,7 +141,6 @@ function readFileTypes(sourceFile, filePath, options) {
|
|
|
141
141
|
for (const statement of sourceFile.statements) {
|
|
142
142
|
if (ts.isImportDeclaration(statement)) {
|
|
143
143
|
const result = readTypeImport(statement, filePath, options);
|
|
144
|
-
problems.push(...result.problems);
|
|
145
144
|
if (result.read?.kind === 'carried')
|
|
146
145
|
carriedTypeImports.push(result.read.typeImport);
|
|
147
146
|
else if (result.read?.kind === 'inlined')
|
|
@@ -149,6 +148,14 @@ function readFileTypes(sourceFile, filePath, options) {
|
|
|
149
148
|
else if (result.read?.kind === 'controller') {
|
|
150
149
|
problems.push({ filePath, message: "a job does not take types from a controller; its input and result are its own, or a service's." });
|
|
151
150
|
}
|
|
151
|
+
else if (result.problems.length > 0 && ts.isStringLiteral(statement.moduleSpecifier) && isRelativeSpecifier(statement.moduleSpecifier.text)) {
|
|
152
|
+
// A shape another file of this job's folder declares: read there instead of reported as unreachable.
|
|
153
|
+
const sibling = folderFiles?.read(statement.moduleSpecifier.text);
|
|
154
|
+
if (!sibling)
|
|
155
|
+
problems.push(...result.problems);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
problems.push(...result.problems);
|
|
152
159
|
continue;
|
|
153
160
|
}
|
|
154
161
|
if (!ts.isInterfaceDeclaration(statement) && !ts.isTypeAliasDeclaration(statement) && !ts.isEnumDeclaration(statement))
|
|
@@ -166,6 +173,35 @@ function readFileTypes(sourceFile, filePath, options) {
|
|
|
166
173
|
}
|
|
167
174
|
return { declarations, carriedTypeImports, inlinedTypeImports, problems };
|
|
168
175
|
}
|
|
176
|
+
function isRelativeSpecifier(specifier) {
|
|
177
|
+
return specifier.startsWith('./') || specifier.startsWith('../');
|
|
178
|
+
}
|
|
179
|
+
function createJobFolderFiles(options) {
|
|
180
|
+
const collected = [];
|
|
181
|
+
const problems = [];
|
|
182
|
+
const bySpecifier = new Map();
|
|
183
|
+
const folderFiles = {
|
|
184
|
+
collected,
|
|
185
|
+
problems,
|
|
186
|
+
read(specifier) {
|
|
187
|
+
if (bySpecifier.has(specifier))
|
|
188
|
+
return bySpecifier.get(specifier);
|
|
189
|
+
const read = options.readStageFile?.(specifier);
|
|
190
|
+
if (!read) {
|
|
191
|
+
bySpecifier.set(specifier, undefined);
|
|
192
|
+
return undefined;
|
|
193
|
+
}
|
|
194
|
+
const sourceFile = ts.createSourceFile(read.filePath, read.source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
195
|
+
const entry = { sourceFile, filePath: read.filePath };
|
|
196
|
+
bySpecifier.set(specifier, entry);
|
|
197
|
+
const types = readFileTypes(sourceFile, read.filePath, options, folderFiles);
|
|
198
|
+
problems.push(...types.problems);
|
|
199
|
+
collected.push({ filePath: read.filePath, declarations: types.declarations, inlinedTypeImports: types.inlinedTypeImports, carriedTypeImports: types.carriedTypeImports });
|
|
200
|
+
return entry;
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
return folderFiles;
|
|
204
|
+
}
|
|
169
205
|
/** `import { mapFunction } from './map'`: every value imported by name, by the name this file calls it. */
|
|
170
206
|
function readValueImports(sourceFile) {
|
|
171
207
|
const valueImports = new Map();
|
|
@@ -200,30 +236,12 @@ function findExportedFunction(sourceFile, name) {
|
|
|
200
236
|
}
|
|
201
237
|
return undefined;
|
|
202
238
|
}
|
|
203
|
-
function readStages(literal, filePath, sourceFile,
|
|
239
|
+
function readStages(literal, filePath, sourceFile, folderFiles) {
|
|
204
240
|
const problems = [];
|
|
205
241
|
const stages = [];
|
|
206
|
-
const stageFiles = [];
|
|
207
242
|
const valueImports = readValueImports(sourceFile);
|
|
208
|
-
const filesBySpecifier = new Map();
|
|
209
243
|
let inputType;
|
|
210
244
|
let resultType;
|
|
211
|
-
/** A stage file, read once however many stages come from it, and its shapes collected as it is read. */
|
|
212
|
-
function readStageFile(specifier) {
|
|
213
|
-
const alreadyRead = filesBySpecifier.get(specifier);
|
|
214
|
-
if (alreadyRead)
|
|
215
|
-
return alreadyRead;
|
|
216
|
-
const read = options.readStageFile?.(specifier);
|
|
217
|
-
if (!read)
|
|
218
|
-
return undefined;
|
|
219
|
-
const stageSourceFile = ts.createSourceFile(read.filePath, read.source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS);
|
|
220
|
-
const types = readFileTypes(stageSourceFile, read.filePath, options);
|
|
221
|
-
problems.push(...types.problems);
|
|
222
|
-
stageFiles.push({ filePath: read.filePath, declarations: types.declarations, inlinedTypeImports: types.inlinedTypeImports, carriedTypeImports: types.carriedTypeImports });
|
|
223
|
-
const entry = { sourceFile: stageSourceFile, filePath: read.filePath };
|
|
224
|
-
filesBySpecifier.set(specifier, entry);
|
|
225
|
-
return entry;
|
|
226
|
-
}
|
|
227
245
|
/** The function a stage names, in the file it is imported from: `getInputData: getInputDataFunction`. */
|
|
228
246
|
function findReferencedStage(stageName, referenced) {
|
|
229
247
|
const imported = valueImports.get(referenced);
|
|
@@ -235,7 +253,7 @@ function readStages(literal, filePath, sourceFile, options) {
|
|
|
235
253
|
problems.push({ filePath, message: `stage '${stageName}' comes from '${imported.specifier}'; a stage is imported from a file beside the job, such as './${stageName}'.` });
|
|
236
254
|
return undefined;
|
|
237
255
|
}
|
|
238
|
-
const stageFile =
|
|
256
|
+
const stageFile = folderFiles.read(imported.specifier);
|
|
239
257
|
if (!stageFile) {
|
|
240
258
|
problems.push({ filePath, message: `stage '${stageName}' is imported from '${imported.specifier}', which is not a file beside the job the generator can read.` });
|
|
241
259
|
return undefined;
|
|
@@ -295,7 +313,7 @@ function readStages(literal, filePath, sourceFile, options) {
|
|
|
295
313
|
if (!stages.includes('map') && !stages.includes('reduce')) {
|
|
296
314
|
problems.push({ filePath, message: 'a job declares a map stage, a reduce stage, or both; NetSuite has nothing to run otherwise.' });
|
|
297
315
|
}
|
|
298
|
-
return { stages, inputType, resultType,
|
|
316
|
+
return { stages, inputType, resultType, problems };
|
|
299
317
|
}
|
|
300
318
|
export function readJobContract(filePath, source, options) {
|
|
301
319
|
const problems = [];
|
|
@@ -314,7 +332,8 @@ export function readJobContract(filePath, source, options) {
|
|
|
314
332
|
if (header !== JOB_SCRIPT_TYPE_HEADER) {
|
|
315
333
|
problems.push({ filePath, message: `the leading JSDoc says '@NScriptType ${header ?? '(none)'}'; a job is a ${JOB_SCRIPT_TYPE_HEADER}.` });
|
|
316
334
|
}
|
|
317
|
-
const
|
|
335
|
+
const folderFiles = createJobFolderFiles(options);
|
|
336
|
+
const fileTypes = readFileTypes(sourceFile, filePath, options, folderFiles);
|
|
318
337
|
problems.push(...fileTypes.problems);
|
|
319
338
|
const { carriedTypeImports, inlinedTypeImports, declarations: typeDeclarations } = fileTypes;
|
|
320
339
|
let contract;
|
|
@@ -342,7 +361,7 @@ export function readJobContract(filePath, source, options) {
|
|
|
342
361
|
}
|
|
343
362
|
const declared = readJobDeclaration(declarationArgument, name, filePath);
|
|
344
363
|
problems.push(...declared.problems);
|
|
345
|
-
const stages = readStages(stagesArgument, filePath, sourceFile,
|
|
364
|
+
const stages = readStages(stagesArgument, filePath, sourceFile, folderFiles);
|
|
346
365
|
problems.push(...stages.problems);
|
|
347
366
|
for (const exported of found.exportedStages) {
|
|
348
367
|
if (!JOB_STAGE_NAMES.includes(exported)) {
|
|
@@ -371,9 +390,10 @@ export function readJobContract(filePath, source, options) {
|
|
|
371
390
|
resultType: stages.resultType,
|
|
372
391
|
stages: stages.stages,
|
|
373
392
|
exportedStages: found.exportedStages,
|
|
374
|
-
|
|
393
|
+
folderFiles: folderFiles.collected,
|
|
375
394
|
};
|
|
376
395
|
}
|
|
396
|
+
problems.push(...folderFiles.problems);
|
|
377
397
|
if (!contract) {
|
|
378
398
|
if (problems.length === 0) {
|
|
379
399
|
problems.push({ filePath, message: 'must declare its script: `export const { getInputData, map, summarize } = defineJob({ name, scriptId, deployments, runParameter, runs }, { ... })`.' });
|
|
@@ -92,6 +92,7 @@ export function selectInlinedTypes(file, names, controllerPath, resolveImport =
|
|
|
92
92
|
else if (imported) {
|
|
93
93
|
problems.push({
|
|
94
94
|
filePath: controllerPath,
|
|
95
|
+
about: dependent ?? name,
|
|
95
96
|
message: dependent
|
|
96
97
|
? `type '${dependent}' (${current.filePath}) is built on ${name}, imported from '${importedFrom}' as a rename of ${exportedName}; import it under its own name so the client can carry it.`
|
|
97
98
|
: `type '${name}' is imported into ${current.filePath} from '${importedFrom}' as a rename of ${exportedName}; import it under its own name so the client can carry it.`,
|
|
@@ -100,6 +101,7 @@ export function selectInlinedTypes(file, names, controllerPath, resolveImport =
|
|
|
100
101
|
else {
|
|
101
102
|
problems.push({
|
|
102
103
|
filePath: controllerPath,
|
|
104
|
+
about: dependent ?? name,
|
|
103
105
|
message: dependent
|
|
104
106
|
? `type '${dependent}' (${current.filePath}) is built on ${name} from '${importedFrom}', which the client cannot carry; write the wire shape out in the controller instead.`
|
|
105
107
|
: `type '${name}' is imported into ${current.filePath} from '${importedFrom}', not declared there; the client cannot carry it.`,
|
|
@@ -107,7 +109,7 @@ export function selectInlinedTypes(file, names, controllerPath, resolveImport =
|
|
|
107
109
|
}
|
|
108
110
|
}
|
|
109
111
|
else if (dependent === undefined) {
|
|
110
|
-
problems.push({ filePath: controllerPath, message: `type '${name}' is not declared in ${current.filePath}.` });
|
|
112
|
+
problems.push({ filePath: controllerPath, about: name, message: `type '${name}' is not declared in ${current.filePath}.` });
|
|
111
113
|
}
|
|
112
114
|
// Anything else a declaration refers to is a global (Date, Record, Array): nothing to copy.
|
|
113
115
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amerilux/netsuite-api",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "The API layer for a NetSuite single-page app: endpoints served by a Restlet or a Suitelet, a typed browser client, SuiteScript module stubs for tests, and a generator that writes the client module from the controllers.",
|
|
6
6
|
"license": "MIT",
|