@amerilux/netsuite-api 0.6.1 → 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
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';
|
|
@@ -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
|
}
|
|
@@ -224,13 +224,31 @@ export function planClientGeneration({ config, fileSystem }) {
|
|
|
224
224
|
}
|
|
225
225
|
const inlinedTypes = [];
|
|
226
226
|
// What the job's own files declare, then whatever they take from a service: both are the run's shapes.
|
|
227
|
+
const inliningProblems = [];
|
|
227
228
|
for (const folderFile of contract.folderFiles) {
|
|
228
229
|
addDeclarations(inlinedTypes, folderFile.filePath, folderFile.declarations);
|
|
229
230
|
for (const typeImport of folderFile.inlinedTypeImports)
|
|
230
|
-
addInlinedTypes(inlinedTypes, typeImport, folderFile.filePath);
|
|
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
|
}
|
|
@@ -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",
|