@rippling/rippling-sdk 0.2.0-alpha.83 → 0.2.0-alpha.85
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/examples/manifest/custom-object-creation-workflow/custom-object-creation-workflow.ts +44 -26
- package/examples/manifest/dental-practice-management/dental-practice-management.ts +470 -142
- package/examples/manifest/dental-practice-management/functions/chair-schedule.ts +23 -13
- package/examples/manifest/dental-practice-management/functions/complete-visit.ts +55 -32
- package/examples/manifest/dental-practice-management/functions/operatory-list.ts +23 -13
- package/examples/manifest/dental-practice-management/functions/patient-list.ts +23 -13
- package/examples/manifest/dental-practice-management/functions/treatment-plan-list.ts +23 -13
- package/examples/manifest/existing-manifest-mutations/existing-manifest-mutations.ts +23 -68
- package/examples/manifest/field-service-dispatch/field-service-dispatch.ts +375 -102
- package/examples/manifest/grant-program-management/grant-program-management.ts +419 -125
- package/examples/manifest/gym-membership-management/functions/check-in-desk.ts +23 -13
- package/examples/manifest/gym-membership-management/functions/member-list.ts +23 -13
- package/examples/manifest/gym-membership-management/functions/today-schedule.ts +23 -13
- package/examples/manifest/gym-membership-management/functions/trainer-list.ts +23 -13
- package/examples/manifest/gym-membership-management/gym-membership-management.ts +442 -92
- package/examples/manifest/procurement-approval-hub/procurement-approval-hub.ts +408 -122
- package/examples/manifest/simple-todo-app/functions/mark-done.ts +12 -16
- package/examples/manifest/simple-todo-app/functions/todo-list.ts +23 -13
- package/examples/manifest/simple-todo-app/simple-todo-app.ts +109 -40
- package/examples/manifest/todo-app-without-object-list-view/functions/todo-page.ts +193 -166
- package/examples/manifest/todo-app-without-object-list-view/todo-app-without-object-list-view.ts +47 -24
- package/package.json +13 -7
- package/resources/custom-objects/custom-objects.d.mts +0 -7
- package/resources/custom-objects/custom-objects.d.mts.map +1 -1
- package/resources/custom-objects/custom-objects.d.ts +0 -7
- package/resources/custom-objects/custom-objects.d.ts.map +1 -1
- package/resources/custom-objects/custom-objects.js.map +1 -1
- package/resources/custom-objects/custom-objects.mjs.map +1 -1
- package/resources/custom-objects/fields.d.mts +14 -128
- package/resources/custom-objects/fields.d.mts.map +1 -1
- package/resources/custom-objects/fields.d.ts +14 -128
- package/resources/custom-objects/fields.d.ts.map +1 -1
- package/src/resources/custom-objects/custom-objects.ts +0 -8
- package/src/resources/custom-objects/fields.ts +13 -191
- package/src/tsconfig.json +1 -1
- package/src/version.ts +1 -1
- package/version.d.mts +1 -1
- package/version.d.ts +1 -1
- package/version.js +1 -1
- package/version.mjs +1 -1
- package/core/platform/custom-objects/category.d.mts +0 -125
- package/core/platform/custom-objects/category.d.mts.map +0 -1
- package/core/platform/custom-objects/category.d.ts +0 -125
- package/core/platform/custom-objects/category.d.ts.map +0 -1
- package/core/platform/custom-objects/category.js +0 -188
- package/core/platform/custom-objects/category.js.map +0 -1
- package/core/platform/custom-objects/category.mjs +0 -178
- package/core/platform/custom-objects/category.mjs.map +0 -1
- package/core/platform/custom-objects/definition.d.mts +0 -147
- package/core/platform/custom-objects/definition.d.mts.map +0 -1
- package/core/platform/custom-objects/definition.d.ts +0 -147
- package/core/platform/custom-objects/definition.d.ts.map +0 -1
- package/core/platform/custom-objects/definition.js +0 -200
- package/core/platform/custom-objects/definition.js.map +0 -1
- package/core/platform/custom-objects/definition.mjs +0 -190
- package/core/platform/custom-objects/definition.mjs.map +0 -1
- package/core/platform/custom-objects/fields.d.mts +0 -302
- package/core/platform/custom-objects/fields.d.mts.map +0 -1
- package/core/platform/custom-objects/fields.d.ts +0 -302
- package/core/platform/custom-objects/fields.d.ts.map +0 -1
- package/core/platform/custom-objects/fields.js +0 -369
- package/core/platform/custom-objects/fields.js.map +0 -1
- package/core/platform/custom-objects/fields.mjs +0 -347
- package/core/platform/custom-objects/fields.mjs.map +0 -1
- package/core/platform/custom-objects/function-runtime.d.mts +0 -42
- package/core/platform/custom-objects/function-runtime.d.mts.map +0 -1
- package/core/platform/custom-objects/function-runtime.d.ts +0 -42
- package/core/platform/custom-objects/function-runtime.d.ts.map +0 -1
- package/core/platform/custom-objects/function-runtime.js +0 -38
- package/core/platform/custom-objects/function-runtime.js.map +0 -1
- package/core/platform/custom-objects/function-runtime.mjs +0 -35
- package/core/platform/custom-objects/function-runtime.mjs.map +0 -1
- package/core/platform/custom-objects/lifecycle.d.mts +0 -83
- package/core/platform/custom-objects/lifecycle.d.mts.map +0 -1
- package/core/platform/custom-objects/lifecycle.d.ts +0 -83
- package/core/platform/custom-objects/lifecycle.d.ts.map +0 -1
- package/core/platform/custom-objects/lifecycle.js +0 -372
- package/core/platform/custom-objects/lifecycle.js.map +0 -1
- package/core/platform/custom-objects/lifecycle.mjs +0 -367
- package/core/platform/custom-objects/lifecycle.mjs.map +0 -1
- package/core/platform/custom-objects/manager.d.mts +0 -354
- package/core/platform/custom-objects/manager.d.mts.map +0 -1
- package/core/platform/custom-objects/manager.d.ts +0 -354
- package/core/platform/custom-objects/manager.d.ts.map +0 -1
- package/core/platform/custom-objects/manager.js +0 -646
- package/core/platform/custom-objects/manager.js.map +0 -1
- package/core/platform/custom-objects/manager.mjs +0 -637
- package/core/platform/custom-objects/manager.mjs.map +0 -1
- package/core/platform/custom-objects/provisioning.d.mts +0 -138
- package/core/platform/custom-objects/provisioning.d.mts.map +0 -1
- package/core/platform/custom-objects/provisioning.d.ts +0 -138
- package/core/platform/custom-objects/provisioning.d.ts.map +0 -1
- package/core/platform/custom-objects/provisioning.js +0 -455
- package/core/platform/custom-objects/provisioning.js.map +0 -1
- package/core/platform/custom-objects/provisioning.mjs +0 -447
- package/core/platform/custom-objects/provisioning.mjs.map +0 -1
- package/core/platform/custom-objects/rql.d.mts +0 -122
- package/core/platform/custom-objects/rql.d.mts.map +0 -1
- package/core/platform/custom-objects/rql.d.ts +0 -122
- package/core/platform/custom-objects/rql.d.ts.map +0 -1
- package/core/platform/custom-objects/rql.js +0 -225
- package/core/platform/custom-objects/rql.js.map +0 -1
- package/core/platform/custom-objects/rql.mjs +0 -222
- package/core/platform/custom-objects/rql.mjs.map +0 -1
- package/core/platform/custom-objects/schema-manager.d.mts +0 -203
- package/core/platform/custom-objects/schema-manager.d.mts.map +0 -1
- package/core/platform/custom-objects/schema-manager.d.ts +0 -203
- package/core/platform/custom-objects/schema-manager.d.ts.map +0 -1
- package/core/platform/custom-objects/schema-manager.js +0 -256
- package/core/platform/custom-objects/schema-manager.js.map +0 -1
- package/core/platform/custom-objects/schema-manager.mjs +0 -249
- package/core/platform/custom-objects/schema-manager.mjs.map +0 -1
- package/core/platform/custom-objects/typed-rql.d.mts +0 -157
- package/core/platform/custom-objects/typed-rql.d.mts.map +0 -1
- package/core/platform/custom-objects/typed-rql.d.ts +0 -157
- package/core/platform/custom-objects/typed-rql.d.ts.map +0 -1
- package/core/platform/custom-objects/typed-rql.js +0 -583
- package/core/platform/custom-objects/typed-rql.js.map +0 -1
- package/core/platform/custom-objects/typed-rql.mjs +0 -580
- package/core/platform/custom-objects/typed-rql.mjs.map +0 -1
- package/core/platform/custom-objects/types.d.mts +0 -549
- package/core/platform/custom-objects/types.d.mts.map +0 -1
- package/core/platform/custom-objects/types.d.ts +0 -549
- package/core/platform/custom-objects/types.d.ts.map +0 -1
- package/core/platform/custom-objects/types.js +0 -20
- package/core/platform/custom-objects/types.js.map +0 -1
- package/core/platform/custom-objects/types.mjs +0 -17
- package/core/platform/custom-objects/types.mjs.map +0 -1
- package/core/platform/custom-objects/validation.d.mts +0 -97
- package/core/platform/custom-objects/validation.d.mts.map +0 -1
- package/core/platform/custom-objects/validation.d.ts +0 -97
- package/core/platform/custom-objects/validation.d.ts.map +0 -1
- package/core/platform/custom-objects/validation.js +0 -477
- package/core/platform/custom-objects/validation.js.map +0 -1
- package/core/platform/custom-objects/validation.mjs +0 -465
- package/core/platform/custom-objects/validation.mjs.map +0 -1
- package/core/platform/custom-objects.d.mts +0 -67
- package/core/platform/custom-objects.d.mts.map +0 -1
- package/core/platform/custom-objects.d.ts +0 -67
- package/core/platform/custom-objects.d.ts.map +0 -1
- package/core/platform/custom-objects.js +0 -118
- package/core/platform/custom-objects.js.map +0 -1
- package/core/platform/custom-objects.mjs +0 -45
- package/core/platform/custom-objects.mjs.map +0 -1
- package/core/platform/manifest/custom-objects/action.d.mts +0 -58
- package/core/platform/manifest/custom-objects/action.d.mts.map +0 -1
- package/core/platform/manifest/custom-objects/action.d.ts +0 -58
- package/core/platform/manifest/custom-objects/action.d.ts.map +0 -1
- package/core/platform/manifest/custom-objects/action.js +0 -44
- package/core/platform/manifest/custom-objects/action.js.map +0 -1
- package/core/platform/manifest/custom-objects/action.mjs +0 -40
- package/core/platform/manifest/custom-objects/action.mjs.map +0 -1
- package/core/platform/manifest/custom-objects/conditions.d.mts +0 -240
- package/core/platform/manifest/custom-objects/conditions.d.mts.map +0 -1
- package/core/platform/manifest/custom-objects/conditions.d.ts +0 -240
- package/core/platform/manifest/custom-objects/conditions.d.ts.map +0 -1
- package/core/platform/manifest/custom-objects/conditions.js +0 -326
- package/core/platform/manifest/custom-objects/conditions.js.map +0 -1
- package/core/platform/manifest/custom-objects/conditions.mjs +0 -321
- package/core/platform/manifest/custom-objects/conditions.mjs.map +0 -1
- package/core/platform/manifest/custom-objects/existing.d.mts +0 -87
- package/core/platform/manifest/custom-objects/existing.d.mts.map +0 -1
- package/core/platform/manifest/custom-objects/existing.d.ts +0 -87
- package/core/platform/manifest/custom-objects/existing.d.ts.map +0 -1
- package/core/platform/manifest/custom-objects/existing.js +0 -113
- package/core/platform/manifest/custom-objects/existing.js.map +0 -1
- package/core/platform/manifest/custom-objects/existing.mjs +0 -109
- package/core/platform/manifest/custom-objects/existing.mjs.map +0 -1
- package/core/platform/manifest/custom-objects/field-resolution.d.mts +0 -48
- package/core/platform/manifest/custom-objects/field-resolution.d.mts.map +0 -1
- package/core/platform/manifest/custom-objects/field-resolution.d.ts +0 -48
- package/core/platform/manifest/custom-objects/field-resolution.d.ts.map +0 -1
- package/core/platform/manifest/custom-objects/field-resolution.js +0 -61
- package/core/platform/manifest/custom-objects/field-resolution.js.map +0 -1
- package/core/platform/manifest/custom-objects/field-resolution.mjs +0 -58
- package/core/platform/manifest/custom-objects/field-resolution.mjs.map +0 -1
- package/core/platform/manifest/custom-objects/function-file.d.mts +0 -87
- package/core/platform/manifest/custom-objects/function-file.d.mts.map +0 -1
- package/core/platform/manifest/custom-objects/function-file.d.ts +0 -87
- package/core/platform/manifest/custom-objects/function-file.d.ts.map +0 -1
- package/core/platform/manifest/custom-objects/function-file.js +0 -76
- package/core/platform/manifest/custom-objects/function-file.js.map +0 -1
- package/core/platform/manifest/custom-objects/function-file.mjs +0 -69
- package/core/platform/manifest/custom-objects/function-file.mjs.map +0 -1
- package/core/platform/manifest/custom-objects/list-view.d.mts +0 -65
- package/core/platform/manifest/custom-objects/list-view.d.mts.map +0 -1
- package/core/platform/manifest/custom-objects/list-view.d.ts +0 -65
- package/core/platform/manifest/custom-objects/list-view.d.ts.map +0 -1
- package/core/platform/manifest/custom-objects/list-view.js +0 -33
- package/core/platform/manifest/custom-objects/list-view.js.map +0 -1
- package/core/platform/manifest/custom-objects/list-view.mjs +0 -30
- package/core/platform/manifest/custom-objects/list-view.mjs.map +0 -1
- package/core/platform/manifest/custom-objects/object-list-page.d.mts +0 -43
- package/core/platform/manifest/custom-objects/object-list-page.d.mts.map +0 -1
- package/core/platform/manifest/custom-objects/object-list-page.d.ts +0 -43
- package/core/platform/manifest/custom-objects/object-list-page.d.ts.map +0 -1
- package/core/platform/manifest/custom-objects/object-list-page.js +0 -54
- package/core/platform/manifest/custom-objects/object-list-page.js.map +0 -1
- package/core/platform/manifest/custom-objects/object-list-page.mjs +0 -50
- package/core/platform/manifest/custom-objects/object-list-page.mjs.map +0 -1
- package/core/platform/manifest/custom-objects/page-layout.d.mts +0 -142
- package/core/platform/manifest/custom-objects/page-layout.d.mts.map +0 -1
- package/core/platform/manifest/custom-objects/page-layout.d.ts +0 -142
- package/core/platform/manifest/custom-objects/page-layout.d.ts.map +0 -1
- package/core/platform/manifest/custom-objects/page-layout.js +0 -184
- package/core/platform/manifest/custom-objects/page-layout.js.map +0 -1
- package/core/platform/manifest/custom-objects/page-layout.mjs +0 -180
- package/core/platform/manifest/custom-objects/page-layout.mjs.map +0 -1
- package/core/platform/manifest/custom-objects/register.d.mts +0 -87
- package/core/platform/manifest/custom-objects/register.d.mts.map +0 -1
- package/core/platform/manifest/custom-objects/register.d.ts +0 -87
- package/core/platform/manifest/custom-objects/register.d.ts.map +0 -1
- package/core/platform/manifest/custom-objects/register.js +0 -395
- package/core/platform/manifest/custom-objects/register.js.map +0 -1
- package/core/platform/manifest/custom-objects/register.mjs +0 -391
- package/core/platform/manifest/custom-objects/register.mjs.map +0 -1
- package/core/platform/manifest/custom-objects/rule.d.mts +0 -113
- package/core/platform/manifest/custom-objects/rule.d.mts.map +0 -1
- package/core/platform/manifest/custom-objects/rule.d.ts +0 -113
- package/core/platform/manifest/custom-objects/rule.d.ts.map +0 -1
- package/core/platform/manifest/custom-objects/rule.js +0 -49
- package/core/platform/manifest/custom-objects/rule.js.map +0 -1
- package/core/platform/manifest/custom-objects/rule.mjs +0 -45
- package/core/platform/manifest/custom-objects/rule.mjs.map +0 -1
- package/core/platform/manifest/custom-objects/sdui-page.d.mts +0 -214
- package/core/platform/manifest/custom-objects/sdui-page.d.mts.map +0 -1
- package/core/platform/manifest/custom-objects/sdui-page.d.ts +0 -214
- package/core/platform/manifest/custom-objects/sdui-page.d.ts.map +0 -1
- package/core/platform/manifest/custom-objects/sdui-page.js +0 -194
- package/core/platform/manifest/custom-objects/sdui-page.js.map +0 -1
- package/core/platform/manifest/custom-objects/sdui-page.mjs +0 -182
- package/core/platform/manifest/custom-objects/sdui-page.mjs.map +0 -1
- package/core/platform/manifest/custom-objects/typed-condition.d.mts +0 -28
- package/core/platform/manifest/custom-objects/typed-condition.d.mts.map +0 -1
- package/core/platform/manifest/custom-objects/typed-condition.d.ts +0 -28
- package/core/platform/manifest/custom-objects/typed-condition.d.ts.map +0 -1
- package/core/platform/manifest/custom-objects/typed-condition.js +0 -434
- package/core/platform/manifest/custom-objects/typed-condition.js.map +0 -1
- package/core/platform/manifest/custom-objects/typed-condition.mjs +0 -431
- package/core/platform/manifest/custom-objects/typed-condition.mjs.map +0 -1
- package/core/platform/manifest/custom-objects/types.d.mts +0 -29
- package/core/platform/manifest/custom-objects/types.d.mts.map +0 -1
- package/core/platform/manifest/custom-objects/types.d.ts +0 -29
- package/core/platform/manifest/custom-objects/types.d.ts.map +0 -1
- package/core/platform/manifest/custom-objects/types.js +0 -3
- package/core/platform/manifest/custom-objects/types.js.map +0 -1
- package/core/platform/manifest/custom-objects/types.mjs +0 -2
- package/core/platform/manifest/custom-objects/types.mjs.map +0 -1
- package/core/platform/manifest/custom-objects/validation.d.mts +0 -44
- package/core/platform/manifest/custom-objects/validation.d.mts.map +0 -1
- package/core/platform/manifest/custom-objects/validation.d.ts +0 -44
- package/core/platform/manifest/custom-objects/validation.d.ts.map +0 -1
- package/core/platform/manifest/custom-objects/validation.js +0 -30
- package/core/platform/manifest/custom-objects/validation.js.map +0 -1
- package/core/platform/manifest/custom-objects/validation.mjs +0 -27
- package/core/platform/manifest/custom-objects/validation.mjs.map +0 -1
- package/core/platform/manifest/custom-objects/workflow.d.mts +0 -47
- package/core/platform/manifest/custom-objects/workflow.d.mts.map +0 -1
- package/core/platform/manifest/custom-objects/workflow.d.ts +0 -47
- package/core/platform/manifest/custom-objects/workflow.d.ts.map +0 -1
- package/core/platform/manifest/custom-objects/workflow.js +0 -38
- package/core/platform/manifest/custom-objects/workflow.js.map +0 -1
- package/core/platform/manifest/custom-objects/workflow.mjs +0 -34
- package/core/platform/manifest/custom-objects/workflow.mjs.map +0 -1
- package/core/platform/manifest/custom-objects.d.mts +0 -85
- package/core/platform/manifest/custom-objects.d.mts.map +0 -1
- package/core/platform/manifest/custom-objects.d.ts +0 -85
- package/core/platform/manifest/custom-objects.d.ts.map +0 -1
- package/core/platform/manifest/custom-objects.js +0 -84
- package/core/platform/manifest/custom-objects.js.map +0 -1
- package/core/platform/manifest/custom-objects.mjs +0 -55
- package/core/platform/manifest/custom-objects.mjs.map +0 -1
- package/examples/manifest/custom-object-creation-workflow/functions/intake-request-created.ts +0 -20
- package/examples/manifest/custom-object-creation-workflow/schema.ts +0 -29
- package/examples/manifest/dental-practice-management/schema.ts +0 -298
- package/examples/manifest/field-service-dispatch/schema.ts +0 -248
- package/examples/manifest/grant-program-management/schema.ts +0 -269
- package/examples/manifest/gym-membership-management/schema.ts +0 -266
- package/examples/manifest/procurement-approval-hub/functions/route-purchase-request.ts +0 -20
- package/examples/manifest/procurement-approval-hub/schema.ts +0 -256
- package/examples/manifest/simple-todo-app/schema.ts +0 -60
- package/examples/manifest/todo-app-without-object-list-view/schema.ts +0 -28
- package/src/core/platform/README.md +0 -20
- package/src/core/platform/custom-objects/category.ts +0 -294
- package/src/core/platform/custom-objects/definition.ts +0 -373
- package/src/core/platform/custom-objects/fields.ts +0 -653
- package/src/core/platform/custom-objects/function-runtime.ts +0 -76
- package/src/core/platform/custom-objects/lifecycle.ts +0 -557
- package/src/core/platform/custom-objects/manager.ts +0 -959
- package/src/core/platform/custom-objects/provisioning.ts +0 -672
- package/src/core/platform/custom-objects/rql.ts +0 -375
- package/src/core/platform/custom-objects/schema-manager.ts +0 -351
- package/src/core/platform/custom-objects/typed-rql.ts +0 -1273
- package/src/core/platform/custom-objects/types.ts +0 -753
- package/src/core/platform/custom-objects/validation.ts +0 -573
- package/src/core/platform/custom-objects.ts +0 -269
- package/src/core/platform/manifest/README.md +0 -26
- package/src/core/platform/manifest/custom-objects/action.ts +0 -87
- package/src/core/platform/manifest/custom-objects/conditions.ts +0 -762
- package/src/core/platform/manifest/custom-objects/existing.ts +0 -221
- package/src/core/platform/manifest/custom-objects/field-resolution.ts +0 -116
- package/src/core/platform/manifest/custom-objects/function-file.ts +0 -149
- package/src/core/platform/manifest/custom-objects/list-view.ts +0 -95
- package/src/core/platform/manifest/custom-objects/object-list-page.ts +0 -87
- package/src/core/platform/manifest/custom-objects/page-layout.ts +0 -380
- package/src/core/platform/manifest/custom-objects/register.ts +0 -555
- package/src/core/platform/manifest/custom-objects/rule.ts +0 -192
- package/src/core/platform/manifest/custom-objects/sdui-page.ts +0 -515
- package/src/core/platform/manifest/custom-objects/typed-condition.ts +0 -617
- package/src/core/platform/manifest/custom-objects/types.ts +0 -92
- package/src/core/platform/manifest/custom-objects/validation.ts +0 -70
- package/src/core/platform/manifest/custom-objects/workflow.ts +0 -71
- package/src/core/platform/manifest/custom-objects.ts +0 -203
- package/src/pebble-sdui-web-custom-apps-shim.d.ts +0 -3
|
@@ -1,573 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
AnyCustomObjectDefinition,
|
|
3
|
-
AnyFieldDefinition,
|
|
4
|
-
CreateInput,
|
|
5
|
-
FieldNames,
|
|
6
|
-
LookupReference,
|
|
7
|
-
LogicalCategoryName,
|
|
8
|
-
LogicalCustomFieldName,
|
|
9
|
-
LogicalCustomObjectName,
|
|
10
|
-
RecordShape,
|
|
11
|
-
UpdateInput,
|
|
12
|
-
} from './types';
|
|
13
|
-
|
|
14
|
-
/** Single validation failure returned by schema or record validators. */
|
|
15
|
-
export interface ValidationIssue {
|
|
16
|
-
readonly path: string;
|
|
17
|
-
readonly message: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/** Aggregate validation result returned by runtime validation helpers. */
|
|
21
|
-
export interface ValidationResult {
|
|
22
|
-
readonly valid: boolean;
|
|
23
|
-
readonly issues: readonly ValidationIssue[];
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
/** Exact runtime regex used to validate logical snake_case names. */
|
|
27
|
-
const LOGICAL_NAME_PATTERN = /^[a-z](?:[a-z0-9_]*[a-z0-9])?$/;
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Builds a single validation issue object.
|
|
31
|
-
*
|
|
32
|
-
* @param path - Logical path identifying the invalid field or property.
|
|
33
|
-
* @param message - Human-readable validation failure message.
|
|
34
|
-
* @returns Structured validation issue object.
|
|
35
|
-
*/
|
|
36
|
-
function issue(path: string, message: string): ValidationIssue {
|
|
37
|
-
return { path, message };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
const AUTONUMBER_PLACEHOLDER_PATTERN = /\{(0+)\}/g;
|
|
41
|
-
const AUTONUMBER_DATE_TOKENS = ['{YYYY}', '{MM}', '{DD}'] as const;
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Validates autonumber metadata against the live backend rules.
|
|
45
|
-
*
|
|
46
|
-
* @param format - Autonumber format string to validate.
|
|
47
|
-
* @param startingNumber - Optional starting number supplied by the caller.
|
|
48
|
-
* @param pathPrefix - Validation path prefix used in returned issues.
|
|
49
|
-
* @returns Validation issues describing any invalid autonumber metadata.
|
|
50
|
-
*/
|
|
51
|
-
function validateAutonumberNameField(
|
|
52
|
-
format: string,
|
|
53
|
-
startingNumber: number | undefined,
|
|
54
|
-
pathPrefix: string,
|
|
55
|
-
): ValidationIssue[] {
|
|
56
|
-
const issues: ValidationIssue[] = [];
|
|
57
|
-
const trimmedFormat = format.trim();
|
|
58
|
-
if (trimmedFormat.length === 0) {
|
|
59
|
-
issues.push(issue(`${pathPrefix}.format`, 'Autonumber format cannot be empty.'));
|
|
60
|
-
return issues;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const placeholders = Array.from(trimmedFormat.matchAll(AUTONUMBER_PLACEHOLDER_PATTERN));
|
|
64
|
-
if (placeholders.length !== 1) {
|
|
65
|
-
issues.push(
|
|
66
|
-
issue(`${pathPrefix}.format`, 'Autonumber format must contain exactly one {0...} placeholder.'),
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
const placeholderDigits = placeholders[0]?.[1];
|
|
71
|
-
const placeholderWidth = placeholderDigits?.length;
|
|
72
|
-
if (placeholderWidth != null && (placeholderWidth < 1 || placeholderWidth > 10)) {
|
|
73
|
-
issues.push(
|
|
74
|
-
issue(`${pathPrefix}.format`, 'Autonumber placeholder width must be between 1 and 10 digits.'),
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
if (startingNumber == null) {
|
|
79
|
-
issues.push(
|
|
80
|
-
issue(`${pathPrefix}.startingNumber`, 'startingNumber is required for autonumber name fields.'),
|
|
81
|
-
);
|
|
82
|
-
} else if (!Number.isInteger(startingNumber) || startingNumber < 0) {
|
|
83
|
-
issues.push(issue(`${pathPrefix}.startingNumber`, 'startingNumber must be a non-negative integer.'));
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
for (const token of AUTONUMBER_DATE_TOKENS) {
|
|
87
|
-
const occurrences = trimmedFormat.split(token).length - 1;
|
|
88
|
-
if (occurrences > 1) {
|
|
89
|
-
issues.push(issue(`${pathPrefix}.format`, `${token} may appear at most once in autonumber format.`));
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
return issues;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Returns whether one value is a plain object-like record.
|
|
98
|
-
*
|
|
99
|
-
* @param value - Candidate value to inspect.
|
|
100
|
-
* @returns Whether the value is a non-array object.
|
|
101
|
-
*/
|
|
102
|
-
function isObject(value: unknown): value is Record<string, unknown> {
|
|
103
|
-
return value != null && typeof value === 'object' && !Array.isArray(value);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Validates a logical snake_case name at runtime.
|
|
108
|
-
*
|
|
109
|
-
* Assumptions enforced by this helper:
|
|
110
|
-
* - the name must be lowercase snake_case
|
|
111
|
-
* - the name must not already include the platform-managed `__c` suffix
|
|
112
|
-
* - the name must not contain consecutive underscores
|
|
113
|
-
*/
|
|
114
|
-
/**
|
|
115
|
-
* Validates one logical snake_case name at runtime.
|
|
116
|
-
*
|
|
117
|
-
* @param value - Candidate logical name to validate.
|
|
118
|
-
* @param path - Validation path to use in returned issues.
|
|
119
|
-
* @returns Validation issues describing any logical-name contract violations.
|
|
120
|
-
*/
|
|
121
|
-
function validateLogicalName(value: string, path: string): ValidationIssue[] {
|
|
122
|
-
const issues: ValidationIssue[] = [];
|
|
123
|
-
if (value.endsWith('__c')) {
|
|
124
|
-
issues.push(issue(path, 'Logical names must be unsuffixed; do not include __c.'));
|
|
125
|
-
}
|
|
126
|
-
if (!LOGICAL_NAME_PATTERN.test(value)) {
|
|
127
|
-
issues.push(issue(path, 'Logical names must be snake_case and start with a lowercase letter.'));
|
|
128
|
-
}
|
|
129
|
-
if (value.includes('__')) {
|
|
130
|
-
issues.push(issue(path, 'Logical names must not contain consecutive underscores.'));
|
|
131
|
-
}
|
|
132
|
-
return issues;
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Throws when a logical custom-object name is invalid.
|
|
137
|
-
*
|
|
138
|
-
* @param value - Candidate logical custom-object name to validate.
|
|
139
|
-
* @typeParam TObjectName - Candidate logical custom-object name.
|
|
140
|
-
*/
|
|
141
|
-
export function validateLogicalCustomObjectName<TObjectName extends string>(
|
|
142
|
-
value: LogicalCustomObjectName<TObjectName> | string,
|
|
143
|
-
): asserts value is LogicalCustomObjectName<TObjectName> {
|
|
144
|
-
const issues = validateLogicalName(value, 'name');
|
|
145
|
-
if (issues.length === 0) return;
|
|
146
|
-
throw new Error(issues.map((current) => `${current.path}: ${current.message}`).join('\n'));
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* Throws when a logical category name is invalid.
|
|
151
|
-
*
|
|
152
|
-
* @param value - Candidate logical category name to validate.
|
|
153
|
-
* @typeParam TCategoryName - Candidate logical category name.
|
|
154
|
-
*/
|
|
155
|
-
export function validateLogicalCategoryName<TCategoryName extends string>(
|
|
156
|
-
value: LogicalCategoryName<TCategoryName> | string,
|
|
157
|
-
): asserts value is LogicalCategoryName<TCategoryName> {
|
|
158
|
-
const issues = validateLogicalName(value, 'category');
|
|
159
|
-
if (issues.length === 0) return;
|
|
160
|
-
throw new Error(issues.map((current) => `${current.path}: ${current.message}`).join('\n'));
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
/**
|
|
164
|
-
* Throws when a logical custom-field name is invalid.
|
|
165
|
-
*
|
|
166
|
-
* @param value - Candidate logical custom-field name to validate.
|
|
167
|
-
* @typeParam TFieldName - Candidate logical custom-field name.
|
|
168
|
-
*/
|
|
169
|
-
export function validateLogicalCustomFieldName<TFieldName extends string>(
|
|
170
|
-
value: LogicalCustomFieldName<TFieldName> | string,
|
|
171
|
-
): asserts value is LogicalCustomFieldName<TFieldName> {
|
|
172
|
-
const issues = validateLogicalName(value, 'field');
|
|
173
|
-
if (issues.length === 0) return;
|
|
174
|
-
throw new Error(issues.map((current) => `${current.path}: ${current.message}`).join('\n'));
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Recognizes lookup values accepted by the typed helpers.
|
|
179
|
-
*
|
|
180
|
-
* Assumption: callers either pass a bare id string or a small `{ id, apiName? }`
|
|
181
|
-
* object. The validator does not attempt to resolve the referenced record.
|
|
182
|
-
*/
|
|
183
|
-
/**
|
|
184
|
-
* Recognizes lookup references accepted by the typed helpers.
|
|
185
|
-
*
|
|
186
|
-
* @param value - Candidate field value.
|
|
187
|
-
* @returns Whether the value is a string id or `{ id, apiName? }` lookup reference.
|
|
188
|
-
*/
|
|
189
|
-
function isLookupReference(value: unknown): value is LookupReference<string> {
|
|
190
|
-
return (
|
|
191
|
-
typeof value === 'string' ||
|
|
192
|
-
(isObject(value) &&
|
|
193
|
-
typeof value['id'] === 'string' &&
|
|
194
|
-
(value['apiName'] == null || typeof value['apiName'] === 'string'))
|
|
195
|
-
);
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
* Recognizes file references accepted by the typed helpers.
|
|
200
|
-
*
|
|
201
|
-
* @param value - Candidate field value.
|
|
202
|
-
* @returns Whether the value is a file id string or `{ id }` object.
|
|
203
|
-
*/
|
|
204
|
-
function isFileReference(value: unknown): boolean {
|
|
205
|
-
return typeof value === 'string' || (isObject(value) && typeof value['id'] === 'string');
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Returns the number of decimal places represented by a finite JavaScript number.
|
|
210
|
-
*
|
|
211
|
-
* @param value - Numeric value to inspect.
|
|
212
|
-
* @returns Decimal-place count after accounting for exponent notation.
|
|
213
|
-
*/
|
|
214
|
-
function countDecimalPlaces(value: number): number {
|
|
215
|
-
if (!Number.isFinite(value)) return 0;
|
|
216
|
-
const normalized = value.toString().toLowerCase();
|
|
217
|
-
if (!normalized.includes('e')) {
|
|
218
|
-
const fraction = normalized.split('.')[1];
|
|
219
|
-
return fraction?.length ?? 0;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
const [coefficient, exponentText] = normalized.split('e');
|
|
223
|
-
const exponent = Number(exponentText);
|
|
224
|
-
const coefficientFractionLength = coefficient?.split('.')[1]?.length ?? 0;
|
|
225
|
-
return Math.max(0, coefficientFractionLength - exponent);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
/**
|
|
229
|
-
* Validates one field value against the field-definition kind metadata.
|
|
230
|
-
*
|
|
231
|
-
* Assumption: this helper intentionally performs shape and primitive checks only.
|
|
232
|
-
* It does not attempt to execute formulas, validate RQL, or verify that lookup
|
|
233
|
-
* ids point at existing records.
|
|
234
|
-
*/
|
|
235
|
-
/**
|
|
236
|
-
* Validates one field value against the field-definition kind metadata.
|
|
237
|
-
*
|
|
238
|
-
* @param field - Typed field definition that owns the value.
|
|
239
|
-
* @param value - Candidate runtime value to validate.
|
|
240
|
-
* @param path - Validation path to use in returned issues.
|
|
241
|
-
* @returns Validation issues describing any mismatches.
|
|
242
|
-
*/
|
|
243
|
-
function validateFieldValue(field: AnyFieldDefinition, value: unknown, path: string): ValidationIssue[] {
|
|
244
|
-
switch (field.kind) {
|
|
245
|
-
case 'text':
|
|
246
|
-
case 'long-text':
|
|
247
|
-
case 'formula-text': {
|
|
248
|
-
if (typeof value !== 'string') return [issue(path, 'Expected a string.')];
|
|
249
|
-
return field.maxLength != null && value.length > field.maxLength ?
|
|
250
|
-
[issue(path, `Expected at most ${field.maxLength} characters.`)]
|
|
251
|
-
: [];
|
|
252
|
-
}
|
|
253
|
-
case 'email':
|
|
254
|
-
case 'url':
|
|
255
|
-
case 'phone':
|
|
256
|
-
case 'radio':
|
|
257
|
-
case 'formula-url':
|
|
258
|
-
if (typeof value !== 'string') return [issue(path, 'Expected a string.')];
|
|
259
|
-
if (field.kind === 'radio' && !field.options.includes(value)) {
|
|
260
|
-
return [issue(path, `Expected one of: ${field.options.join(', ')}.`)];
|
|
261
|
-
}
|
|
262
|
-
return [];
|
|
263
|
-
case 'number':
|
|
264
|
-
case 'currency':
|
|
265
|
-
case 'percentage':
|
|
266
|
-
case 'formula-number':
|
|
267
|
-
case 'formula-currency':
|
|
268
|
-
case 'formula-percentage': {
|
|
269
|
-
if (typeof value !== 'number') return [issue(path, 'Expected a number.')];
|
|
270
|
-
if (!Number.isFinite(value)) return [issue(path, 'Expected a finite number.')];
|
|
271
|
-
if ('decimalPlaces' in field && typeof field.decimalPlaces === 'number') {
|
|
272
|
-
return countDecimalPlaces(value) > field.decimalPlaces ?
|
|
273
|
-
[issue(path, `Expected at most ${field.decimalPlaces} decimal places.`)]
|
|
274
|
-
: [];
|
|
275
|
-
}
|
|
276
|
-
return [];
|
|
277
|
-
}
|
|
278
|
-
case 'date':
|
|
279
|
-
case 'datetime':
|
|
280
|
-
case 'time':
|
|
281
|
-
case 'formula-date':
|
|
282
|
-
return typeof value === 'string' ? [] : [issue(path, 'Expected an ISO-like date/time string.')];
|
|
283
|
-
case 'boolean':
|
|
284
|
-
case 'formula-boolean':
|
|
285
|
-
return typeof value === 'boolean' ? [] : [issue(path, 'Expected a boolean.')];
|
|
286
|
-
case 'select':
|
|
287
|
-
if (!(Array.isArray(value) && value.every((entry) => typeof entry === 'string'))) {
|
|
288
|
-
return [issue(path, 'Expected an array of strings.')];
|
|
289
|
-
}
|
|
290
|
-
return value.every((entry) => field.options.includes(entry)) ?
|
|
291
|
-
[]
|
|
292
|
-
: [issue(path, `Expected each value to be one of: ${field.options.join(', ')}.`)];
|
|
293
|
-
case 'file':
|
|
294
|
-
return isFileReference(value) ? [] : [issue(path, 'Expected a file id string or { id } object.')];
|
|
295
|
-
case 'lookup':
|
|
296
|
-
case 'parent-child':
|
|
297
|
-
return isLookupReference(value) ? [] : [issue(path, 'Expected a record id string or { id } object.')];
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
/**
|
|
302
|
-
* Returns the logical field names defined on a schema object.
|
|
303
|
-
*
|
|
304
|
-
* @typeParam TDefinition - Object definition whose field names should be listed.
|
|
305
|
-
*/
|
|
306
|
-
/**
|
|
307
|
-
* Returns the logical field names defined on one schema object.
|
|
308
|
-
*
|
|
309
|
-
* @param definition - Object definition whose field names should be listed.
|
|
310
|
-
* @returns Logical field names from the supplied definition.
|
|
311
|
-
* @typeParam TDefinition - Object definition whose field names should be listed.
|
|
312
|
-
*/
|
|
313
|
-
function definedFieldNames<TDefinition extends AnyCustomObjectDefinition>(
|
|
314
|
-
definition: TDefinition,
|
|
315
|
-
): FieldNames<TDefinition>[] {
|
|
316
|
-
return Object.keys(definition.fields) as FieldNames<TDefinition>[];
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* Validates a schema definition after the type system has done its part.
|
|
321
|
-
*
|
|
322
|
-
* @param definition - Typed object definition to validate at runtime.
|
|
323
|
-
* @returns Validation issues describing any remaining runtime-level problems.
|
|
324
|
-
*/
|
|
325
|
-
export function validateCustomObjectDefinition(definition: AnyCustomObjectDefinition): ValidationIssue[] {
|
|
326
|
-
const issues: ValidationIssue[] = [];
|
|
327
|
-
|
|
328
|
-
issues.push(...validateLogicalName(definition.name, 'name'));
|
|
329
|
-
issues.push(...validateLogicalName(definition.category, 'category'));
|
|
330
|
-
|
|
331
|
-
if (!definition.apiName.endsWith('__c')) {
|
|
332
|
-
issues.push(issue('apiName', 'Compiled custom-object api names must end with __c.'));
|
|
333
|
-
}
|
|
334
|
-
if (!definition.categoryApiName.endsWith('__c')) {
|
|
335
|
-
issues.push(issue('categoryApiName', 'Compiled category api names must end with __c.'));
|
|
336
|
-
}
|
|
337
|
-
if (definition.categoryDefinition != null) {
|
|
338
|
-
if (definition.categoryDefinition.name !== definition.category) {
|
|
339
|
-
issues.push(issue('categoryDefinition.name', 'categoryDefinition.name must match category.'));
|
|
340
|
-
}
|
|
341
|
-
if (definition.categoryDefinition.apiName !== definition.categoryApiName) {
|
|
342
|
-
issues.push(
|
|
343
|
-
issue('categoryDefinition.apiName', 'categoryDefinition.apiName must match categoryApiName.'),
|
|
344
|
-
);
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
if (definition.ownerRole != null && typeof definition.ownerRole !== 'string') {
|
|
348
|
-
issues.push(issue('ownerRole', 'ownerRole must be a role id string.'));
|
|
349
|
-
}
|
|
350
|
-
if (definition.enableHistory != null && typeof definition.enableHistory !== 'boolean') {
|
|
351
|
-
issues.push(issue('enableHistory', 'enableHistory must be a boolean when provided.'));
|
|
352
|
-
}
|
|
353
|
-
if (definition.nameField.kind === 'autonumber') {
|
|
354
|
-
issues.push(
|
|
355
|
-
...validateAutonumberNameField(
|
|
356
|
-
definition.nameField.format,
|
|
357
|
-
definition.nameField.startingNumber,
|
|
358
|
-
'nameField',
|
|
359
|
-
),
|
|
360
|
-
);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
const knownSections = new Set(Object.keys(definition.sections));
|
|
364
|
-
for (const [logicalFieldName, rawField] of Object.entries(definition.fields)) {
|
|
365
|
-
const field = rawField as AnyFieldDefinition & { apiName?: string; logicalName?: string };
|
|
366
|
-
issues.push(...validateLogicalName(logicalFieldName, `fields.${logicalFieldName}`));
|
|
367
|
-
if (field.apiName == null || !field.apiName.endsWith('__c')) {
|
|
368
|
-
issues.push(
|
|
369
|
-
issue(`fields.${logicalFieldName}.apiName`, 'Compiled custom-field api names must end with __c.'),
|
|
370
|
-
);
|
|
371
|
-
}
|
|
372
|
-
if (field.logicalName !== logicalFieldName) {
|
|
373
|
-
issues.push(
|
|
374
|
-
issue(`fields.${logicalFieldName}.logicalName`, 'Field logicalName must match the field map key.'),
|
|
375
|
-
);
|
|
376
|
-
}
|
|
377
|
-
if (field.section != null && !knownSections.has(field.section)) {
|
|
378
|
-
issues.push(issue(`fields.${logicalFieldName}.section`, `Unknown section key "${field.section}".`));
|
|
379
|
-
}
|
|
380
|
-
if (field.enableHistory != null && typeof field.enableHistory !== 'boolean') {
|
|
381
|
-
issues.push(
|
|
382
|
-
issue(`fields.${logicalFieldName}.enableHistory`, 'enableHistory must be a boolean when provided.'),
|
|
383
|
-
);
|
|
384
|
-
}
|
|
385
|
-
if ((field.kind === 'select' || field.kind === 'radio') && field.options.length === 0) {
|
|
386
|
-
issues.push(
|
|
387
|
-
issue(`fields.${logicalFieldName}.options`, 'Choice fields must define at least one option.'),
|
|
388
|
-
);
|
|
389
|
-
}
|
|
390
|
-
if (field.kind === 'formula-text' && (field.maxLength == null || field.maxLength <= 0)) {
|
|
391
|
-
issues.push(
|
|
392
|
-
issue(`fields.${logicalFieldName}.maxLength`, 'Text formula fields require a positive maxLength.'),
|
|
393
|
-
);
|
|
394
|
-
}
|
|
395
|
-
if ('maxLength' in field && typeof field.maxLength === 'number' && field.maxLength <= 0) {
|
|
396
|
-
issues.push(issue(`fields.${logicalFieldName}.maxLength`, 'maxLength must be positive.'));
|
|
397
|
-
}
|
|
398
|
-
if ('decimalPlaces' in field && typeof field.decimalPlaces === 'number' && field.decimalPlaces < 0) {
|
|
399
|
-
issues.push(issue(`fields.${logicalFieldName}.decimalPlaces`, 'decimalPlaces cannot be negative.'));
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
return issues;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
/**
|
|
407
|
-
* Shared implementation for create and update validation.
|
|
408
|
-
*
|
|
409
|
-
* @typeParam TDefinition - Object definition whose record payload is being validated.
|
|
410
|
-
*/
|
|
411
|
-
/**
|
|
412
|
-
* Shared implementation for create and update payload validation.
|
|
413
|
-
*
|
|
414
|
-
* @param definition - Object definition whose payload is being validated.
|
|
415
|
-
* @param input - Candidate logical payload to validate.
|
|
416
|
-
* @param mode - Validation mode determining create-vs-update requirements.
|
|
417
|
-
* @returns Validation issues describing any payload problems.
|
|
418
|
-
* @typeParam TDefinition - Object definition whose record payload is being validated.
|
|
419
|
-
*/
|
|
420
|
-
function validateRecordInputInternal<TDefinition extends AnyCustomObjectDefinition>(
|
|
421
|
-
definition: TDefinition,
|
|
422
|
-
input: Record<string, unknown>,
|
|
423
|
-
mode: 'create' | 'update',
|
|
424
|
-
): ValidationIssue[] {
|
|
425
|
-
const issues: ValidationIssue[] = [];
|
|
426
|
-
const fields = definition.fields as Record<string, AnyFieldDefinition>;
|
|
427
|
-
const names = definedFieldNames(definition);
|
|
428
|
-
const knownFieldNames = new Set(names);
|
|
429
|
-
const writableFieldNames = new Set(names.filter((name) => fields[name]!.writable) as readonly string[]);
|
|
430
|
-
|
|
431
|
-
if (mode === 'create' && definition.nameField.kind !== 'autonumber' && typeof input['name'] !== 'string') {
|
|
432
|
-
issues.push(issue('name', 'name is required when the object does not use an autonumber name field.'));
|
|
433
|
-
}
|
|
434
|
-
if (input['name'] != null && typeof input['name'] !== 'string') {
|
|
435
|
-
issues.push(issue('name', 'Expected a string.'));
|
|
436
|
-
}
|
|
437
|
-
if (input['external_id'] != null && typeof input['external_id'] !== 'string') {
|
|
438
|
-
issues.push(issue('external_id', 'Expected a string.'));
|
|
439
|
-
}
|
|
440
|
-
if (input['owner_role'] != null && !isLookupReference(input['owner_role'])) {
|
|
441
|
-
issues.push(issue('owner_role', 'Expected a record id string or { id } object.'));
|
|
442
|
-
}
|
|
443
|
-
|
|
444
|
-
for (const name of names) {
|
|
445
|
-
const field = fields[name]!;
|
|
446
|
-
const hasValue = Object.prototype.hasOwnProperty.call(input, name);
|
|
447
|
-
|
|
448
|
-
if (mode === 'create' && field.writable && field.required && !hasValue) {
|
|
449
|
-
issues.push(issue(name, 'Missing required field.'));
|
|
450
|
-
continue;
|
|
451
|
-
}
|
|
452
|
-
if (!hasValue) continue;
|
|
453
|
-
|
|
454
|
-
const value = input[name];
|
|
455
|
-
if (!field.writable) {
|
|
456
|
-
issues.push(issue(name, 'Read-only fields cannot be written.'));
|
|
457
|
-
continue;
|
|
458
|
-
}
|
|
459
|
-
issues.push(...validateFieldValue(field, value, name));
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
for (const key of Object.keys(input)) {
|
|
463
|
-
if (key === 'name' || key === 'external_id' || key === 'owner_role') continue;
|
|
464
|
-
if (!knownFieldNames.has(key as FieldNames<TDefinition>)) {
|
|
465
|
-
issues.push(issue(key, 'Unknown field.'));
|
|
466
|
-
continue;
|
|
467
|
-
}
|
|
468
|
-
if (!writableFieldNames.has(key)) {
|
|
469
|
-
issues.push(issue(key, 'Read-only fields cannot be written.'));
|
|
470
|
-
}
|
|
471
|
-
}
|
|
472
|
-
|
|
473
|
-
return issues;
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
/**
|
|
477
|
-
* Runtime validator for create payloads derived from a schema definition.
|
|
478
|
-
*
|
|
479
|
-
* @param definition - Object definition whose create payload is being validated.
|
|
480
|
-
* @param input - Candidate create payload expressed with logical field names.
|
|
481
|
-
* @returns Aggregate validation result for the supplied create payload.
|
|
482
|
-
* @typeParam TDefinition - Object definition whose create payload is being validated.
|
|
483
|
-
*/
|
|
484
|
-
export function validateCreateInput<TDefinition extends AnyCustomObjectDefinition>(
|
|
485
|
-
definition: TDefinition,
|
|
486
|
-
input: Record<string, unknown>,
|
|
487
|
-
): ValidationResult {
|
|
488
|
-
const issues = validateRecordInputInternal(definition, input, 'create');
|
|
489
|
-
return { valid: issues.length === 0, issues };
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
/**
|
|
493
|
-
* Runtime validator for update payloads derived from a schema definition.
|
|
494
|
-
*
|
|
495
|
-
* @param definition - Object definition whose update payload is being validated.
|
|
496
|
-
* @param input - Candidate update payload expressed with logical field names.
|
|
497
|
-
* @returns Aggregate validation result for the supplied update payload.
|
|
498
|
-
* @typeParam TDefinition - Object definition whose update payload is being validated.
|
|
499
|
-
*/
|
|
500
|
-
export function validateUpdateInput<TDefinition extends AnyCustomObjectDefinition>(
|
|
501
|
-
definition: TDefinition,
|
|
502
|
-
input: Record<string, unknown>,
|
|
503
|
-
): ValidationResult {
|
|
504
|
-
const issues = validateRecordInputInternal(definition, input, 'update');
|
|
505
|
-
return { valid: issues.length === 0, issues };
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
/**
|
|
509
|
-
* Throws a detailed error when a create payload is invalid.
|
|
510
|
-
*
|
|
511
|
-
* @param definition - Object definition whose create payload is being asserted.
|
|
512
|
-
* @param input - Candidate create payload expressed with logical field names.
|
|
513
|
-
* @typeParam TDefinition - Object definition whose create payload is being asserted.
|
|
514
|
-
*/
|
|
515
|
-
export function assertValidCreateInput<TDefinition extends AnyCustomObjectDefinition>(
|
|
516
|
-
definition: TDefinition,
|
|
517
|
-
input: Record<string, unknown>,
|
|
518
|
-
): asserts input is CreateInput<TDefinition> {
|
|
519
|
-
const result = validateCreateInput(definition, input);
|
|
520
|
-
if (result.valid) return;
|
|
521
|
-
throw new Error(result.issues.map((current) => `${current.path}: ${current.message}`).join('\n'));
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
/**
|
|
525
|
-
* Throws a detailed error when an update payload is invalid.
|
|
526
|
-
*
|
|
527
|
-
* @param definition - Object definition whose update payload is being asserted.
|
|
528
|
-
* @param input - Candidate update payload expressed with logical field names.
|
|
529
|
-
* @typeParam TDefinition - Object definition whose update payload is being asserted.
|
|
530
|
-
*/
|
|
531
|
-
export function assertValidUpdateInput<TDefinition extends AnyCustomObjectDefinition>(
|
|
532
|
-
definition: TDefinition,
|
|
533
|
-
input: Record<string, unknown>,
|
|
534
|
-
): asserts input is UpdateInput<TDefinition> {
|
|
535
|
-
const result = validateUpdateInput(definition, input);
|
|
536
|
-
if (result.valid) return;
|
|
537
|
-
throw new Error(result.issues.map((current) => `${current.path}: ${current.message}`).join('\n'));
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
/**
|
|
541
|
-
* Casts a generic record object into the strongly typed shape derived from the
|
|
542
|
-
* schema.
|
|
543
|
-
*
|
|
544
|
-
* This helper does not perform runtime validation by itself; pair it with a
|
|
545
|
-
* schema-aware check when the source data is not trusted.
|
|
546
|
-
*
|
|
547
|
-
* @param _definition - Object definition whose record shape should be applied.
|
|
548
|
-
* @param record - Generic record object to cast into the typed record shape.
|
|
549
|
-
* @returns Record cast to the schema-derived typed record shape.
|
|
550
|
-
* @typeParam TDefinition - Object definition whose record shape should be applied.
|
|
551
|
-
*/
|
|
552
|
-
export function asTypedRecord<TDefinition extends AnyCustomObjectDefinition>(
|
|
553
|
-
_definition: TDefinition,
|
|
554
|
-
record: Record<string, unknown>,
|
|
555
|
-
): RecordShape<TDefinition> {
|
|
556
|
-
return record as RecordShape<TDefinition>;
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
/**
|
|
560
|
-
* Convenience helper for pulling a known field value out of a typed record.
|
|
561
|
-
*
|
|
562
|
-
* @param record - Typed record to read from.
|
|
563
|
-
* @param fieldName - Logical field name to read.
|
|
564
|
-
* @returns Typed value stored at the requested field name.
|
|
565
|
-
* @typeParam TDefinition - Object definition that produced the record shape.
|
|
566
|
-
* @typeParam TFieldName - Logical field name being read from the record.
|
|
567
|
-
*/
|
|
568
|
-
export function getRecordValue<
|
|
569
|
-
TDefinition extends AnyCustomObjectDefinition,
|
|
570
|
-
TFieldName extends FieldNames<TDefinition>,
|
|
571
|
-
>(record: RecordShape<TDefinition>, fieldName: TFieldName): RecordShape<TDefinition>[TFieldName] {
|
|
572
|
-
return record[fieldName];
|
|
573
|
-
}
|