@memberjunction/core-actions 6.2.0-edge.2 → 6.2.0-edge.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/custom/ai/execute-agent.action.d.ts +10 -0
- package/dist/custom/ai/execute-agent.action.d.ts.map +1 -1
- package/dist/custom/ai/execute-agent.action.js +24 -1
- package/dist/custom/ai/execute-agent.action.js.map +1 -1
- package/dist/custom/ai/execute-ai-prompt.action.d.ts.map +1 -1
- package/dist/custom/ai/execute-ai-prompt.action.js +3 -0
- package/dist/custom/ai/execute-ai-prompt.action.js.map +1 -1
- package/dist/custom/ai/generate-image.action.d.ts +5 -2
- package/dist/custom/ai/generate-image.action.d.ts.map +1 -1
- package/dist/custom/ai/generate-image.action.js +18 -8
- package/dist/custom/ai/generate-image.action.js.map +1 -1
- package/dist/custom/ai/summarize-content.action.d.ts.map +1 -1
- package/dist/custom/ai/summarize-content.action.js +3 -0
- package/dist/custom/ai/summarize-content.action.js.map +1 -1
- package/dist/custom/data/run-adhoc-query.action.d.ts.map +1 -1
- package/dist/custom/data/run-adhoc-query.action.js +3 -0
- package/dist/custom/data/run-adhoc-query.action.js.map +1 -1
- package/dist/custom/interactive-forms/_shared.d.ts +113 -31
- package/dist/custom/interactive-forms/_shared.d.ts.map +1 -1
- package/dist/custom/interactive-forms/_shared.js +198 -74
- package/dist/custom/interactive-forms/_shared.js.map +1 -1
- package/dist/custom/interactive-forms/activate-form-contribution-version.action.d.ts +23 -0
- package/dist/custom/interactive-forms/activate-form-contribution-version.action.d.ts.map +1 -0
- package/dist/custom/interactive-forms/activate-form-contribution-version.action.js +151 -0
- package/dist/custom/interactive-forms/activate-form-contribution-version.action.js.map +1 -0
- package/dist/custom/interactive-forms/activate-interactive-form-version.action.d.ts +23 -7
- package/dist/custom/interactive-forms/activate-interactive-form-version.action.d.ts.map +1 -1
- package/dist/custom/interactive-forms/activate-interactive-form-version.action.js +114 -58
- package/dist/custom/interactive-forms/activate-interactive-form-version.action.js.map +1 -1
- package/dist/custom/interactive-forms/create-form-contribution.action.d.ts +35 -0
- package/dist/custom/interactive-forms/create-form-contribution.action.d.ts.map +1 -0
- package/dist/custom/interactive-forms/create-form-contribution.action.js +149 -0
- package/dist/custom/interactive-forms/create-form-contribution.action.js.map +1 -0
- package/dist/custom/interactive-forms/get-active-form-for-entity.action.d.ts +3 -0
- package/dist/custom/interactive-forms/get-active-form-for-entity.action.d.ts.map +1 -1
- package/dist/custom/interactive-forms/get-active-form-for-entity.action.js +5 -1
- package/dist/custom/interactive-forms/get-active-form-for-entity.action.js.map +1 -1
- package/dist/custom/interactive-forms/get-form-composition-for-entity.action.d.ts +59 -0
- package/dist/custom/interactive-forms/get-form-composition-for-entity.action.d.ts.map +1 -0
- package/dist/custom/interactive-forms/get-form-composition-for-entity.action.js +313 -0
- package/dist/custom/interactive-forms/get-form-composition-for-entity.action.js.map +1 -0
- package/dist/custom/interactive-forms/get-form-contributions-for-entity.action.d.ts +47 -0
- package/dist/custom/interactive-forms/get-form-contributions-for-entity.action.d.ts.map +1 -0
- package/dist/custom/interactive-forms/get-form-contributions-for-entity.action.js +115 -0
- package/dist/custom/interactive-forms/get-form-contributions-for-entity.action.js.map +1 -0
- package/dist/custom/interactive-forms/index.d.ts +5 -0
- package/dist/custom/interactive-forms/index.d.ts.map +1 -1
- package/dist/custom/interactive-forms/index.js +5 -0
- package/dist/custom/interactive-forms/index.js.map +1 -1
- package/dist/custom/interactive-forms/modify-form-contribution.action.d.ts +36 -0
- package/dist/custom/interactive-forms/modify-form-contribution.action.d.ts.map +1 -0
- package/dist/custom/interactive-forms/modify-form-contribution.action.js +181 -0
- package/dist/custom/interactive-forms/modify-form-contribution.action.js.map +1 -0
- package/dist/custom/interactive-forms/modify-interactive-form.action.d.ts +13 -11
- package/dist/custom/interactive-forms/modify-interactive-form.action.d.ts.map +1 -1
- package/dist/custom/interactive-forms/modify-interactive-form.action.js +65 -64
- package/dist/custom/interactive-forms/modify-interactive-form.action.js.map +1 -1
- package/dist/custom/interactive-forms/revert-interactive-form.action.d.ts +12 -0
- package/dist/custom/interactive-forms/revert-interactive-form.action.d.ts.map +1 -1
- package/dist/custom/interactive-forms/revert-interactive-form.action.js +29 -4
- package/dist/custom/interactive-forms/revert-interactive-form.action.js.map +1 -1
- package/package.json +42 -42
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Shared helpers for the Interactive Forms action family. These
|
|
3
|
-
*
|
|
4
|
-
* lint the same spec shape, and need the
|
|
5
|
-
* extracting here keeps each action thin.
|
|
2
|
+
* Shared helpers for the Interactive Forms action family. These touch the
|
|
3
|
+
* same entities (`MJ: Components`, `MJ: Entity Form Overrides`,
|
|
4
|
+
* `MJ: Entity Form Contributions`), lint the same spec shape, and need the
|
|
5
|
+
* same parameter conventions — extracting here keeps each action thin.
|
|
6
6
|
*/
|
|
7
7
|
import { ActionResultSimple, RunActionParams } from "@memberjunction/actions-base";
|
|
8
|
-
import { IMetadataProvider, UserInfo } from "@memberjunction/core";
|
|
9
|
-
import { MJComponentEntity, MJEntityFormOverrideEntity } from "@memberjunction/core-entities";
|
|
8
|
+
import { IMetadataProvider, RunInEntityTransaction, UserInfo } from "@memberjunction/core";
|
|
9
|
+
import { type ContributionRowOptions, type FormScope, MJComponentEntity, MJEntityFormContributionEntity, MJEntityFormOverrideEntity } from "@memberjunction/core-entities";
|
|
10
10
|
import type { ComponentSpec } from "@memberjunction/interactive-component-types";
|
|
11
|
+
import { type NormalizedFormContributionSpec } from "@memberjunction/interactive-component-types/forms";
|
|
11
12
|
export declare function GetParam(params: RunActionParams, name: string): unknown;
|
|
12
13
|
/** @deprecated Use {@link GetParam}. */
|
|
13
14
|
export declare function getParam(params: RunActionParams, name: string): unknown;
|
|
@@ -15,6 +16,8 @@ export declare function GetStringParam(params: RunActionParams, name: string): s
|
|
|
15
16
|
/** @deprecated Use {@link GetStringParam}. */
|
|
16
17
|
export declare function getStringParam(params: RunActionParams, name: string): string | null;
|
|
17
18
|
export declare function GetNumberParam(params: RunActionParams, name: string): number | null;
|
|
19
|
+
/** The `Precedence` input as a whole number of zero or more, or null when absent or not one. */
|
|
20
|
+
export declare function GetPrecedenceParam(params: RunActionParams): number | null;
|
|
18
21
|
/** @deprecated Use {@link GetNumberParam}. */
|
|
19
22
|
export declare function getNumberParam(params: RunActionParams, name: string): number | null;
|
|
20
23
|
export declare function AddOutput(params: RunActionParams, name: string, value: unknown): void;
|
|
@@ -34,14 +37,10 @@ export declare function ParseSpecParam(raw: unknown): ComponentSpec | {
|
|
|
34
37
|
export declare function parseSpecParam(raw: unknown): ComponentSpec | {
|
|
35
38
|
error: string;
|
|
36
39
|
};
|
|
37
|
-
/**
|
|
38
|
-
* Lint a form-role ComponentSpec. Returns null on success, a fail-fast
|
|
39
|
-
* ActionResultSimple on failure. Two rules are suppressed for form-role
|
|
40
|
-
* specs (component-props-validation, callback-event-validation) — see
|
|
41
|
-
* lint comments in the original create-interactive-form action for the
|
|
42
|
-
* rationale.
|
|
43
|
-
*/
|
|
40
|
+
/** Lint a whole-form spec — `componentRole: 'form'`. */
|
|
44
41
|
export declare function LintFormSpec(spec: ComponentSpec, contextUser: UserInfo): Promise<ActionResultSimple | null>;
|
|
42
|
+
/** Lint a form-panel spec — `componentRole: 'form-panel'`. Same rules as {@link LintFormSpec}. */
|
|
43
|
+
export declare function LintFormPanelSpec(spec: ComponentSpec, contextUser: UserInfo): Promise<ActionResultSimple | null>;
|
|
45
44
|
/** @deprecated Use {@link LintFormSpec}. */
|
|
46
45
|
export declare function lintFormSpec(spec: ComponentSpec, contextUser: UserInfo): Promise<ActionResultSimple | null>;
|
|
47
46
|
/** Load a Component entity object by primary key. */
|
|
@@ -52,26 +51,57 @@ export declare function loadComponent(provider: IMetadataProvider, user: UserInf
|
|
|
52
51
|
export declare function LoadOverride(provider: IMetadataProvider, user: UserInfo, overrideID: string): Promise<MJEntityFormOverrideEntity | null>;
|
|
53
52
|
/** @deprecated Use {@link LoadOverride}. */
|
|
54
53
|
export declare function loadOverride(provider: IMetadataProvider, user: UserInfo, overrideID: string): Promise<MJEntityFormOverrideEntity | null>;
|
|
54
|
+
/** Load a Contribution entity object by primary key. */
|
|
55
|
+
export declare function LoadContribution(provider: IMetadataProvider, user: UserInfo, contributionID: string): Promise<MJEntityFormContributionEntity | null>;
|
|
55
56
|
/**
|
|
56
|
-
*
|
|
57
|
-
*
|
|
57
|
+
* Filter for the `MJ: Entity Form Contributions` rows a caller may see: their own
|
|
58
|
+
* User-scope rows, their roles' rows, and Global rows.
|
|
58
59
|
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
|
|
63
|
-
|
|
60
|
+
* Shared so every reader applies the same visibility. Two readers that disagree would
|
|
61
|
+
* show a contribution in one place and hide it in another, and the apply flow's
|
|
62
|
+
* duplicate check would miss a row the form is already rendering.
|
|
63
|
+
*/
|
|
64
|
+
export declare function ContributionScopeFilter(entityID: string, user: NonNullable<RunActionParams['ContextUser']>): string;
|
|
65
|
+
/**
|
|
66
|
+
* Whether metadata contributions are on for a server answer.
|
|
64
67
|
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
68
|
+
* Off when the Node switch is off (`MJ_FORMS_METADATA_CONTRIBUTIONS=false`, read into
|
|
69
|
+
* `InteractiveFormsEngine.MetadataContributionsEnabled`), or when the instance configuration
|
|
70
|
+
* Explorer reads (`Forms.MetadataContributions.Enabled`) is `false`. The instance configuration
|
|
71
|
+
* comes from the cached `InstanceConfigEngine`, which is refreshed after a save in the same
|
|
72
|
+
* process or by cross-server cache invalidation, so setting it back to `true` turns the answer
|
|
73
|
+
* back on once the cache has the new value. When Instance Config cannot load, only the Node
|
|
74
|
+
* switch counts.
|
|
75
|
+
*/
|
|
76
|
+
export declare function MetadataContributionsOn(provider: IMetadataProvider, user: UserInfo): Promise<boolean>;
|
|
77
|
+
/** What {@link CheckPersonalWrite} reads from a form override or contribution row. */
|
|
78
|
+
export interface ScopedFormRow {
|
|
79
|
+
ID: string;
|
|
80
|
+
Scope: FormScope;
|
|
81
|
+
UserID: string | null;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Why an action may not change this form or panel, as a `FORBIDDEN` result, or null.
|
|
69
85
|
*
|
|
70
|
-
*
|
|
86
|
+
* Actions change the caller's own personal forms and panels only. A row whose Scope is not
|
|
87
|
+
* `User` is refused for every caller, a Manage Form Defaults holder included: shared forms and
|
|
88
|
+
* panels are managed by people, from Form Builder or the form's Manage drawer. A `User` row is
|
|
89
|
+
* then checked with `FormScopeWriteRefusal`, the rule the server entity applies on save, which
|
|
90
|
+
* refuses a row that belongs to someone else.
|
|
71
91
|
*/
|
|
72
|
-
export declare function
|
|
73
|
-
/**
|
|
74
|
-
export
|
|
92
|
+
export declare function CheckPersonalWrite(row: ScopedFormRow, user: UserInfo): ActionResultSimple | null;
|
|
93
|
+
/** The provider shape `RunInEntityTransaction` reads. */
|
|
94
|
+
export type TransactableProvider = Parameters<typeof RunInEntityTransaction>[0];
|
|
95
|
+
/**
|
|
96
|
+
* Runs `work` in one entity transaction. When `work` returns an `{ error }` result, every write
|
|
97
|
+
* it made is rolled back and that result is returned; anything `work` throws also rolls back and
|
|
98
|
+
* is rethrown. A provider without entity transactions runs `work` without one.
|
|
99
|
+
*/
|
|
100
|
+
export declare function WriteAtomically<T extends object>(provider: IMetadataProvider, work: () => Promise<T | {
|
|
101
|
+
error: ActionResultSimple;
|
|
102
|
+
}>): Promise<T | {
|
|
103
|
+
error: ActionResultSimple;
|
|
104
|
+
}>;
|
|
75
105
|
/**
|
|
76
106
|
* Lifecycle mapping. EntityFormOverride.Status uses
|
|
77
107
|
* 'Active' / 'Pending' / 'Inactive' (it's the resolver-facing union). The
|
|
@@ -108,6 +138,8 @@ export declare function InsertComponent(opts: {
|
|
|
108
138
|
version: string;
|
|
109
139
|
versionSequence: number;
|
|
110
140
|
componentStatus: FormLifecycle;
|
|
141
|
+
/** 'Form' for whole forms (default), 'Widget' for form panels. */
|
|
142
|
+
componentType?: 'Form' | 'Widget';
|
|
111
143
|
}): Promise<{
|
|
112
144
|
id: string;
|
|
113
145
|
} | {
|
|
@@ -123,15 +155,17 @@ export declare function insertComponent(opts: {
|
|
|
123
155
|
version: string;
|
|
124
156
|
versionSequence: number;
|
|
125
157
|
componentStatus: FormLifecycle;
|
|
158
|
+
/** 'Form' for whole forms (default), 'Widget' for form panels. */
|
|
159
|
+
componentType?: 'Form' | 'Widget';
|
|
126
160
|
}): Promise<{
|
|
127
161
|
id: string;
|
|
128
162
|
} | {
|
|
129
163
|
error: ActionResultSimple;
|
|
130
164
|
}>;
|
|
131
165
|
/**
|
|
132
|
-
* Insert a new EntityFormOverride row
|
|
133
|
-
*
|
|
134
|
-
*
|
|
166
|
+
* Insert a new EntityFormOverride row, always User-scoped to the caller. Actions
|
|
167
|
+
* write personal forms only; sharing a form with a role or everyone is done from
|
|
168
|
+
* Form Builder or the form's Manage drawer.
|
|
135
169
|
*/
|
|
136
170
|
export declare function InsertOverride(opts: {
|
|
137
171
|
provider: IMetadataProvider;
|
|
@@ -210,4 +244,52 @@ export declare function bumpVersion(current: string | null | undefined, kind: Ve
|
|
|
210
244
|
export declare function ParseVersionBumpKind(raw: unknown): VersionBumpKind | null;
|
|
211
245
|
/** @deprecated Use {@link ParseVersionBumpKind}. */
|
|
212
246
|
export declare function parseVersionBumpKind(raw: unknown): VersionBumpKind | null;
|
|
247
|
+
/**
|
|
248
|
+
* A form-panel spec's registration intent, with its related entity resolved and the key its
|
|
249
|
+
* row will carry derived and checked.
|
|
250
|
+
*/
|
|
251
|
+
export interface ContributionRegistration {
|
|
252
|
+
Contribution: NormalizedFormContributionSpec;
|
|
253
|
+
/** What {@link ApplyContributionSpecToRow} needs beyond the spec. */
|
|
254
|
+
RowOptions: ContributionRowOptions;
|
|
255
|
+
/** The key the row will carry, or null when nothing supplies one. */
|
|
256
|
+
WriteKey: string | null;
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Reads the `formContribution` block of a linted form-panel spec, resolves the related entity it
|
|
260
|
+
* claims to its registered name and ID, and derives the row's key with
|
|
261
|
+
* `ResolveContributionWriteKey`, seeding a panel key from `spec.name`.
|
|
262
|
+
*
|
|
263
|
+
* Fails with `LINT_FAILED` when the block cannot be read, `RELATED_ENTITY_NOT_FOUND` when the
|
|
264
|
+
* related entity is not registered, `INVALID_CLAIM` when the row the spec maps to breaks a claim
|
|
265
|
+
* rule the database enforces (`ContributionClaimRefusal`), and `INVALID_CONTRIBUTION_KEY` when the
|
|
266
|
+
* key does not match `CONTRIBUTION_KEY_PATTERN`. Callers run it before any write.
|
|
267
|
+
*
|
|
268
|
+
* @param currentKey The key of the row being modified; omit for a new row. When the spec names
|
|
269
|
+
* no key and claims no grid, a panel key here is kept, so renaming the component does not change
|
|
270
|
+
* the contribution's identity.
|
|
271
|
+
*/
|
|
272
|
+
export declare function ResolveContributionRegistration(provider: IMetadataProvider, spec: ComponentSpec, currentKey?: string | null): ContributionRegistration | {
|
|
273
|
+
error: ActionResultSimple;
|
|
274
|
+
};
|
|
275
|
+
/**
|
|
276
|
+
* Insert a new EntityFormContribution row. Always User-scoped — promotion to Role or
|
|
277
|
+
* Global is a deliberate human act, not something an agent write path can reach.
|
|
278
|
+
*/
|
|
279
|
+
export declare function InsertContribution(opts: {
|
|
280
|
+
provider: IMetadataProvider;
|
|
281
|
+
user: UserInfo;
|
|
282
|
+
entityID: string;
|
|
283
|
+
componentID: string;
|
|
284
|
+
name: string;
|
|
285
|
+
description: string | null;
|
|
286
|
+
notes?: string | null;
|
|
287
|
+
registration: ContributionRegistration;
|
|
288
|
+
status: 'Active' | 'Pending';
|
|
289
|
+
precedence: number;
|
|
290
|
+
}): Promise<{
|
|
291
|
+
id: string;
|
|
292
|
+
} | {
|
|
293
|
+
error: ActionResultSimple;
|
|
294
|
+
}>;
|
|
213
295
|
//# sourceMappingURL=_shared.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"_shared.d.ts","sourceRoot":"","sources":["../../../src/custom/interactive-forms/_shared.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACnF,OAAO,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"_shared.d.ts","sourceRoot":"","sources":["../../../src/custom/interactive-forms/_shared.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AACnF,OAAO,EAAE,iBAAiB,EAAY,sBAAsB,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAErG,OAAO,EAIH,KAAK,sBAAsB,EAG3B,KAAK,SAAS,EAGd,iBAAiB,EACjB,8BAA8B,EAC9B,0BAA0B,EAC7B,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6CAA6C,CAAC;AACjF,OAAO,EAOH,KAAK,8BAA8B,EACtC,MAAM,mDAAmD,CAAC;AAK3D,wBAAgB,QAAQ,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAIvE;AAED,wCAAwC;AACxC,wBAAgB,QAAQ,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAEvE;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKnF;AAED,8CAA8C;AAC9C,wBAAgB,cAAc,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEnF;AAED,wBAAgB,cAAc,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAKnF;AAED,gGAAgG;AAChG,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,eAAe,GAAG,MAAM,GAAG,IAAI,CAGzE;AAED,8CAA8C;AAC9C,wBAAgB,cAAc,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAEnF;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAErF;AAED,yCAAyC;AACzC,wBAAgB,SAAS,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAErF;AAID,wBAAgB,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,kBAAkB,CAE/E;AAED,uCAAuC;AACvC,wBAAgB,OAAO,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,kBAAkB,CAE/E;AAED,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB,CAE3D;AAED,uCAAuC;AACvC,wBAAgB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,kBAAkB,CAE3D;AAID,wEAAwE;AACxE,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,aAAa,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAS9E;AAED,8CAA8C;AAC9C,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,GAAG,aAAa,GAAG;IAAE,KAAK,EAAE,MAAM,CAAA;CAAE,CAE9E;AAwED,wDAAwD;AACxD,wBAAsB,YAAY,CAAC,IAAI,EAAE,aAAa,EAAE,WAAW,EAAE,QAAQ,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAEjH;AAED,kGAAkG;AAClG,wBAAsB,iBAAiB,CAAC,IAAI,EAAE,aAAa,EAAE,WAAW,EAAE,QAAQ,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAEtH;AAED,4CAA4C;AAC5C,wBAAsB,YAAY,CAAC,IAAI,EAAE,aAAa,EAAE,WAAW,EAAE,QAAQ,GAAG,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAEjH;AAID,qDAAqD;AACrD,wBAAsB,aAAa,CAC/B,QAAQ,EAAE,iBAAiB,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,GACjE,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAInC;AAED,6CAA6C;AAC7C,wBAAsB,aAAa,CAC/B,QAAQ,EAAE,iBAAiB,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,MAAM,GACjE,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC,CAEnC;AAED,qDAAqD;AACrD,wBAAsB,YAAY,CAC9B,QAAQ,EAAE,iBAAiB,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,GAChE,OAAO,CAAC,0BAA0B,GAAG,IAAI,CAAC,CAI5C;AAED,4CAA4C;AAC5C,wBAAsB,YAAY,CAC9B,QAAQ,EAAE,iBAAiB,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,GAChE,OAAO,CAAC,0BAA0B,GAAG,IAAI,CAAC,CAE5C;AAED,wDAAwD;AACxD,wBAAsB,gBAAgB,CAClC,QAAQ,EAAE,iBAAiB,EAAE,IAAI,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,GACpE,OAAO,CAAC,8BAA8B,GAAG,IAAI,CAAC,CAIhD;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,CAAC,eAAe,CAAC,aAAa,CAAC,CAAC,GAAG,MAAM,CAOnH;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,uBAAuB,CAAC,QAAQ,EAAE,iBAAiB,EAAE,IAAI,EAAE,QAAQ,GAAG,OAAO,CAAC,OAAO,CAAC,CAU3G;AAED,sFAAsF;AACtF,MAAM,WAAW,aAAa;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,SAAS,CAAC;IACjB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED;;;;;;;;GAQG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,aAAa,EAAE,IAAI,EAAE,QAAQ,GAAG,kBAAkB,GAAG,IAAI,CAehG;AAED,yDAAyD;AACzD,MAAM,MAAM,oBAAoB,GAAG,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC;AAShF;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,CAAC,SAAS,MAAM,EAClD,QAAQ,EAAE,iBAAiB,EAC3B,IAAI,EAAE,MAAM,OAAO,CAAC,CAAC,GAAG;IAAE,KAAK,EAAE,kBAAkB,CAAA;CAAE,CAAC,GACvD,OAAO,CAAC,CAAC,GAAG;IAAE,KAAK,EAAE,kBAAkB,CAAA;CAAE,CAAC,CAW5C;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,SAAS,GAAG,UAAU,CAAC;AAC9D,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,aAAa,GAAG,WAAW,GAAG,OAAO,GAAG,YAAY,CAEnG;AAED,oDAAoD;AACpD,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,aAAa,GAAG,WAAW,GAAG,OAAO,GAAG,YAAY,CAEnG;AACD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,aAAa,CAOvF;AAED,sDAAsD;AACtD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,aAAa,CAEvF;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE;IACxC,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,aAAa,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,aAAa,CAAC;IAC/B,kEAAkE;IAClE,aAAa,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;CACrC,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,kBAAkB,CAAA;CAAE,CAAC,CAqB1D;AAED,+CAA+C;AAC/C,wBAAsB,eAAe,CAAC,IAAI,EAAE;IACxC,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,IAAI,EAAE,QAAQ,CAAC;IACf,IAAI,EAAE,aAAa,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,aAAa,CAAC;IAC/B,kEAAkE;IAClE,aAAa,CAAC,EAAE,MAAM,GAAG,QAAQ,CAAC;CACrC,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,kBAAkB,CAAA;CAAE,CAAC,CAE1D;AAED;;;;GAIG;AACH,wBAAsB,cAAc,CAAC,IAAI,EAAE;IACvC,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,kBAAkB,CAAA;CAAE,CAAC,CA2B1D;AAED,8CAA8C;AAC9C,wBAAsB,cAAc,CAAC,IAAI,EAAE;IACvC,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC7B,QAAQ,CAAC,EAAE,MAAM,CAAC;CACrB,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,kBAAkB,CAAA;CAAE,CAAC,CAE1D;AAED;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,OAAO,CAAC;AAavE;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAG3E;AAED,gDAAgD;AAChD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAE3E;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAG3E;AAED,gDAAgD;AAChD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAE3E;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAG3E;AAED,gDAAgD;AAChD,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAE3E;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,IAAI,EAAE,eAAe,GAAG,MAAM,CAO7F;AAED,2CAA2C;AAC3C,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAAE,IAAI,EAAE,eAAe,GAAG,MAAM,CAE7F;AAED;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,eAAe,GAAG,IAAI,CASzE;AAED,oDAAoD;AACpD,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,OAAO,GAAG,eAAe,GAAG,IAAI,CAEzE;AAID;;;GAGG;AACH,MAAM,WAAW,wBAAwB;IACrC,YAAY,EAAE,8BAA8B,CAAC;IAC7C,qEAAqE;IACrE,UAAU,EAAE,sBAAsB,CAAC;IACnC,qEAAqE;IACrE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,+BAA+B,CAC3C,QAAQ,EAAE,iBAAiB,EAC3B,IAAI,EAAE,aAAa,EACnB,UAAU,GAAE,MAAM,GAAG,IAAW,GACjC,wBAAwB,GAAG;IAAE,KAAK,EAAE,kBAAkB,CAAA;CAAE,CA8B1D;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC3C,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,IAAI,EAAE,QAAQ,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,YAAY,EAAE,wBAAwB,CAAC;IACvC,MAAM,EAAE,QAAQ,GAAG,SAAS,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;CACtB,GAAG,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,KAAK,EAAE,kBAAkB,CAAA;CAAE,CAAC,CA0B1D"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { LogError, RunInEntityTransaction } from "@memberjunction/core";
|
|
2
|
+
import { EscapeSQLString } from "@memberjunction/global";
|
|
3
|
+
import { ApplyContributionSpecToRow, ContributionClaimRefusal, ContributionSpecColumns, FormLifecycleComponentStatus, FormScopeWriteRefusal, InstanceConfigEngine, InteractiveFormsEngine, } from "@memberjunction/core-entities";
|
|
4
|
+
import { CONTRIBUTION_KEY_PATTERN, GetDeclaredFormContribution, IsFormPanelRole, isFormRole, IsPanelContributionKey, ResolveContributionWriteKey, } from "@memberjunction/interactive-component-types/forms";
|
|
3
5
|
import { ComponentLinter } from "@memberjunction/react-linter";
|
|
4
6
|
// ── parameter helpers ────────────────────────────────────────────────────
|
|
5
7
|
export function GetParam(params, name) {
|
|
@@ -28,6 +30,11 @@ export function GetNumberParam(params, name) {
|
|
|
28
30
|
const n = typeof v === 'number' ? v : Number(String(v));
|
|
29
31
|
return Number.isFinite(n) ? n : null;
|
|
30
32
|
}
|
|
33
|
+
/** The `Precedence` input as a whole number of zero or more, or null when absent or not one. */
|
|
34
|
+
export function GetPrecedenceParam(params) {
|
|
35
|
+
const precedence = GetNumberParam(params, "Precedence");
|
|
36
|
+
return precedence != null && precedence >= 0 ? Math.floor(precedence) : null;
|
|
37
|
+
}
|
|
31
38
|
/** @deprecated Use {@link GetNumberParam}. */
|
|
32
39
|
export function getNumberParam(params, name) {
|
|
33
40
|
return GetNumberParam(params, name);
|
|
@@ -72,15 +79,24 @@ export function parseSpecParam(raw) {
|
|
|
72
79
|
return ParseSpecParam(raw);
|
|
73
80
|
}
|
|
74
81
|
/**
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
* rationale.
|
|
82
|
+
* Rules suppressed for both form roles. The form host (mj-react-component) creates
|
|
83
|
+
* `callbacks` / `utilities` / `components` / `styles` once per instance (stable refs),
|
|
84
|
+
* so depending on them in useEffect does not loop here; the prop and callback shape
|
|
85
|
+
* rules only know the generic component surface, not the host-provided one.
|
|
80
86
|
*/
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
87
|
+
const ROLE_SUPPRESSED_RULES = new Set([
|
|
88
|
+
"component-props-validation",
|
|
89
|
+
"callback-event-validation",
|
|
90
|
+
"useeffect-unstable-dependencies",
|
|
91
|
+
]);
|
|
92
|
+
/**
|
|
93
|
+
* Lint body shared by both roles. `roleCheck` decides which role the spec must
|
|
94
|
+
* declare; `roleLabel` names it in the failure message. Returns null on success,
|
|
95
|
+
* a fail-fast ActionResultSimple on failure.
|
|
96
|
+
*/
|
|
97
|
+
async function lintRoleSpec(spec, contextUser, roleCheck, roleLabel) {
|
|
98
|
+
if (!roleCheck(spec)) {
|
|
99
|
+
return Failure("LINT_FAILED", `Spec must declare componentRole='${roleLabel}'. Got '${spec.componentRole ?? "(unset)"}'. The InteractiveForm runtime refuses to mount any other role.`);
|
|
84
100
|
}
|
|
85
101
|
if (!spec.name || spec.name.trim().length === 0) {
|
|
86
102
|
return Failure("LINT_FAILED", "Spec.name is required.");
|
|
@@ -91,22 +107,13 @@ export async function LintFormSpec(spec, contextUser) {
|
|
|
91
107
|
if (!spec.location) {
|
|
92
108
|
return Failure("LINT_FAILED", "Spec.location is required (use 'embedded' for inline JSX or 'registry' to reference a published component).");
|
|
93
109
|
}
|
|
94
|
-
const FORM_ROLE_SUPPRESSED_RULES = new Set([
|
|
95
|
-
"component-props-validation",
|
|
96
|
-
"callback-event-validation",
|
|
97
|
-
// The form host (mj-react-component) creates `callbacks`/`utilities`/`components`/
|
|
98
|
-
// `styles` once per instance (stable refs), so depending on them in useEffect does
|
|
99
|
-
// not loop here. This rule only flags those host-provided prop names, making it a
|
|
100
|
-
// false positive on the form-role surface.
|
|
101
|
-
"useeffect-unstable-dependencies",
|
|
102
|
-
]);
|
|
103
110
|
try {
|
|
104
111
|
const result = await ComponentLinter.lintComponent(spec.code, spec.name, spec, true, contextUser);
|
|
105
112
|
const blocking = (result.violations ?? []).filter(v => {
|
|
106
113
|
const isBlocking = v.severity === "critical" || v.severity === "high";
|
|
107
114
|
if (!isBlocking)
|
|
108
115
|
return false;
|
|
109
|
-
if (v.rule &&
|
|
116
|
+
if (v.rule && ROLE_SUPPRESSED_RULES.has(v.rule))
|
|
110
117
|
return false;
|
|
111
118
|
return true;
|
|
112
119
|
});
|
|
@@ -123,6 +130,14 @@ export async function LintFormSpec(spec, contextUser) {
|
|
|
123
130
|
}
|
|
124
131
|
return null;
|
|
125
132
|
}
|
|
133
|
+
/** Lint a whole-form spec — `componentRole: 'form'`. */
|
|
134
|
+
export async function LintFormSpec(spec, contextUser) {
|
|
135
|
+
return lintRoleSpec(spec, contextUser, isFormRole, 'form');
|
|
136
|
+
}
|
|
137
|
+
/** Lint a form-panel spec — `componentRole: 'form-panel'`. Same rules as {@link LintFormSpec}. */
|
|
138
|
+
export async function LintFormPanelSpec(spec, contextUser) {
|
|
139
|
+
return lintRoleSpec(spec, contextUser, IsFormPanelRole, 'form-panel');
|
|
140
|
+
}
|
|
126
141
|
/** @deprecated Use {@link LintFormSpec}. */
|
|
127
142
|
export async function lintFormSpec(spec, contextUser) {
|
|
128
143
|
return LintFormSpec(spec, contextUser);
|
|
@@ -148,65 +163,105 @@ export async function LoadOverride(provider, user, overrideID) {
|
|
|
148
163
|
export async function loadOverride(provider, user, overrideID) {
|
|
149
164
|
return LoadOverride(provider, user, overrideID);
|
|
150
165
|
}
|
|
166
|
+
/** Load a Contribution entity object by primary key. */
|
|
167
|
+
export async function LoadContribution(provider, user, contributionID) {
|
|
168
|
+
const c = await provider.GetEntityObject("MJ: Entity Form Contributions", user);
|
|
169
|
+
const loaded = await c.Load(contributionID);
|
|
170
|
+
return loaded ? c : null;
|
|
171
|
+
}
|
|
151
172
|
/**
|
|
152
|
-
*
|
|
153
|
-
*
|
|
173
|
+
* Filter for the `MJ: Entity Form Contributions` rows a caller may see: their own
|
|
174
|
+
* User-scope rows, their roles' rows, and Global rows.
|
|
154
175
|
*
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
|
|
159
|
-
|
|
176
|
+
* Shared so every reader applies the same visibility. Two readers that disagree would
|
|
177
|
+
* show a contribution in one place and hide it in another, and the apply flow's
|
|
178
|
+
* duplicate check would miss a row the form is already rendering.
|
|
179
|
+
*/
|
|
180
|
+
export function ContributionScopeFilter(entityID, user) {
|
|
181
|
+
const roleIDs = (user.UserRoles ?? [])
|
|
182
|
+
.map(r => r.RoleID).filter((x) => !!x);
|
|
183
|
+
const roleClause = roleIDs.length > 0
|
|
184
|
+
? `(Scope='Role' AND RoleID IN (${roleIDs.map(id => `'${EscapeSQLString(id)}'`).join(',')}))`
|
|
185
|
+
: `(1=0)`;
|
|
186
|
+
return `EntityID='${EscapeSQLString(entityID)}' AND ((Scope='User' AND UserID='${EscapeSQLString(user.ID)}') OR ${roleClause} OR Scope='Global')`;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Whether metadata contributions are on for a server answer.
|
|
160
190
|
*
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
191
|
+
* Off when the Node switch is off (`MJ_FORMS_METADATA_CONTRIBUTIONS=false`, read into
|
|
192
|
+
* `InteractiveFormsEngine.MetadataContributionsEnabled`), or when the instance configuration
|
|
193
|
+
* Explorer reads (`Forms.MetadataContributions.Enabled`) is `false`. The instance configuration
|
|
194
|
+
* comes from the cached `InstanceConfigEngine`, which is refreshed after a save in the same
|
|
195
|
+
* process or by cross-server cache invalidation, so setting it back to `true` turns the answer
|
|
196
|
+
* back on once the cache has the new value. When Instance Config cannot load, only the Node
|
|
197
|
+
* switch counts.
|
|
198
|
+
*/
|
|
199
|
+
export async function MetadataContributionsOn(provider, user) {
|
|
200
|
+
if (!InteractiveFormsEngine.MetadataContributionsEnabled)
|
|
201
|
+
return false;
|
|
202
|
+
try {
|
|
203
|
+
const config = InstanceConfigEngine.Instance;
|
|
204
|
+
await config.Config(false, user, provider);
|
|
205
|
+
return config.GetBoolean(InteractiveFormsEngine.MetadataContributionsConfigKey, true);
|
|
206
|
+
}
|
|
207
|
+
catch (err) {
|
|
208
|
+
LogError(`Instance Config did not load, so form contributions stay on: ${err instanceof Error ? err.message : String(err)}`);
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Why an action may not change this form or panel, as a `FORBIDDEN` result, or null.
|
|
165
214
|
*
|
|
166
|
-
*
|
|
215
|
+
* Actions change the caller's own personal forms and panels only. A row whose Scope is not
|
|
216
|
+
* `User` is refused for every caller, a Manage Form Defaults holder included: shared forms and
|
|
217
|
+
* panels are managed by people, from Form Builder or the form's Manage drawer. A `User` row is
|
|
218
|
+
* then checked with `FormScopeWriteRefusal`, the rule the server entity applies on save, which
|
|
219
|
+
* refuses a row that belongs to someone else.
|
|
167
220
|
*/
|
|
168
|
-
export function
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
// generated entity field). Owners can manage Global overrides;
|
|
189
|
-
// everyone else is rejected.
|
|
190
|
-
const isOwner = (user.Type ?? '').toLowerCase() === 'owner';
|
|
191
|
-
if (!isOwner) {
|
|
192
|
-
return Failure("FORBIDDEN", `Override ${ID} is Global. Only Owner-type users can mutate Global overrides; promote / demote them via Component Studio with appropriate privileges.`);
|
|
193
|
-
}
|
|
194
|
-
return null;
|
|
195
|
-
}
|
|
196
|
-
default:
|
|
197
|
-
return Failure("FORBIDDEN", `Override ${ID} has an unrecognized Scope ('${override.Scope}').`);
|
|
221
|
+
export function CheckPersonalWrite(row, user) {
|
|
222
|
+
if (row.Scope !== 'User') {
|
|
223
|
+
return Failure("FORBIDDEN", `${row.ID} is shared (Scope '${row.Scope}'). Actions change only your own personal forms and panels; ` +
|
|
224
|
+
`shared ones are managed from Form Builder or the form's Manage drawer.`);
|
|
225
|
+
}
|
|
226
|
+
const refusal = FormScopeWriteRefusal({
|
|
227
|
+
Operation: 'update',
|
|
228
|
+
PriorScope: row.Scope, PriorUserID: row.UserID,
|
|
229
|
+
NextScope: row.Scope, NextUserID: row.UserID,
|
|
230
|
+
CallerID: user.ID,
|
|
231
|
+
// A User row needs no grant, so only the ownership half of the rule can refuse it.
|
|
232
|
+
CallerHoldsGrant: false,
|
|
233
|
+
});
|
|
234
|
+
return refusal ? Failure("FORBIDDEN", refusal) : null;
|
|
235
|
+
}
|
|
236
|
+
/** Carries a failed write step out of the transaction, so the transaction rolls back. */
|
|
237
|
+
class RolledBackWrite extends Error {
|
|
238
|
+
constructor(Outcome) {
|
|
239
|
+
super(Outcome.error.Message);
|
|
240
|
+
this.Outcome = Outcome;
|
|
198
241
|
}
|
|
199
242
|
}
|
|
200
|
-
/**
|
|
201
|
-
|
|
202
|
-
|
|
243
|
+
/**
|
|
244
|
+
* Runs `work` in one entity transaction. When `work` returns an `{ error }` result, every write
|
|
245
|
+
* it made is rolled back and that result is returned; anything `work` throws also rolls back and
|
|
246
|
+
* is rethrown. A provider without entity transactions runs `work` without one.
|
|
247
|
+
*/
|
|
248
|
+
export async function WriteAtomically(provider, work) {
|
|
249
|
+
try {
|
|
250
|
+
return await RunInEntityTransaction(provider, async () => {
|
|
251
|
+
const outcome = await work();
|
|
252
|
+
if ('error' in outcome)
|
|
253
|
+
throw new RolledBackWrite(outcome);
|
|
254
|
+
return outcome;
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
catch (err) {
|
|
258
|
+
if (err instanceof RolledBackWrite)
|
|
259
|
+
return err.Outcome;
|
|
260
|
+
throw err;
|
|
261
|
+
}
|
|
203
262
|
}
|
|
204
263
|
export function MapToComponentStatus(lifecycle) {
|
|
205
|
-
|
|
206
|
-
case 'Active': return 'Published';
|
|
207
|
-
case 'Pending': return 'Draft';
|
|
208
|
-
case 'Inactive': return 'Deprecated';
|
|
209
|
-
}
|
|
264
|
+
return FormLifecycleComponentStatus(lifecycle);
|
|
210
265
|
}
|
|
211
266
|
/** @deprecated Use {@link MapToComponentStatus}. */
|
|
212
267
|
export function mapToComponentStatus(lifecycle) {
|
|
@@ -242,7 +297,7 @@ export async function InsertComponent(opts) {
|
|
|
242
297
|
component.Name = spec.name ?? fallbackName;
|
|
243
298
|
component.Title = spec.title ?? fallbackName;
|
|
244
299
|
component.Description = description ?? spec.description ?? null;
|
|
245
|
-
component.Type = "Form";
|
|
300
|
+
component.Type = opts.componentType ?? "Form";
|
|
246
301
|
component.Status = MapToComponentStatus(componentStatus);
|
|
247
302
|
component.Version = version;
|
|
248
303
|
component.VersionSequence = versionSequence;
|
|
@@ -259,9 +314,9 @@ export async function insertComponent(opts) {
|
|
|
259
314
|
return InsertComponent(opts);
|
|
260
315
|
}
|
|
261
316
|
/**
|
|
262
|
-
* Insert a new EntityFormOverride row
|
|
263
|
-
*
|
|
264
|
-
*
|
|
317
|
+
* Insert a new EntityFormOverride row, always User-scoped to the caller. Actions
|
|
318
|
+
* write personal forms only; sharing a form with a role or everyone is done from
|
|
319
|
+
* Form Builder or the form's Manage drawer.
|
|
265
320
|
*/
|
|
266
321
|
export async function InsertOverride(opts) {
|
|
267
322
|
const { provider, user, entityID, componentID, name, description, notes, status, priority } = opts;
|
|
@@ -382,4 +437,73 @@ export function ParseVersionBumpKind(raw) {
|
|
|
382
437
|
export function parseVersionBumpKind(raw) {
|
|
383
438
|
return ParseVersionBumpKind(raw);
|
|
384
439
|
}
|
|
440
|
+
/**
|
|
441
|
+
* Reads the `formContribution` block of a linted form-panel spec, resolves the related entity it
|
|
442
|
+
* claims to its registered name and ID, and derives the row's key with
|
|
443
|
+
* `ResolveContributionWriteKey`, seeding a panel key from `spec.name`.
|
|
444
|
+
*
|
|
445
|
+
* Fails with `LINT_FAILED` when the block cannot be read, `RELATED_ENTITY_NOT_FOUND` when the
|
|
446
|
+
* related entity is not registered, `INVALID_CLAIM` when the row the spec maps to breaks a claim
|
|
447
|
+
* rule the database enforces (`ContributionClaimRefusal`), and `INVALID_CONTRIBUTION_KEY` when the
|
|
448
|
+
* key does not match `CONTRIBUTION_KEY_PATTERN`. Callers run it before any write.
|
|
449
|
+
*
|
|
450
|
+
* @param currentKey The key of the row being modified; omit for a new row. When the spec names
|
|
451
|
+
* no key and claims no grid, a panel key here is kept, so renaming the component does not change
|
|
452
|
+
* the contribution's identity.
|
|
453
|
+
*/
|
|
454
|
+
export function ResolveContributionRegistration(provider, spec, currentKey = null) {
|
|
455
|
+
const contribution = GetDeclaredFormContribution(spec);
|
|
456
|
+
if (!contribution)
|
|
457
|
+
return { error: Failure("LINT_FAILED", "Spec.formContribution could not be read.") };
|
|
458
|
+
let relatedEntityID = null;
|
|
459
|
+
if (contribution.relatedEntity) {
|
|
460
|
+
const related = provider.EntityByName(contribution.relatedEntity);
|
|
461
|
+
if (!related) {
|
|
462
|
+
return { error: Failure("RELATED_ENTITY_NOT_FOUND", `Related entity '${contribution.relatedEntity}' is not registered.`) };
|
|
463
|
+
}
|
|
464
|
+
relatedEntityID = related.ID;
|
|
465
|
+
// The registered casing keeps the derived key stable.
|
|
466
|
+
contribution.relatedEntity = related.Name;
|
|
467
|
+
}
|
|
468
|
+
const relatedEntityName = contribution.relatedEntity ?? null;
|
|
469
|
+
if (!contribution.contributionKey && !relatedEntityName && IsPanelContributionKey(currentKey)) {
|
|
470
|
+
contribution.contributionKey = currentKey;
|
|
471
|
+
}
|
|
472
|
+
const componentName = spec.name?.trim() || null;
|
|
473
|
+
const rowOptions = { relatedEntityID, relatedEntityName, componentName };
|
|
474
|
+
const claimRefusal = ContributionClaimRefusal(ContributionSpecColumns(contribution, rowOptions));
|
|
475
|
+
if (claimRefusal)
|
|
476
|
+
return { error: Failure("INVALID_CLAIM", claimRefusal) };
|
|
477
|
+
const writeKey = ResolveContributionWriteKey(contribution, relatedEntityName, componentName);
|
|
478
|
+
if (writeKey && !CONTRIBUTION_KEY_PATTERN.test(writeKey)) {
|
|
479
|
+
return { error: Failure("INVALID_CONTRIBUTION_KEY", `Contribution key '${writeKey}' must match ${CONTRIBUTION_KEY_PATTERN.source}.`) };
|
|
480
|
+
}
|
|
481
|
+
return { Contribution: contribution, RowOptions: rowOptions, WriteKey: writeKey };
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* Insert a new EntityFormContribution row. Always User-scoped — promotion to Role or
|
|
485
|
+
* Global is a deliberate human act, not something an agent write path can reach.
|
|
486
|
+
*/
|
|
487
|
+
export async function InsertContribution(opts) {
|
|
488
|
+
const { provider, user, entityID, componentID, name, description, notes, registration, status, precedence } = opts;
|
|
489
|
+
const row = await provider.GetEntityObject("MJ: Entity Form Contributions", user);
|
|
490
|
+
row.NewRecord();
|
|
491
|
+
row.EntityID = entityID;
|
|
492
|
+
row.ComponentID = componentID;
|
|
493
|
+
row.Name = name;
|
|
494
|
+
row.Description = description;
|
|
495
|
+
row.Notes = notes ?? null;
|
|
496
|
+
ApplyContributionSpecToRow(row, registration.Contribution, registration.RowOptions);
|
|
497
|
+
// Security clamp: agents write User scope only. Promotion is a human act.
|
|
498
|
+
row.Scope = "User";
|
|
499
|
+
row.UserID = user.ID;
|
|
500
|
+
row.RoleID = null;
|
|
501
|
+
row.Precedence = precedence;
|
|
502
|
+
row.Status = status;
|
|
503
|
+
const saved = await row.Save();
|
|
504
|
+
if (!saved) {
|
|
505
|
+
return { error: Failure("PERSIST_FAILED", `Contribution insert failed: ${row.LatestResult?.CompleteMessage ?? "unknown error"}`) };
|
|
506
|
+
}
|
|
507
|
+
return { id: row.ID };
|
|
508
|
+
}
|
|
385
509
|
//# sourceMappingURL=_shared.js.map
|