velocious 1.0.456 → 1.0.458
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/build/cli/commands/db/drop.js +7 -3
- package/build/configuration-types.js +18 -2
- package/build/database/datetime-storage.js +188 -0
- package/build/database/drivers/base.js +2 -2
- package/build/database/drivers/mssql/index.js +4 -0
- package/build/database/drivers/mysql/index.js +11 -1
- package/build/database/drivers/pgsql/index.js +15 -3
- package/build/database/migration/index.js +127 -0
- package/build/database/record/index.js +6 -63
- package/build/environment-handlers/node/cli/commands/generate/frontend-models.js +76 -9
- package/build/frontend-model-controller.js +4 -2
- package/build/frontend-models/base.js +44 -15
- package/build/frontend-models/resource-definition.js +105 -14
- package/build/src/cli/commands/db/drop.d.ts.map +1 -1
- package/build/src/cli/commands/db/drop.js +10 -4
- package/build/src/configuration-types.d.ts +53 -6
- package/build/src/configuration-types.d.ts.map +1 -1
- package/build/src/configuration-types.js +17 -3
- package/build/src/database/datetime-storage.d.ts +56 -0
- package/build/src/database/datetime-storage.d.ts.map +1 -0
- package/build/src/database/datetime-storage.js +174 -0
- package/build/src/database/drivers/base.d.ts.map +1 -1
- package/build/src/database/drivers/base.js +3 -3
- package/build/src/database/drivers/mssql/index.d.ts.map +1 -1
- package/build/src/database/drivers/mssql/index.js +4 -1
- package/build/src/database/drivers/mysql/index.d.ts +5 -0
- package/build/src/database/drivers/mysql/index.d.ts.map +1 -1
- package/build/src/database/drivers/mysql/index.js +10 -2
- package/build/src/database/drivers/pgsql/index.d.ts +5 -0
- package/build/src/database/drivers/pgsql/index.d.ts.map +1 -1
- package/build/src/database/drivers/pgsql/index.js +13 -3
- package/build/src/database/migration/index.d.ts +66 -0
- package/build/src/database/migration/index.d.ts.map +1 -1
- package/build/src/database/migration/index.js +112 -1
- package/build/src/database/record/index.d.ts.map +1 -1
- package/build/src/database/record/index.js +7 -52
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.d.ts +35 -0
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.d.ts.map +1 -1
- package/build/src/environment-handlers/node/cli/commands/generate/frontend-models.js +68 -10
- package/build/src/frontend-model-controller.d.ts.map +1 -1
- package/build/src/frontend-model-controller.js +5 -3
- package/build/src/frontend-models/base.d.ts +73 -51
- package/build/src/frontend-models/base.d.ts.map +1 -1
- package/build/src/frontend-models/base.js +43 -16
- package/build/src/frontend-models/resource-definition.d.ts.map +1 -1
- package/build/src/frontend-models/resource-definition.js +92 -15
- package/build/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/cli/commands/db/drop.js +7 -3
- package/src/configuration-types.js +18 -2
- package/src/database/datetime-storage.js +188 -0
- package/src/database/drivers/base.js +2 -2
- package/src/database/drivers/mssql/index.js +4 -0
- package/src/database/drivers/mysql/index.js +11 -1
- package/src/database/drivers/pgsql/index.js +15 -3
- package/src/database/migration/index.js +127 -0
- package/src/database/record/index.js +6 -63
- package/src/environment-handlers/node/cli/commands/generate/frontend-models.js +76 -9
- package/src/frontend-model-controller.js +4 -2
- package/src/frontend-models/base.js +44 -15
- package/src/frontend-models/resource-definition.js +105 -14
|
@@ -288,6 +288,7 @@ export default class DbGenerateFrontendModels extends BaseCommand {
|
|
|
288
288
|
}
|
|
289
289
|
const collectionCommands = modelConfig.collectionCommands
|
|
290
290
|
const memberCommands = modelConfig.memberCommands
|
|
291
|
+
const commandMetadata = modelConfig.commandMetadata || {}
|
|
291
292
|
const builtInCollectionCommandsAreDefault = builtInCollectionCommands.create === "create" && builtInCollectionCommands.index === "index"
|
|
292
293
|
const builtInMemberCommandsAreDefault = builtInMemberCommands.attach === "attach"
|
|
293
294
|
&& builtInMemberCommands.destroy === "destroy"
|
|
@@ -460,35 +461,39 @@ export default class DbGenerateFrontendModels extends BaseCommand {
|
|
|
460
461
|
}
|
|
461
462
|
|
|
462
463
|
for (const methodName of Object.keys(collectionCommands)) {
|
|
464
|
+
const signature = this.customCommandMethodSignature({commandMetadata, methodName})
|
|
465
|
+
|
|
463
466
|
fileContent += "\n"
|
|
464
467
|
fileContent += " /**\n"
|
|
465
468
|
fileContent += ` * Runs ${methodName}.\n`
|
|
466
|
-
fileContent +=
|
|
467
|
-
fileContent +=
|
|
469
|
+
fileContent += signature.paramDocs
|
|
470
|
+
fileContent += ` * @returns {Promise<${signature.returnType}>} - Command response.\n`
|
|
468
471
|
fileContent += " */\n"
|
|
469
|
-
fileContent += ` static async ${methodName}(
|
|
472
|
+
fileContent += ` static async ${methodName}(${signature.parameters}) {\n`
|
|
470
473
|
fileContent += " return await this.executeCustomCommand({\n"
|
|
471
474
|
fileContent += ` commandName: ${JSON.stringify(collectionCommands[methodName])},\n`
|
|
472
475
|
fileContent += ` commandType: ${JSON.stringify(collectionCommands[methodName])},\n`
|
|
473
|
-
fileContent += ` payload: ${className}.normalizeCustomCommandPayloadArguments(
|
|
476
|
+
fileContent += ` payload: ${className}.normalizeCustomCommandPayloadArguments(${signature.payloadArguments}),\n`
|
|
474
477
|
fileContent += " resourcePath: this.resourcePath()\n"
|
|
475
478
|
fileContent += " })\n"
|
|
476
479
|
fileContent += " }\n"
|
|
477
480
|
}
|
|
478
481
|
|
|
479
482
|
for (const methodName of Object.keys(memberCommands)) {
|
|
483
|
+
const signature = this.customCommandMethodSignature({commandMetadata, methodName})
|
|
484
|
+
|
|
480
485
|
fileContent += "\n"
|
|
481
486
|
fileContent += " /**\n"
|
|
482
487
|
fileContent += ` * Runs ${methodName}.\n`
|
|
483
|
-
fileContent +=
|
|
484
|
-
fileContent +=
|
|
488
|
+
fileContent += signature.paramDocs
|
|
489
|
+
fileContent += ` * @returns {Promise<${signature.returnType}>} - Command response.\n`
|
|
485
490
|
fileContent += " */\n"
|
|
486
|
-
fileContent += ` async ${methodName}(
|
|
491
|
+
fileContent += ` async ${methodName}(${signature.parameters}) {\n`
|
|
487
492
|
fileContent += ` return await ${className}.executeCustomCommand({\n`
|
|
488
493
|
fileContent += ` commandName: ${JSON.stringify(memberCommands[methodName])},\n`
|
|
489
494
|
fileContent += ` commandType: ${JSON.stringify(memberCommands[methodName])},\n`
|
|
490
495
|
fileContent += " memberId: this.primaryKeyValue(),\n"
|
|
491
|
-
fileContent += ` payload: ${className}.normalizeCustomCommandPayloadArguments(
|
|
496
|
+
fileContent += ` payload: ${className}.normalizeCustomCommandPayloadArguments(${signature.payloadArguments}),\n`
|
|
492
497
|
fileContent += ` resourcePath: ${className}.resourcePath()\n`
|
|
493
498
|
fileContent += " })\n"
|
|
494
499
|
fileContent += " }\n"
|
|
@@ -1327,7 +1332,69 @@ export default class DbGenerateFrontendModels extends BaseCommand {
|
|
|
1327
1332
|
sourceClassName: ownerClassName
|
|
1328
1333
|
})
|
|
1329
1334
|
|
|
1330
|
-
|
|
1335
|
+
// Frontend attributes hold the serialized (resolved) value, so an async
|
|
1336
|
+
// backend accessor typed `Promise<number>` must surface as `number` — the
|
|
1337
|
+
// same unwrapping the resource-method inference path applies.
|
|
1338
|
+
return jsDocType
|
|
1339
|
+
? {jsDocType: this.frontendResolvableAttributeJsDocType(this.unwrappedPromiseJsDocType({jsDocType}))}
|
|
1340
|
+
: null
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1343
|
+
/**
|
|
1344
|
+
* A backend accessor's `@returns` can reference types that exist only on the
|
|
1345
|
+
* backend (e.g. a model-local `@typedef AgentRunPlanningArtifact`). The frontend
|
|
1346
|
+
* model can't resolve those, so fall back to `any` rather than emitting an
|
|
1347
|
+
* undefined type name. Types built only from primitives and known generic
|
|
1348
|
+
* builtins pass through unchanged.
|
|
1349
|
+
* @param {string} jsDocType - Resolved (Promise-unwrapped) attribute type.
|
|
1350
|
+
* @returns {string} - A frontend-resolvable attribute type.
|
|
1351
|
+
*/
|
|
1352
|
+
frontendResolvableAttributeJsDocType(jsDocType) {
|
|
1353
|
+
const safeTypeIdentifiers = new Set([
|
|
1354
|
+
"Array", "Date", "Exclude", "Extract", "FrontendModelAttributeValue", "FrontendModelTransportValue",
|
|
1355
|
+
"Map", "NonNullable", "Omit", "Partial", "Pick", "Promise", "Readonly", "ReadonlyArray", "Record",
|
|
1356
|
+
"Required", "ReturnType", "Set"
|
|
1357
|
+
])
|
|
1358
|
+
const referencedIdentifiers = jsDocType.match(/[A-Z][A-Za-z0-9_$]*/g) || []
|
|
1359
|
+
|
|
1360
|
+
if (referencedIdentifiers.some((identifier) => !safeTypeIdentifiers.has(identifier))) {
|
|
1361
|
+
return "any"
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
return jsDocType
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
/**
|
|
1368
|
+
* Builds the JSDoc param block, parameter list, payload-argument expression, and
|
|
1369
|
+
* return type for a custom command method. With declared `args` each becomes a
|
|
1370
|
+
* named, typed parameter mapped positionally into the command payload; without
|
|
1371
|
+
* them the method stays variadic.
|
|
1372
|
+
* @param {object} args - Arguments.
|
|
1373
|
+
* @param {Record<string, {args: Array<{name: string, type: string}>, returnType: string | null}>} args.commandMetadata - Per-command metadata.
|
|
1374
|
+
* @param {string} args.methodName - Command method name.
|
|
1375
|
+
* @returns {{paramDocs: string, parameters: string, payloadArguments: string, returnType: string}} - Generation pieces.
|
|
1376
|
+
*/
|
|
1377
|
+
customCommandMethodSignature({commandMetadata, methodName}) {
|
|
1378
|
+
const metadata = commandMetadata[methodName] || {args: [], returnType: null}
|
|
1379
|
+
const returnType = metadata.returnType || "Record<string, ?>"
|
|
1380
|
+
|
|
1381
|
+
if (metadata.args.length > 0) {
|
|
1382
|
+
const parameterNames = metadata.args.map((arg) => arg.name)
|
|
1383
|
+
|
|
1384
|
+
return {
|
|
1385
|
+
paramDocs: metadata.args.map((arg) => ` * @param {${arg.type}} ${arg.name} - Command argument.\n`).join(""),
|
|
1386
|
+
parameters: parameterNames.join(", "),
|
|
1387
|
+
payloadArguments: `[${parameterNames.join(", ")}]`,
|
|
1388
|
+
returnType
|
|
1389
|
+
}
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
return {
|
|
1393
|
+
paramDocs: " * @param {...FrontendModelAttributeValue} commandArguments - Custom command arguments.\n",
|
|
1394
|
+
parameters: "...commandArguments",
|
|
1395
|
+
payloadArguments: "commandArguments",
|
|
1396
|
+
returnType
|
|
1397
|
+
}
|
|
1331
1398
|
}
|
|
1332
1399
|
|
|
1333
1400
|
/**
|
|
@@ -10,7 +10,9 @@ import {normalizeGroup as normalizeQueryGroup, normalizeJoins as normalizeQueryJ
|
|
|
10
10
|
import {assignSafeProperty, deserializeFrontendModelTransportValue, isBackendModelInstance, serializeFrontendModelTransportValue} from "./frontend-models/transport-serialization.js"
|
|
11
11
|
import RoutesResolver from "./routes/resolver.js"
|
|
12
12
|
import {ValidationError} from "./database/record/index.js"
|
|
13
|
+
import { normalizeDateStringForWrite } from "./database/datetime-storage.js"
|
|
13
14
|
import VelociousError from "./velocious-error.js"
|
|
15
|
+
import isDate from "./utils/is-date.js"
|
|
14
16
|
import isPlainObject from "./utils/plain-object.js"
|
|
15
17
|
|
|
16
18
|
/**
|
|
@@ -2212,9 +2214,9 @@ export default class FrontendModelController extends Controller {
|
|
|
2212
2214
|
const isDateTimeColumn = typeof columnType === "string" && ["date", "datetime", "timestamp"].some((type) => columnType.includes(type))
|
|
2213
2215
|
|
|
2214
2216
|
if (isDateTimeColumn) {
|
|
2215
|
-
const parsedDate =
|
|
2217
|
+
const parsedDate = normalizeDateStringForWrite(value)
|
|
2216
2218
|
|
|
2217
|
-
if (
|
|
2219
|
+
if (isDate(parsedDate)) {
|
|
2218
2220
|
return parsedDate
|
|
2219
2221
|
}
|
|
2220
2222
|
}
|
|
@@ -5,6 +5,7 @@ import timeout from "awaitery/build/timeout.js"
|
|
|
5
5
|
import wait from "awaitery/build/wait.js"
|
|
6
6
|
import FrontendModelQuery, {frontendModelEventOptionsPayload} from "./query.js"
|
|
7
7
|
import FrontendModelPreloader from "./preloader.js"
|
|
8
|
+
import { normalizeDateStringForWrite } from "../database/datetime-storage.js"
|
|
8
9
|
import {registerFrontendModel, resolveFrontendModelClass} from "./model-registry.js"
|
|
9
10
|
import {validateFrontendModelResourceCommandName, validateFrontendModelResourcePath} from "./resource-config-validation.js"
|
|
10
11
|
import {deserializeFrontendModelTransportValue, serializeFrontendModelTransportValue} from "./transport-serialization.js"
|
|
@@ -65,9 +66,19 @@ import {readPayloadAssociationCount, readPayloadComputedAbility, readPayloadQuer
|
|
|
65
66
|
*/
|
|
66
67
|
/**
|
|
67
68
|
* Frontend model static side.
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
69
|
+
*
|
|
70
|
+
* The template defaults are intentionally permissive (`any` model/attribute
|
|
71
|
+
* params). The bare `FrontendModelClass` is the `@this`/constraint type on the
|
|
72
|
+
* static query methods (findBy/find/where/preload/...); a generated subclass
|
|
73
|
+
* declares typed-attribute generics (e.g. `FrontendModelBase<AccountAttributes,
|
|
74
|
+
* AccountCreateAttributes, AccountUpdateAttributes>`) which, against a concrete
|
|
75
|
+
* `Record<string, FrontendModelTransportValue>` default, fail the constraint by
|
|
76
|
+
* invariance. Defaulting to `any` lets any subclass satisfy the constraint while
|
|
77
|
+
* the methods' own `@template T` still captures the precise calling class for
|
|
78
|
+
* their return types.
|
|
79
|
+
* @template {FrontendModelBase} [T=FrontendModelBase<any, any, any>]
|
|
80
|
+
* @template {object} [Attributes=any]
|
|
81
|
+
* @template {object} [CreateAttributes=any]
|
|
71
82
|
* @typedef {{new (): T, create(attributes?: CreateAttributes): Promise<T>} & Omit<typeof FrontendModelBase, "create" | "prototype">} FrontendModelClass
|
|
72
83
|
*/
|
|
73
84
|
/**
|
|
@@ -79,7 +90,7 @@ import {readPayloadAssociationCount, readPayloadComputedAbility, readPayloadQuer
|
|
|
79
90
|
* Loaded instance type for relationship helper generics. Older generated
|
|
80
91
|
* frontend models passed model classes into relationship helpers, while newer
|
|
81
92
|
* generated models pass instance types.
|
|
82
|
-
* @template {FrontendModelBase | typeof FrontendModelBase} T
|
|
93
|
+
* @template {FrontendModelBase<any, any, any> | typeof FrontendModelBase} T
|
|
83
94
|
* @typedef {T extends new (...args: any[]) => infer Instance ? Instance : T} FrontendModelRelationshipModel
|
|
84
95
|
*/
|
|
85
96
|
/**
|
|
@@ -280,9 +291,9 @@ export class AttributeNotSelectedError extends Error {
|
|
|
280
291
|
|
|
281
292
|
/**
|
|
282
293
|
* Lightweight singular relationship state holder for frontend model instances.
|
|
283
|
-
* @template {FrontendModelBase | typeof FrontendModelBase} S
|
|
284
|
-
* @template {FrontendModelBase | typeof FrontendModelBase} T
|
|
285
|
-
* @template {
|
|
294
|
+
* @template {FrontendModelBase<any, any, any> | typeof FrontendModelBase} S
|
|
295
|
+
* @template {FrontendModelBase<any, any, any> | typeof FrontendModelBase} T
|
|
296
|
+
* @template {object} [TargetCreateAttributes=Record<string, FrontendModelAttributeValue>]
|
|
286
297
|
*/
|
|
287
298
|
export class FrontendModelSingularRelationship {
|
|
288
299
|
/**
|
|
@@ -400,9 +411,9 @@ export class FrontendModelSingularRelationship {
|
|
|
400
411
|
|
|
401
412
|
/**
|
|
402
413
|
* Lightweight has-many relationship state holder for frontend model instances.
|
|
403
|
-
* @template {FrontendModelBase | typeof FrontendModelBase} S
|
|
404
|
-
* @template {FrontendModelBase | typeof FrontendModelBase} T
|
|
405
|
-
* @template {
|
|
414
|
+
* @template {FrontendModelBase<any, any, any> | typeof FrontendModelBase} S
|
|
415
|
+
* @template {FrontendModelBase<any, any, any> | typeof FrontendModelBase} T
|
|
416
|
+
* @template {object} [TargetCreateAttributes=Record<string, FrontendModelAttributeValue>]
|
|
406
417
|
*/
|
|
407
418
|
export class FrontendModelHasManyRelationship {
|
|
408
419
|
/**
|
|
@@ -539,8 +550,13 @@ export class FrontendModelHasManyRelationship {
|
|
|
539
550
|
}
|
|
540
551
|
|
|
541
552
|
/**
|
|
542
|
-
* Frontend model relationship helper type.
|
|
543
|
-
*
|
|
553
|
+
* Frontend model relationship helper type. Returned by `getRelationshipByName`,
|
|
554
|
+
* which generated models immediately cast to their concrete relationship type
|
|
555
|
+
* (e.g. `FrontendModelSingularRelationship<Owner, Target, TargetCreateAttributes>`).
|
|
556
|
+
* The members use `any` type args so that cast is allowed regardless of the
|
|
557
|
+
* target model's typed-attribute generics — a concrete `FrontendModelBase` member
|
|
558
|
+
* here makes the cast a non-overlapping (TS2352) error for every typed model.
|
|
559
|
+
* @typedef {FrontendModelHasManyRelationship<any, any, any> | FrontendModelSingularRelationship<any, any, any>} FrontendModelRelationship
|
|
544
560
|
*/
|
|
545
561
|
|
|
546
562
|
/**
|
|
@@ -1882,9 +1898,16 @@ function assertDefinedFindByConditionValue(value, keyPath) {
|
|
|
1882
1898
|
|
|
1883
1899
|
/**
|
|
1884
1900
|
* Base frontend model.
|
|
1885
|
-
*
|
|
1886
|
-
*
|
|
1887
|
-
*
|
|
1901
|
+
*
|
|
1902
|
+
* Defaults are `any` so the bare `FrontendModelBase` — used throughout as a
|
|
1903
|
+
* constraint/parameter type for "any frontend model" — accepts generated
|
|
1904
|
+
* subclasses declaring typed-attribute generics (`FrontendModelBase<XAttributes,
|
|
1905
|
+
* ...>`). A concrete `Record<string, FrontendModelAttributeValue>` default makes
|
|
1906
|
+
* those subclasses fail by invariance. Subclasses still pass their precise
|
|
1907
|
+
* attribute typedefs, so typed accessors keep their precision.
|
|
1908
|
+
* @template {object} [Attributes=any]
|
|
1909
|
+
* @template {object} [CreateAttributes=any]
|
|
1910
|
+
* @template {object} [UpdateAttributes=any]
|
|
1888
1911
|
*/
|
|
1889
1912
|
export default class FrontendModelBase {
|
|
1890
1913
|
/**
|
|
@@ -3775,6 +3798,12 @@ export default class FrontendModelBase {
|
|
|
3775
3798
|
*/
|
|
3776
3799
|
static findByPrimitiveValuesMatch(actualValue, expectedValue) {
|
|
3777
3800
|
if (actualValue instanceof Date && typeof expectedValue === "string") {
|
|
3801
|
+
const normalizedExpectedValue = normalizeDateStringForWrite(expectedValue)
|
|
3802
|
+
|
|
3803
|
+
if (normalizedExpectedValue instanceof Date) {
|
|
3804
|
+
return actualValue.toISOString() === normalizedExpectedValue.toISOString()
|
|
3805
|
+
}
|
|
3806
|
+
|
|
3778
3807
|
return actualValue.toISOString() === expectedValue
|
|
3779
3808
|
}
|
|
3780
3809
|
|
|
@@ -90,6 +90,10 @@ function normalizeFrontendModelResourceConfiguration(resourceConfiguration) {
|
|
|
90
90
|
builtInCollectionCommands: normalizedCommands.builtInCollectionCommands,
|
|
91
91
|
builtInMemberCommands: normalizedCommands.builtInMemberCommands,
|
|
92
92
|
collectionCommands: normalizedCommands.collectionCommands,
|
|
93
|
+
// Per-command metadata (typed args + declared return type) keyed by method
|
|
94
|
+
// name, derived from `{name, args?, returnType?}` command entries. The
|
|
95
|
+
// generator uses it to type each custom command method.
|
|
96
|
+
commandMetadata: normalizedCommands.commandMetadata,
|
|
93
97
|
memberCommands: normalizedCommands.memberCommands
|
|
94
98
|
}
|
|
95
99
|
}
|
|
@@ -151,7 +155,7 @@ function defaultCrudAbilities() {
|
|
|
151
155
|
/**
|
|
152
156
|
* Runs normalize frontend model resource commands.
|
|
153
157
|
* @param {import("../configuration-types.js").FrontendModelResourceConfiguration} resourceConfiguration - Raw resource configuration.
|
|
154
|
-
* @returns {{builtInCollectionCommands: Record<string, string>, builtInMemberCommands: Record<string, string>, collectionCommands: Record<string, string>, memberCommands: Record<string, string>}} - Normalized command configuration.
|
|
158
|
+
* @returns {{builtInCollectionCommands: Record<string, string>, builtInMemberCommands: Record<string, string>, collectionCommands: Record<string, string>, commandMetadata: Record<string, {args: Array<{name: string, type: string}>, returnType: string | null}>, memberCommands: Record<string, string>}} - Normalized command configuration.
|
|
155
159
|
*/
|
|
156
160
|
function normalizeFrontendModelResourceCommands(resourceConfiguration) {
|
|
157
161
|
const builtInCollectionCommands = resourceConfiguration.builtInCollectionCommands
|
|
@@ -180,11 +184,15 @@ function normalizeFrontendModelResourceCommands(resourceConfiguration) {
|
|
|
180
184
|
modelName: "MemberCommand"
|
|
181
185
|
})
|
|
182
186
|
|
|
187
|
+
const normalizedCollectionCommands = normalizeFrontendModelCustomCommands({commandsConfig: customCollectionCommands, modelName: "CollectionCommand"})
|
|
188
|
+
const normalizedMemberCommands = normalizeFrontendModelCustomCommands({commandsConfig: customMemberCommands, modelName: "MemberCommand"})
|
|
189
|
+
|
|
183
190
|
return {
|
|
184
191
|
builtInCollectionCommands: normalizedBuiltInCollectionCommands,
|
|
185
192
|
builtInMemberCommands: normalizedBuiltInMemberCommands,
|
|
186
|
-
collectionCommands:
|
|
187
|
-
|
|
193
|
+
collectionCommands: normalizedCollectionCommands.commands,
|
|
194
|
+
commandMetadata: {...normalizedCollectionCommands.metadata, ...normalizedMemberCommands.metadata},
|
|
195
|
+
memberCommands: normalizedMemberCommands.commands
|
|
188
196
|
}
|
|
189
197
|
}
|
|
190
198
|
|
|
@@ -228,27 +236,30 @@ function normalizeFrontendModelBuiltInCommands({commandDefaults, commandsConfig,
|
|
|
228
236
|
}
|
|
229
237
|
|
|
230
238
|
/**
|
|
231
|
-
* Runs normalize frontend model custom commands.
|
|
239
|
+
* Runs normalize frontend model custom commands. Entries are either a plain
|
|
240
|
+
* camelCase method-name string or a `{name, args?, returnType?}` object that
|
|
241
|
+
* also declares the command's typed arguments and/or response type.
|
|
232
242
|
* @param {object} args - Arguments.
|
|
233
|
-
* @param {string
|
|
243
|
+
* @param {Array<string | {name: string, args?: Array<{name: string, type: string}>, returnType?: string}> | undefined} args.commandsConfig - Custom commands config.
|
|
234
244
|
* @param {string} args.modelName - Diagnostic model name.
|
|
235
|
-
* @returns {Record<string, string>} -
|
|
245
|
+
* @returns {{commands: Record<string, string>, metadata: Record<string, {args: Array<{name: string, type: string}>, returnType: string | null}>}} - Route map (method name → kebab slug) + per-command metadata.
|
|
236
246
|
*/
|
|
237
247
|
function normalizeFrontendModelCustomCommands({commandsConfig, modelName}) {
|
|
238
248
|
if (!commandsConfig) {
|
|
239
|
-
return {}
|
|
249
|
+
return {commands: {}, metadata: {}}
|
|
240
250
|
}
|
|
241
251
|
|
|
242
252
|
if (!Array.isArray(commandsConfig)) {
|
|
243
253
|
throw new Error(`${modelName} configuration must use the array form. Object form is no longer supported.`)
|
|
244
254
|
}
|
|
245
255
|
|
|
246
|
-
/**
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
const
|
|
256
|
+
/** @type {Record<string, string>} */
|
|
257
|
+
const commands = {}
|
|
258
|
+
/** @type {Record<string, {args: Array<{name: string, type: string}>, returnType: string | null}>} */
|
|
259
|
+
const metadata = {}
|
|
250
260
|
|
|
251
|
-
for (const
|
|
261
|
+
for (const commandEntry of commandsConfig) {
|
|
262
|
+
const {methodName, args, returnType} = normalizeFrontendModelCustomCommandEntry({commandEntry, modelName})
|
|
252
263
|
const validatedMethodName = validateFrontendModelResourceCommandName({
|
|
253
264
|
commandName: methodName,
|
|
254
265
|
commandType: methodName,
|
|
@@ -256,10 +267,90 @@ function normalizeFrontendModelCustomCommands({commandsConfig, modelName}) {
|
|
|
256
267
|
})
|
|
257
268
|
const commandSlug = inflection.dasherize(inflection.underscore(validatedMethodName))
|
|
258
269
|
|
|
259
|
-
|
|
270
|
+
commands[validatedMethodName] = commandSlug
|
|
271
|
+
metadata[validatedMethodName] = {args, returnType}
|
|
260
272
|
}
|
|
261
273
|
|
|
262
|
-
return
|
|
274
|
+
return {commands, metadata}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Normalizes one custom-command entry (string shorthand or contract object).
|
|
279
|
+
* @param {object} args - Arguments.
|
|
280
|
+
* @param {unknown} args.commandEntry - Raw command entry.
|
|
281
|
+
* @param {string} args.modelName - Diagnostic model name.
|
|
282
|
+
* @returns {{methodName: string, args: Array<{name: string, type: string}>, returnType: string | null}} - Method name + metadata.
|
|
283
|
+
*/
|
|
284
|
+
function normalizeFrontendModelCustomCommandEntry({commandEntry, modelName}) {
|
|
285
|
+
if (typeof commandEntry === "string") {
|
|
286
|
+
return {methodName: commandEntry, args: [], returnType: null}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (!commandEntry || typeof commandEntry !== "object" || Array.isArray(commandEntry)) {
|
|
290
|
+
throw new Error(`${modelName} entries must be a camelCase name string or a {name, args?, returnType?} object`)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const {name, args, returnType, ...rest} = /** @type {{name?: unknown, args?: unknown, returnType?: unknown}} */ (commandEntry)
|
|
294
|
+
|
|
295
|
+
if (Object.keys(rest).length > 0) {
|
|
296
|
+
throw new Error(`Unexpected ${modelName} keys: ${Object.keys(rest).join(", ")}. Allowed: name, args, returnType`)
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (typeof name !== "string" || name.length < 1) {
|
|
300
|
+
throw new Error(`${modelName} object entries require a non-empty 'name' string`)
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
methodName: name,
|
|
305
|
+
args: normalizeFrontendModelCommandArgs({args, commandName: name, modelName}),
|
|
306
|
+
returnType: normalizeFrontendModelCommandReturnType({commandName: name, modelName, returnType})
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Validates and normalizes a custom command's typed-argument list.
|
|
312
|
+
* @param {object} args - Arguments.
|
|
313
|
+
* @param {unknown} args.args - Raw command args.
|
|
314
|
+
* @param {string} args.commandName - Command name for diagnostics.
|
|
315
|
+
* @param {string} args.modelName - Diagnostic model name.
|
|
316
|
+
* @returns {Array<{name: string, type: string}>} - Normalized typed command arguments.
|
|
317
|
+
*/
|
|
318
|
+
function normalizeFrontendModelCommandArgs({args, commandName, modelName}) {
|
|
319
|
+
if (args === undefined || args === null) {
|
|
320
|
+
return []
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
if (!Array.isArray(args)) {
|
|
324
|
+
throw new Error(`${modelName} '${commandName}' args must be an array of {name, type} objects`)
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return args.map((arg) => {
|
|
328
|
+
if (!arg || typeof arg !== "object" || typeof arg.name !== "string" || arg.name.length < 1 || typeof arg.type !== "string" || arg.type.trim().length < 1) {
|
|
329
|
+
throw new Error(`${modelName} '${commandName}' args entries require non-empty 'name' and JSDoc-type 'type' strings`)
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
return {name: arg.name, type: arg.type.trim()}
|
|
333
|
+
})
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Validates and normalizes a custom command's declared JSDoc return type.
|
|
338
|
+
* @param {object} args - Arguments.
|
|
339
|
+
* @param {string} args.commandName - Command name for diagnostics.
|
|
340
|
+
* @param {string} args.modelName - Diagnostic model name.
|
|
341
|
+
* @param {unknown} args.returnType - Raw return type.
|
|
342
|
+
* @returns {string | null} - Normalized JSDoc return type.
|
|
343
|
+
*/
|
|
344
|
+
function normalizeFrontendModelCommandReturnType({commandName, modelName, returnType}) {
|
|
345
|
+
if (returnType === undefined || returnType === null) {
|
|
346
|
+
return null
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (typeof returnType !== "string" || returnType.trim().length < 1) {
|
|
350
|
+
throw new Error(`${modelName} '${commandName}' returnType must be a non-empty JSDoc type string`)
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
return returnType.trim()
|
|
263
354
|
}
|
|
264
355
|
|
|
265
356
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"drop.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/db/drop.js"],"names":[],"mappings":"AAIA;IACE;;;OAGG;IACH,WAFa,OAAO,CAAC,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"drop.d.ts","sourceRoot":"","sources":["../../../../../src/cli/commands/db/drop.js"],"names":[],"mappings":"AAIA;IACE;;;OAGG;IACH,WAFa,OAAO,CAAC,IAAI,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CA8CzC;IAED;;;;OAIG;IACH,yCAHW,MAAM,GACJ,MAAM,CAOlB;IAED;;;;OAIG;IACH,iCAHW,MAAM,GACJ,OAAO,CAAC,IAAI,CAAC,CAOzB;CACF;0BA9EyB,mBAAmB"}
|
|
@@ -26,9 +26,15 @@ export default class DbDrop extends DbBaseCommand {
|
|
|
26
26
|
// driver's system default.
|
|
27
27
|
const configuredFallback = newConfiguration.useDatabase;
|
|
28
28
|
const useConfiguredFallback = typeof configuredFallback == "string" && configuredFallback.length > 0 && configuredFallback != targetDatabaseName;
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
if (useConfiguredFallback) {
|
|
30
|
+
newConfiguration.database = configuredFallback;
|
|
31
|
+
}
|
|
32
|
+
else if (databaseType == "mysql") {
|
|
33
|
+
delete newConfiguration.database;
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
newConfiguration.database = this.systemFallbackDatabaseName(databaseType);
|
|
37
|
+
}
|
|
32
38
|
if (databaseType == "mssql" && newConfiguration.sqlConfig?.database) {
|
|
33
39
|
delete newConfiguration.sqlConfig.database;
|
|
34
40
|
}
|
|
@@ -63,4 +69,4 @@ export default class DbDrop extends DbBaseCommand {
|
|
|
63
69
|
await this.queryOrCollectSqls(sqls, (sql) => ({ databaseName, sql }));
|
|
64
70
|
}
|
|
65
71
|
}
|
|
66
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
72
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZHJvcC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL3NyYy9jbGkvY29tbWFuZHMvZGIvZHJvcC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLGFBQWEsTUFBTSxtQkFBbUIsQ0FBQTtBQUM3QyxPQUFPLEVBQUMsSUFBSSxFQUFDLE1BQU0sV0FBVyxDQUFBO0FBQzlCLE9BQU8sRUFBQyxXQUFXLEVBQUMsTUFBTSxjQUFjLENBQUE7QUFFeEMsTUFBTSxDQUFDLE9BQU8sT0FBTyxNQUFPLFNBQVEsYUFBYTtJQUMvQzs7O09BR0c7SUFDSCxLQUFLLENBQUMsT0FBTztRQUNYLE1BQU0sV0FBVyxHQUFHLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDLGNBQWMsRUFBRSxDQUFBO1FBRTVELElBQUksV0FBVyxJQUFJLGFBQWEsSUFBSSxXQUFXLElBQUksTUFBTSxFQUFFLENBQUM7WUFDMUQsTUFBTSxJQUFJLEtBQUssQ0FBQyxzRkFBc0YsV0FBVyxFQUFFLENBQUMsQ0FBQTtRQUN0SCxDQUFDO1FBRUQsS0FBSyxNQUFNLGtCQUFrQixJQUFJLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDLHNCQUFzQixFQUFFLEVBQUUsQ0FBQztZQUNsRixNQUFNLFlBQVksR0FBRyxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQyxlQUFlLENBQUMsa0JBQWtCLENBQUMsQ0FBQTtZQUVoRixJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTztnQkFBRSxJQUFJLENBQUMsTUFBTSxHQUFHLEVBQUUsQ0FBQTtZQUV2QyxJQUFJLFlBQVksSUFBSSxRQUFRLEVBQUUsQ0FBQztnQkFDN0IsTUFBTSxZQUFZLEdBQUcsSUFBSSxDQUFDLGdCQUFnQixFQUFFLENBQUMsZUFBZSxDQUFDLGtCQUFrQixDQUFDLENBQUE7Z0JBQ2hGLE1BQU0sZ0JBQWdCLEdBQUcsV0FBVyxDQUFDLEVBQUUsRUFBRSxZQUFZLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQyxDQUFBO2dCQUN6RSxNQUFNLGtCQUFrQixHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLEVBQUUsQ0FBQyx3QkFBd0IsRUFBRSxFQUFFLGtCQUFrQixFQUFFLFVBQVUsQ0FBQyxDQUFBO2dCQUVuSCxzRUFBc0U7Z0JBQ3RFLGdFQUFnRTtnQkFDaEUsb0VBQW9FO2dCQUNwRSxvRUFBb0U7Z0JBQ3BFLDJCQUEyQjtnQkFDM0IsTUFBTSxrQkFBa0IsR0FBRyxnQkFBZ0IsQ0FBQyxXQUFXLENBQUE7Z0JBQ3ZELE1BQU0scUJBQXFCLEdBQUcsT0FBTyxrQkFBa0IsSUFBSSxRQUFRLElBQUksa0JBQWtCLENBQUMsTUFBTSxHQUFHLENBQUMsSUFBSSxrQkFBa0IsSUFBSSxrQkFBa0IsQ0FBQTtnQkFFaEosSUFBSSxxQkFBcUIsRUFBRSxDQUFDO29CQUMxQixnQkFBZ0IsQ0FBQyxRQUFRLEdBQUcsa0JBQWtCLENBQUE7Z0JBQ2hELENBQUM7cUJBQU0sSUFBSSxZQUFZLElBQUksT0FBTyxFQUFFLENBQUM7b0JBQ25DLE9BQU8sZ0JBQWdCLENBQUMsUUFBUSxDQUFBO2dCQUNsQyxDQUFDO3FCQUFNLENBQUM7b0JBQ04sZ0JBQWdCLENBQUMsUUFBUSxHQUFHLElBQUksQ0FBQywwQkFBMEIsQ0FBQyxZQUFZLENBQUMsQ0FBQTtnQkFDM0UsQ0FBQztnQkFFRCxJQUFJLFlBQVksSUFBSSxPQUFPLElBQUksZ0JBQWdCLENBQUMsU0FBUyxFQUFFLFFBQVEsRUFBRSxDQUFDO29CQUNwRSxPQUFPLGdCQUFnQixDQUFDLFNBQVMsQ0FBQyxRQUFRLENBQUE7Z0JBQzVDLENBQUM7Z0JBRUQsTUFBTSxJQUFJLENBQUMsNEJBQTRCLENBQUMsZ0JBQWdCLEVBQUUsS0FBSyxJQUFJLEVBQUU7b0JBQ25FLE1BQU0sSUFBSSxDQUFDLFlBQVksQ0FBQyxrQkFBa0IsQ0FBQyxDQUFBO2dCQUM3QyxDQUFDLENBQUMsQ0FBQTtZQUNKLENBQUM7WUFFRCxJQUFJLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTztnQkFBRSxPQUFPLElBQUksQ0FBQyxNQUFNLENBQUE7UUFDM0MsQ0FBQztJQUNILENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsMEJBQTBCLENBQUMsWUFBWTtRQUNyQyxJQUFJLFlBQVksSUFBSSxPQUFPO1lBQUUsT0FBTyxVQUFVLENBQUE7UUFDOUMsSUFBSSxZQUFZLElBQUksT0FBTztZQUFFLE9BQU8sUUFBUSxDQUFBO1FBRTVDLE9BQU8sT0FBTyxDQUFBO0lBQ2hCLENBQUM7SUFFRDs7OztPQUlHO0lBQ0gsS0FBSyxDQUFDLFlBQVksQ0FBQyxrQkFBa0I7UUFDbkMsTUFBTSxZQUFZLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDLHdCQUF3QixFQUFFLEVBQUUsa0JBQWtCLEVBQUUsVUFBVSxDQUFDLENBQUE7UUFDN0csTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLHFCQUFxQixFQUFFLENBQUMsZUFBZSxDQUFDLFlBQVksRUFBRSxFQUFDLFFBQVEsRUFBRSxJQUFJLEVBQUMsQ0FBQyxDQUFBO1FBRXpGLE1BQU0sSUFBSSxDQUFDLGtCQUFrQixDQUFDLElBQUksRUFBRSxDQUFDLEdBQUcsRUFBRSxFQUFFLENBQUMsQ0FBQyxFQUFDLFlBQVksRUFBRSxHQUFHLEVBQUMsQ0FBQyxDQUFDLENBQUE7SUFDckUsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IERiQmFzZUNvbW1hbmQgZnJvbSBcIi4vYmFzZS1jb21tYW5kLmpzXCJcbmltcG9ydCB7ZGlnZ30gZnJvbSBcImRpZ2dlcml6ZVwiXG5pbXBvcnQge2luY29ycG9yYXRlfSBmcm9tIFwiaW5jb3Jwb3JhdG9yXCJcblxuZXhwb3J0IGRlZmF1bHQgY2xhc3MgRGJEcm9wIGV4dGVuZHMgRGJCYXNlQ29tbWFuZCB7XG4gIC8qKlxuICAgKiBSdW5zIGV4ZWN1dGUuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQgfCBBcnJheTxvYmplY3Q+Pn0gLSBSZXNvbHZlcyB3aXRoIFNRTCBzdGF0ZW1lbnRzIHdoZW4gcnVubmluZyBpbiBkcnkgbW9kZS5cbiAgICovXG4gIGFzeW5jIGV4ZWN1dGUoKSB7XG4gICAgY29uc3QgZW52aXJvbm1lbnQgPSB0aGlzLmdldENvbmZpZ3VyYXRpb24oKS5nZXRFbnZpcm9ubWVudCgpXG5cbiAgICBpZiAoZW52aXJvbm1lbnQgIT0gXCJkZXZlbG9wbWVudFwiICYmIGVudmlyb25tZW50ICE9IFwidGVzdFwiKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFRoaXMgY29tbWFuZCBzaG91bGQgb25seSBiZSBleGVjdXRlZCBvbiBkZXZlbG9wbWVudCBhbmQgdGVzdCBlbnZpcm9ubWVudHMgYW5kIG5vdDogJHtlbnZpcm9ubWVudH1gKVxuICAgIH1cblxuICAgIGZvciAoY29uc3QgZGF0YWJhc2VJZGVudGlmaWVyIG9mIHRoaXMuZ2V0Q29uZmlndXJhdGlvbigpLmdldERhdGFiYXNlSWRlbnRpZmllcnMoKSkge1xuICAgICAgY29uc3QgZGF0YWJhc2VUeXBlID0gdGhpcy5nZXRDb25maWd1cmF0aW9uKCkuZ2V0RGF0YWJhc2VUeXBlKGRhdGFiYXNlSWRlbnRpZmllcilcblxuICAgICAgaWYgKHRoaXMuYXJncy50ZXN0aW5nKSB0aGlzLnJlc3VsdCA9IFtdXG5cbiAgICAgIGlmIChkYXRhYmFzZVR5cGUgIT0gXCJzcWxpdGVcIikge1xuICAgICAgICBjb25zdCBkYXRhYmFzZVBvb2wgPSB0aGlzLmdldENvbmZpZ3VyYXRpb24oKS5nZXREYXRhYmFzZVBvb2woZGF0YWJhc2VJZGVudGlmaWVyKVxuICAgICAgICBjb25zdCBuZXdDb25maWd1cmF0aW9uID0gaW5jb3Jwb3JhdGUoe30sIGRhdGFiYXNlUG9vbC5nZXRDb25maWd1cmF0aW9uKCkpXG4gICAgICAgIGNvbnN0IHRhcmdldERhdGFiYXNlTmFtZSA9IGRpZ2codGhpcy5nZXRDb25maWd1cmF0aW9uKCkuZ2V0RGF0YWJhc2VDb25maWd1cmF0aW9uKCksIGRhdGFiYXNlSWRlbnRpZmllciwgXCJkYXRhYmFzZVwiKVxuXG4gICAgICAgIC8vIENvbm5lY3QgdG8gYSBrbm93bi1leGlzdGluZyBzeXN0ZW0gZGF0YWJhc2U6IHRoZSB0YXJnZXQgaXMgYWJvdXQgdG9cbiAgICAgICAgLy8gYmUgZHJvcHBlZCAoc28gd2UgY2FuJ3QgYmUgY29ubmVjdGVkIHRvIGl0KSwgUG9zdGdyZXMgcmVqZWN0c1xuICAgICAgICAvLyBEUk9QIERBVEFCQVNFIHdoaWxlIGNvbm5lY3RlZCB0byBpdCwgYW5kIGNvbmZpZ3VyZWQgYHVzZURhdGFiYXNlYFxuICAgICAgICAvLyBtYXkgaGFwcGVuIHRvIGVxdWFsIHRoZSB0YXJnZXQg4oCUIGluIHRoYXQgY2FzZSBmYWxsIHRocm91Z2ggdG8gdGhlXG4gICAgICAgIC8vIGRyaXZlcidzIHN5c3RlbSBkZWZhdWx0LlxuICAgICAgICBjb25zdCBjb25maWd1cmVkRmFsbGJhY2sgPSBuZXdDb25maWd1cmF0aW9uLnVzZURhdGFiYXNlXG4gICAgICAgIGNvbnN0IHVzZUNvbmZpZ3VyZWRGYWxsYmFjayA9IHR5cGVvZiBjb25maWd1cmVkRmFsbGJhY2sgPT0gXCJzdHJpbmdcIiAmJiBjb25maWd1cmVkRmFsbGJhY2subGVuZ3RoID4gMCAmJiBjb25maWd1cmVkRmFsbGJhY2sgIT0gdGFyZ2V0RGF0YWJhc2VOYW1lXG5cbiAgICAgICAgaWYgKHVzZUNvbmZpZ3VyZWRGYWxsYmFjaykge1xuICAgICAgICAgIG5ld0NvbmZpZ3VyYXRpb24uZGF0YWJhc2UgPSBjb25maWd1cmVkRmFsbGJhY2tcbiAgICAgICAgfSBlbHNlIGlmIChkYXRhYmFzZVR5cGUgPT0gXCJteXNxbFwiKSB7XG4gICAgICAgICAgZGVsZXRlIG5ld0NvbmZpZ3VyYXRpb24uZGF0YWJhc2VcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICBuZXdDb25maWd1cmF0aW9uLmRhdGFiYXNlID0gdGhpcy5zeXN0ZW1GYWxsYmFja0RhdGFiYXNlTmFtZShkYXRhYmFzZVR5cGUpXG4gICAgICAgIH1cblxuICAgICAgICBpZiAoZGF0YWJhc2VUeXBlID09IFwibXNzcWxcIiAmJiBuZXdDb25maWd1cmF0aW9uLnNxbENvbmZpZz8uZGF0YWJhc2UpIHtcbiAgICAgICAgICBkZWxldGUgbmV3Q29uZmlndXJhdGlvbi5zcWxDb25maWcuZGF0YWJhc2VcbiAgICAgICAgfVxuXG4gICAgICAgIGF3YWl0IHRoaXMud2l0aERpcmVjdERhdGFiYXNlQ29ubmVjdGlvbihuZXdDb25maWd1cmF0aW9uLCBhc3luYyAoKSA9PiB7XG4gICAgICAgICAgYXdhaXQgdGhpcy5kcm9wRGF0YWJhc2UoZGF0YWJhc2VJZGVudGlmaWVyKVxuICAgICAgICB9KVxuICAgICAgfVxuXG4gICAgICBpZiAodGhpcy5hcmdzLnRlc3RpbmcpIHJldHVybiB0aGlzLnJlc3VsdFxuICAgIH1cbiAgfVxuXG4gIC8qKlxuICAgKiBSdW5zIHN5c3RlbSBmYWxsYmFjayBkYXRhYmFzZSBuYW1lLlxuICAgKiBAcGFyYW0ge3N0cmluZ30gZGF0YWJhc2VUeXBlIC0gRGF0YWJhc2UgdHlwZS5cbiAgICogQHJldHVybnMge3N0cmluZ30gLSBTeXN0ZW0vbWFpbnRlbmFuY2UgZGF0YWJhc2UgbmFtZSBmb3IgdGhhdCBkcml2ZXIuXG4gICAqL1xuICBzeXN0ZW1GYWxsYmFja0RhdGFiYXNlTmFtZShkYXRhYmFzZVR5cGUpIHtcbiAgICBpZiAoZGF0YWJhc2VUeXBlID09IFwicGdzcWxcIikgcmV0dXJuIFwicG9zdGdyZXNcIlxuICAgIGlmIChkYXRhYmFzZVR5cGUgPT0gXCJtc3NxbFwiKSByZXR1cm4gXCJtYXN0ZXJcIlxuXG4gICAgcmV0dXJuIFwibXlzcWxcIlxuICB9XG5cbiAgLyoqXG4gICAqIFJ1bnMgZHJvcCBkYXRhYmFzZS5cbiAgICogQHBhcmFtIHtzdHJpbmd9IGRhdGFiYXNlSWRlbnRpZmllciAtIERhdGFiYXNlIGlkZW50aWZpZXIuXG4gICAqIEByZXR1cm5zIHtQcm9taXNlPHZvaWQ+fSAtIFJlc29sdmVzIHdoZW4gY29tcGxldGUuXG4gICAqL1xuICBhc3luYyBkcm9wRGF0YWJhc2UoZGF0YWJhc2VJZGVudGlmaWVyKSB7XG4gICAgY29uc3QgZGF0YWJhc2VOYW1lID0gZGlnZyh0aGlzLmdldENvbmZpZ3VyYXRpb24oKS5nZXREYXRhYmFzZUNvbmZpZ3VyYXRpb24oKSwgZGF0YWJhc2VJZGVudGlmaWVyLCBcImRhdGFiYXNlXCIpXG4gICAgY29uc3Qgc3FscyA9IHRoaXMuZ2V0RGF0YWJhc2VDb25uZWN0aW9uKCkuZHJvcERhdGFiYXNlU3FsKGRhdGFiYXNlTmFtZSwge2lmRXhpc3RzOiB0cnVlfSlcblxuICAgIGF3YWl0IHRoaXMucXVlcnlPckNvbGxlY3RTcWxzKHNxbHMsIChzcWwpID0+ICh7ZGF0YWJhc2VOYW1lLCBzcWx9KSlcbiAgfVxufVxuIl19
|
|
@@ -264,20 +264,34 @@
|
|
|
264
264
|
* @property {string[]} [abilities] - Ability action list (camelCase action names). Defaults to `["read"]` for `find` and `index` when omitted.
|
|
265
265
|
* @property {Record<string, FrontendModelAttachmentConfiguration>} [attachments] - Attachment helpers keyed by attachment name.
|
|
266
266
|
* @property {string[]} [commands] - Legacy built-in command names (`index`, `find`, `create`, `update`, `destroy`, `attach`, `download`, `url`).
|
|
267
|
-
* @property {
|
|
268
|
-
* @property {
|
|
267
|
+
* @property {Array<FrontendModelResourceCustomCommand>} [collectionCommands] - Custom collection commands. Each entry is a camelCase method name, or a `{name, args?, returnType?}` object declaring typed arguments and/or a response type. The runtime derives the kebab-case command slug from the name.
|
|
268
|
+
* @property {Array<FrontendModelResourceCustomCommand>} [memberCommands] - Custom member commands. Each entry is a camelCase method name, or a `{name, args?, returnType?}` object declaring typed arguments and/or a response type. The runtime derives the kebab-case command slug from the name.
|
|
269
269
|
* @property {string[]} [builtInCollectionCommands] - Built-in collection command names (`index`, `create`).
|
|
270
270
|
* @property {string[]} [builtInMemberCommands] - Built-in member command names (`find`, `update`, `destroy`, `attach`, `download`, `url`).
|
|
271
271
|
* @property {string[]} [relationships] - Relationship names to expose in frontend models. Type and target model are inferred from the backend model's registered relationships.
|
|
272
272
|
* @property {string} [primaryKey] - Primary key attribute name.
|
|
273
273
|
* @property {FrontendModelResourceServerConfiguration} [server] - Optional legacy backend behavior overrides for built-in frontend actions.
|
|
274
274
|
*/
|
|
275
|
+
/**
|
|
276
|
+
* Object form of a custom command entry, declaring its typed arguments and/or
|
|
277
|
+
* response type alongside the command name.
|
|
278
|
+
* @typedef {object} FrontendModelResourceCustomCommandObject
|
|
279
|
+
* @property {string} name - camelCase command method name.
|
|
280
|
+
* @property {Array<{name: string, type: string}>} [args] - Typed command arguments; each generates a named, typed method parameter mapped positionally into the command payload. `type` is a JSDoc type string.
|
|
281
|
+
* @property {string} [returnType] - JSDoc type for the command response. When set, the generated method is typed `Promise<returnType>` instead of `Promise<Record<string, ?>>`. Emitted verbatim into the generated frontend model, so it must resolve there.
|
|
282
|
+
*/
|
|
283
|
+
/**
|
|
284
|
+
* A custom command entry: a plain camelCase method name, or an object declaring
|
|
285
|
+
* typed args and/or a response type.
|
|
286
|
+
* @typedef {string | FrontendModelResourceCustomCommandObject} FrontendModelResourceCustomCommand
|
|
287
|
+
*/
|
|
275
288
|
/**
|
|
276
289
|
* @typedef {Omit<FrontendModelResourceConfiguration, "abilities" | "builtInCollectionCommands" | "builtInMemberCommands" | "collectionCommands" | "commands" | "memberCommands"> & {
|
|
277
290
|
* abilities: FrontendModelResourceAbilitiesConfiguration
|
|
278
291
|
* builtInCollectionCommands: Record<string, string>
|
|
279
292
|
* builtInMemberCommands: Record<string, string>
|
|
280
293
|
* collectionCommands: Record<string, string>
|
|
294
|
+
* commandMetadata: Record<string, {args: Array<{name: string, type: string}>, returnType: string | null}>
|
|
281
295
|
* memberCommands: Record<string, string>
|
|
282
296
|
* }} NormalizedFrontendModelResourceConfiguration
|
|
283
297
|
*/
|
|
@@ -1043,13 +1057,13 @@ export type FrontendModelResourceConfiguration = {
|
|
|
1043
1057
|
*/
|
|
1044
1058
|
commands?: string[] | undefined;
|
|
1045
1059
|
/**
|
|
1046
|
-
* - Custom collection commands
|
|
1060
|
+
* - Custom collection commands. Each entry is a camelCase method name, or a `{name, args?, returnType?}` object declaring typed arguments and/or a response type. The runtime derives the kebab-case command slug from the name.
|
|
1047
1061
|
*/
|
|
1048
|
-
collectionCommands?:
|
|
1062
|
+
collectionCommands?: FrontendModelResourceCustomCommand[] | undefined;
|
|
1049
1063
|
/**
|
|
1050
|
-
* - Custom member commands
|
|
1064
|
+
* - Custom member commands. Each entry is a camelCase method name, or a `{name, args?, returnType?}` object declaring typed arguments and/or a response type. The runtime derives the kebab-case command slug from the name.
|
|
1051
1065
|
*/
|
|
1052
|
-
memberCommands?:
|
|
1066
|
+
memberCommands?: FrontendModelResourceCustomCommand[] | undefined;
|
|
1053
1067
|
/**
|
|
1054
1068
|
* - Built-in collection command names (`index`, `create`).
|
|
1055
1069
|
*/
|
|
@@ -1071,11 +1085,44 @@ export type FrontendModelResourceConfiguration = {
|
|
|
1071
1085
|
*/
|
|
1072
1086
|
server?: FrontendModelResourceServerConfiguration | undefined;
|
|
1073
1087
|
};
|
|
1088
|
+
/**
|
|
1089
|
+
* Object form of a custom command entry, declaring its typed arguments and/or
|
|
1090
|
+
* response type alongside the command name.
|
|
1091
|
+
*/
|
|
1092
|
+
export type FrontendModelResourceCustomCommandObject = {
|
|
1093
|
+
/**
|
|
1094
|
+
* - camelCase command method name.
|
|
1095
|
+
*/
|
|
1096
|
+
name: string;
|
|
1097
|
+
/**
|
|
1098
|
+
* - Typed command arguments; each generates a named, typed method parameter mapped positionally into the command payload. `type` is a JSDoc type string.
|
|
1099
|
+
*/
|
|
1100
|
+
args?: {
|
|
1101
|
+
name: string;
|
|
1102
|
+
type: string;
|
|
1103
|
+
}[] | undefined;
|
|
1104
|
+
/**
|
|
1105
|
+
* - JSDoc type for the command response. When set, the generated method is typed `Promise<returnType>` instead of `Promise<Record<string, ?>>`. Emitted verbatim into the generated frontend model, so it must resolve there.
|
|
1106
|
+
*/
|
|
1107
|
+
returnType?: string | undefined;
|
|
1108
|
+
};
|
|
1109
|
+
/**
|
|
1110
|
+
* A custom command entry: a plain camelCase method name, or an object declaring
|
|
1111
|
+
* typed args and/or a response type.
|
|
1112
|
+
*/
|
|
1113
|
+
export type FrontendModelResourceCustomCommand = string | FrontendModelResourceCustomCommandObject;
|
|
1074
1114
|
export type NormalizedFrontendModelResourceConfiguration = Omit<FrontendModelResourceConfiguration, "abilities" | "builtInCollectionCommands" | "builtInMemberCommands" | "collectionCommands" | "commands" | "memberCommands"> & {
|
|
1075
1115
|
abilities: FrontendModelResourceAbilitiesConfiguration;
|
|
1076
1116
|
builtInCollectionCommands: Record<string, string>;
|
|
1077
1117
|
builtInMemberCommands: Record<string, string>;
|
|
1078
1118
|
collectionCommands: Record<string, string>;
|
|
1119
|
+
commandMetadata: Record<string, {
|
|
1120
|
+
args: Array<{
|
|
1121
|
+
name: string;
|
|
1122
|
+
type: string;
|
|
1123
|
+
}>;
|
|
1124
|
+
returnType: string | null;
|
|
1125
|
+
}>;
|
|
1079
1126
|
memberCommands: Record<string, string>;
|
|
1080
1127
|
};
|
|
1081
1128
|
export type FrontendModelResourceClassType = Omit<typeof import("./frontend-model-resource/base-resource.js").default, never> & {
|