@konitif/workbench 0.285.4 → 0.285.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -0
- package/dist/application/experience/experienceLifecycle.d.ts +62 -0
- package/dist/application/experience/experienceLifecycle.js +115 -0
- package/dist/application/experience/experienceRepository.d.ts +41 -0
- package/dist/application/experience/experienceRepository.js +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/workspace-contracts.d.ts +2 -0
- package/dist/workspace-contracts.js +2 -0
- package/package.json +1 -1
- package/reference/catalog.json +26 -0
package/README.md
CHANGED
|
@@ -39,6 +39,12 @@ domain that specializes it. A product can reference an authored workspace
|
|
|
39
39
|
preset unchanged or declare an override with ordered transformation records;
|
|
40
40
|
admission always verifies the referenced canonical content.
|
|
41
41
|
|
|
42
|
+
Workbench also owns the generic Experience lifecycle: an immutable revision
|
|
43
|
+
opens into a draft bound to an explicit base, saving creates a new revision only
|
|
44
|
+
when that base is still the current head, and archive, restore and tombstone
|
|
45
|
+
transitions are explicit. The repository contract is storage-neutral; browser,
|
|
46
|
+
remote, tenant and product-admission policies remain host adapters.
|
|
47
|
+
|
|
42
48
|
## Quick start
|
|
43
49
|
|
|
44
50
|
```ts
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { type ExperienceIdentity, type ExperienceLifecycleStatus, type ExperienceRevisionIdentity, type WorkbenchExperience, type WorkbenchExperienceRevision, type WorkspaceExperiencePresetBinding } from "./experienceArtifacts.js";
|
|
2
|
+
export interface ExperienceDraftIdentity {
|
|
3
|
+
kind: 'experience-draft';
|
|
4
|
+
value: string;
|
|
5
|
+
}
|
|
6
|
+
export interface WorkbenchExperienceDraft<TSpecialization = unknown> {
|
|
7
|
+
id: ExperienceDraftIdentity;
|
|
8
|
+
experienceId: ExperienceIdentity;
|
|
9
|
+
baseRevisionId: ExperienceRevisionIdentity;
|
|
10
|
+
preset: WorkspaceExperiencePresetBinding;
|
|
11
|
+
specialization: TSpecialization;
|
|
12
|
+
}
|
|
13
|
+
export interface WorkbenchExperienceLifecycleValue<TMetadata = unknown, TSpecialization = unknown> {
|
|
14
|
+
experience: WorkbenchExperience<TMetadata>;
|
|
15
|
+
revision: WorkbenchExperienceRevision<TSpecialization>;
|
|
16
|
+
}
|
|
17
|
+
export type ExperienceLifecycleDiagnosticCode = 'experience.lifecycle.experience-mismatch' | 'experience.lifecycle.head-conflict' | 'experience.lifecycle.inactive' | 'experience.lifecycle.status-conflict';
|
|
18
|
+
export interface ExperienceLifecycleDiagnostic {
|
|
19
|
+
code: ExperienceLifecycleDiagnosticCode;
|
|
20
|
+
path: string;
|
|
21
|
+
message: string;
|
|
22
|
+
expected?: string;
|
|
23
|
+
actual?: string;
|
|
24
|
+
}
|
|
25
|
+
export type ExperienceLifecycleResult<T> = {
|
|
26
|
+
ok: true;
|
|
27
|
+
value: T;
|
|
28
|
+
diagnostics: [];
|
|
29
|
+
} | {
|
|
30
|
+
ok: false;
|
|
31
|
+
diagnostics: ExperienceLifecycleDiagnostic[];
|
|
32
|
+
};
|
|
33
|
+
export declare function defineExperienceDraftIdentity(value: string): ExperienceDraftIdentity;
|
|
34
|
+
export declare function createWorkbenchExperience<TMetadata, TSpecialization>(input: {
|
|
35
|
+
experienceId: ExperienceIdentity;
|
|
36
|
+
revisionId: ExperienceRevisionIdentity;
|
|
37
|
+
metadata: TMetadata;
|
|
38
|
+
preset: WorkspaceExperiencePresetBinding;
|
|
39
|
+
specialization: TSpecialization;
|
|
40
|
+
derivedFrom?: ExperienceRevisionIdentity | null;
|
|
41
|
+
}): WorkbenchExperienceLifecycleValue<TMetadata, TSpecialization>;
|
|
42
|
+
export declare function createWorkbenchExperienceDraft<TSpecialization>(input: {
|
|
43
|
+
draftId: ExperienceDraftIdentity;
|
|
44
|
+
revision: WorkbenchExperienceRevision<TSpecialization>;
|
|
45
|
+
}): WorkbenchExperienceDraft<TSpecialization>;
|
|
46
|
+
export declare function updateWorkbenchExperienceDraft<TSpecialization>(input: {
|
|
47
|
+
draft: WorkbenchExperienceDraft<TSpecialization>;
|
|
48
|
+
preset: WorkspaceExperiencePresetBinding;
|
|
49
|
+
specialization: TSpecialization;
|
|
50
|
+
}): WorkbenchExperienceDraft<TSpecialization>;
|
|
51
|
+
export declare function saveWorkbenchExperienceRevision<TMetadata, TSpecialization>(input: {
|
|
52
|
+
experience: WorkbenchExperience<TMetadata>;
|
|
53
|
+
draft: WorkbenchExperienceDraft<TSpecialization>;
|
|
54
|
+
expectedHeadRevisionId: ExperienceRevisionIdentity;
|
|
55
|
+
revisionId: ExperienceRevisionIdentity;
|
|
56
|
+
metadata?: TMetadata;
|
|
57
|
+
}): ExperienceLifecycleResult<WorkbenchExperienceLifecycleValue<TMetadata, TSpecialization>>;
|
|
58
|
+
export declare function transitionWorkbenchExperienceLifecycle<TMetadata>(input: {
|
|
59
|
+
experience: WorkbenchExperience<TMetadata>;
|
|
60
|
+
expected: Exclude<ExperienceLifecycleStatus, 'tombstoned'>;
|
|
61
|
+
next: ExperienceLifecycleStatus;
|
|
62
|
+
}): ExperienceLifecycleResult<WorkbenchExperience<TMetadata>>;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { defineExperienceIdentity, defineExperienceRevisionIdentity, } from "./experienceArtifacts.js";
|
|
2
|
+
export function defineExperienceDraftIdentity(value) {
|
|
3
|
+
return { kind: 'experience-draft', value: requireIdentity(value, 'Experience draft') };
|
|
4
|
+
}
|
|
5
|
+
export function createWorkbenchExperience(input) {
|
|
6
|
+
return freezeDetached({
|
|
7
|
+
experience: {
|
|
8
|
+
id: defineExperienceIdentity(input.experienceId.value),
|
|
9
|
+
headRevisionId: defineExperienceRevisionIdentity(input.revisionId.value),
|
|
10
|
+
lifecycle: 'active',
|
|
11
|
+
metadata: input.metadata,
|
|
12
|
+
},
|
|
13
|
+
revision: {
|
|
14
|
+
schema: 'konitif.workbench-experience',
|
|
15
|
+
schemaVersion: 1,
|
|
16
|
+
id: defineExperienceRevisionIdentity(input.revisionId.value),
|
|
17
|
+
experienceId: defineExperienceIdentity(input.experienceId.value),
|
|
18
|
+
parentRevisionId: null,
|
|
19
|
+
derivedFrom: input.derivedFrom ?? null,
|
|
20
|
+
preset: input.preset,
|
|
21
|
+
specialization: input.specialization,
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
export function createWorkbenchExperienceDraft(input) {
|
|
26
|
+
return freezeDetached({
|
|
27
|
+
id: defineExperienceDraftIdentity(input.draftId.value),
|
|
28
|
+
experienceId: input.revision.experienceId,
|
|
29
|
+
baseRevisionId: input.revision.id,
|
|
30
|
+
preset: input.revision.preset,
|
|
31
|
+
specialization: input.revision.specialization,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
export function updateWorkbenchExperienceDraft(input) {
|
|
35
|
+
return freezeDetached({
|
|
36
|
+
...input.draft,
|
|
37
|
+
preset: input.preset,
|
|
38
|
+
specialization: input.specialization,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
export function saveWorkbenchExperienceRevision(input) {
|
|
42
|
+
if (input.experience.lifecycle !== 'active') {
|
|
43
|
+
return refused('experience.lifecycle.inactive', '$.experience.lifecycle', 'Only an active Experience can receive a new revision.', 'active', input.experience.lifecycle);
|
|
44
|
+
}
|
|
45
|
+
if (input.draft.experienceId.value !== input.experience.id.value) {
|
|
46
|
+
return refused('experience.lifecycle.experience-mismatch', '$.draft.experienceId', 'The draft and Experience identities do not match.', input.experience.id.value, input.draft.experienceId.value);
|
|
47
|
+
}
|
|
48
|
+
const actualHead = input.experience.headRevisionId.value;
|
|
49
|
+
if (input.expectedHeadRevisionId.value !== actualHead
|
|
50
|
+
|| input.draft.baseRevisionId.value !== actualHead) {
|
|
51
|
+
return refused('experience.lifecycle.head-conflict', '$.expectedHeadRevisionId', 'The draft base is no longer the expected Experience head.', input.expectedHeadRevisionId.value, actualHead);
|
|
52
|
+
}
|
|
53
|
+
const revisionId = defineExperienceRevisionIdentity(input.revisionId.value);
|
|
54
|
+
return accepted({
|
|
55
|
+
experience: {
|
|
56
|
+
...input.experience,
|
|
57
|
+
headRevisionId: revisionId,
|
|
58
|
+
metadata: input.metadata ?? input.experience.metadata,
|
|
59
|
+
},
|
|
60
|
+
revision: {
|
|
61
|
+
schema: 'konitif.workbench-experience',
|
|
62
|
+
schemaVersion: 1,
|
|
63
|
+
id: revisionId,
|
|
64
|
+
experienceId: input.experience.id,
|
|
65
|
+
parentRevisionId: input.draft.baseRevisionId,
|
|
66
|
+
derivedFrom: null,
|
|
67
|
+
preset: input.draft.preset,
|
|
68
|
+
specialization: input.draft.specialization,
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
export function transitionWorkbenchExperienceLifecycle(input) {
|
|
73
|
+
if (input.experience.lifecycle === 'tombstoned') {
|
|
74
|
+
return refused('experience.lifecycle.inactive', '$.experience.lifecycle', 'A tombstoned Experience cannot transition again.', input.expected, input.experience.lifecycle);
|
|
75
|
+
}
|
|
76
|
+
if (input.experience.lifecycle === input.next)
|
|
77
|
+
return accepted(input.experience);
|
|
78
|
+
if (input.experience.lifecycle !== input.expected) {
|
|
79
|
+
return refused('experience.lifecycle.status-conflict', '$.expected', 'The Experience lifecycle no longer matches the expected status.', input.expected, input.experience.lifecycle);
|
|
80
|
+
}
|
|
81
|
+
if (!isAllowedTransition(input.experience.lifecycle, input.next)) {
|
|
82
|
+
return refused('experience.lifecycle.status-conflict', '$.next', `Experience lifecycle cannot transition from ${input.experience.lifecycle} to ${input.next}.`, undefined, input.next);
|
|
83
|
+
}
|
|
84
|
+
return accepted({ ...input.experience, lifecycle: input.next });
|
|
85
|
+
}
|
|
86
|
+
function isAllowedTransition(current, next) {
|
|
87
|
+
return next === 'tombstoned'
|
|
88
|
+
|| (current === 'active' && next === 'archived')
|
|
89
|
+
|| (current === 'archived' && next === 'active');
|
|
90
|
+
}
|
|
91
|
+
function requireIdentity(value, label) {
|
|
92
|
+
const normalized = value.trim();
|
|
93
|
+
if (!normalized)
|
|
94
|
+
throw new TypeError(`${label} identity is required.`);
|
|
95
|
+
return normalized;
|
|
96
|
+
}
|
|
97
|
+
function accepted(value) {
|
|
98
|
+
return { ok: true, value: freezeDetached(value), diagnostics: [] };
|
|
99
|
+
}
|
|
100
|
+
function refused(code, path, message, expected, actual) {
|
|
101
|
+
return {
|
|
102
|
+
ok: false,
|
|
103
|
+
diagnostics: [{ code, path, message, ...(expected ? { expected } : {}), ...(actual ? { actual } : {}) }],
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
function freezeDetached(value) {
|
|
107
|
+
return deepFreeze(structuredClone(value));
|
|
108
|
+
}
|
|
109
|
+
function deepFreeze(value) {
|
|
110
|
+
if (!value || typeof value !== 'object' || Object.isFrozen(value))
|
|
111
|
+
return value;
|
|
112
|
+
for (const nested of Object.values(value))
|
|
113
|
+
deepFreeze(nested);
|
|
114
|
+
return Object.freeze(value);
|
|
115
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { ExperienceIdentity, ExperienceLifecycleStatus, ExperienceRevisionIdentity, WorkbenchExperience, WorkbenchExperienceRevision } from "./experienceArtifacts.js";
|
|
2
|
+
import type { ExperienceLifecycleDiagnostic, WorkbenchExperienceDraft, WorkbenchExperienceLifecycleValue } from "./experienceLifecycle.js";
|
|
3
|
+
export interface ExperienceRevisionReference {
|
|
4
|
+
experienceId: ExperienceIdentity;
|
|
5
|
+
revisionId: ExperienceRevisionIdentity;
|
|
6
|
+
}
|
|
7
|
+
export interface WorkbenchExperienceRepositorySnapshot<TMetadata = unknown, TSpecialization = unknown> {
|
|
8
|
+
experiences: readonly WorkbenchExperience<TMetadata>[];
|
|
9
|
+
revisions: readonly WorkbenchExperienceRevision<TSpecialization>[];
|
|
10
|
+
}
|
|
11
|
+
export type WorkbenchExperienceRepositoryErrorCode = 'experience.repository.not-found' | 'experience.repository.already-exists' | 'experience.repository.head-conflict' | 'experience.repository.lifecycle-conflict' | 'experience.repository.invalid-artifact' | 'experience.repository.unavailable';
|
|
12
|
+
export interface WorkbenchExperienceRepositoryError {
|
|
13
|
+
code: WorkbenchExperienceRepositoryErrorCode;
|
|
14
|
+
message: string;
|
|
15
|
+
lifecycleDiagnostic?: ExperienceLifecycleDiagnostic;
|
|
16
|
+
}
|
|
17
|
+
export type WorkbenchExperienceRepositoryResult<T> = {
|
|
18
|
+
ok: true;
|
|
19
|
+
value: T;
|
|
20
|
+
} | {
|
|
21
|
+
ok: false;
|
|
22
|
+
error: WorkbenchExperienceRepositoryError;
|
|
23
|
+
};
|
|
24
|
+
export interface WorkbenchExperienceRepository<TMetadata = unknown, TSpecialization = unknown> {
|
|
25
|
+
hydrate(): Promise<WorkbenchExperienceRepositoryResult<WorkbenchExperienceRepositorySnapshot<TMetadata, TSpecialization>>>;
|
|
26
|
+
list(): Promise<WorkbenchExperienceRepositoryResult<readonly WorkbenchExperience<TMetadata>[]>>;
|
|
27
|
+
resolveRevision(reference: ExperienceRevisionReference): Promise<WorkbenchExperienceRepositoryResult<WorkbenchExperienceRevision<TSpecialization>>>;
|
|
28
|
+
create(value: WorkbenchExperienceLifecycleValue<TMetadata, TSpecialization>): Promise<WorkbenchExperienceRepositoryResult<WorkbenchExperienceLifecycleValue<TMetadata, TSpecialization>>>;
|
|
29
|
+
saveRevision(input: {
|
|
30
|
+
experience: WorkbenchExperience<TMetadata>;
|
|
31
|
+
draft: WorkbenchExperienceDraft<TSpecialization>;
|
|
32
|
+
expectedHeadRevisionId: ExperienceRevisionIdentity;
|
|
33
|
+
revisionId: ExperienceRevisionIdentity;
|
|
34
|
+
metadata?: TMetadata;
|
|
35
|
+
}): Promise<WorkbenchExperienceRepositoryResult<WorkbenchExperienceLifecycleValue<TMetadata, TSpecialization>>>;
|
|
36
|
+
transitionLifecycle(input: {
|
|
37
|
+
experienceId: ExperienceIdentity;
|
|
38
|
+
expected: Exclude<ExperienceLifecycleStatus, 'tombstoned'>;
|
|
39
|
+
next: ExperienceLifecycleStatus;
|
|
40
|
+
}): Promise<WorkbenchExperienceRepositoryResult<WorkbenchExperience<TMetadata>>>;
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -56,6 +56,8 @@ export * from "./application/runtime/RuntimeProjectionViewerSessionSync.js";
|
|
|
56
56
|
export * from "./application/experiment/ControlledExperimentRecorder.js";
|
|
57
57
|
export * from "./application/experiment/ControlledExperimentCampaign.js";
|
|
58
58
|
export * from "./application/experience/experienceArtifacts.js";
|
|
59
|
+
export * from "./application/experience/experienceLifecycle.js";
|
|
60
|
+
export * from "./application/experience/experienceRepository.js";
|
|
59
61
|
export * from "./application/repository/RepositoryPreflightEngine.js";
|
|
60
62
|
export * from "./domain/layout/model.js";
|
|
61
63
|
export * from "./domain/layout/interaction.js";
|
package/dist/index.js
CHANGED
|
@@ -56,6 +56,8 @@ export * from "./application/runtime/RuntimeProjectionViewerSessionSync.js";
|
|
|
56
56
|
export * from "./application/experiment/ControlledExperimentRecorder.js";
|
|
57
57
|
export * from "./application/experiment/ControlledExperimentCampaign.js";
|
|
58
58
|
export * from "./application/experience/experienceArtifacts.js";
|
|
59
|
+
export * from "./application/experience/experienceLifecycle.js";
|
|
60
|
+
export * from "./application/experience/experienceRepository.js";
|
|
59
61
|
export * from "./application/repository/RepositoryPreflightEngine.js";
|
|
60
62
|
export * from "./domain/layout/model.js";
|
|
61
63
|
export * from "./domain/layout/interaction.js";
|
|
@@ -40,6 +40,8 @@ export * from "./application/workspace/usageSnapshots.js";
|
|
|
40
40
|
export * from "./application/launch/workbenchLaunchPolicy.js";
|
|
41
41
|
export * from "./application/tool/experienceToolSelection.js";
|
|
42
42
|
export * from "./application/experience/experienceArtifacts.js";
|
|
43
|
+
export * from "./application/experience/experienceLifecycle.js";
|
|
44
|
+
export * from "./application/experience/experienceRepository.js";
|
|
43
45
|
export * from "./application/workspace/unassignTool.js";
|
|
44
46
|
export * from "./application/workspace/parkedTool.js";
|
|
45
47
|
export * from "./application/workspace/workspacePresetCatalogAdmission.js";
|
|
@@ -32,6 +32,8 @@ export * from "./application/workspace/usageSnapshots.js";
|
|
|
32
32
|
export * from "./application/launch/workbenchLaunchPolicy.js";
|
|
33
33
|
export * from "./application/tool/experienceToolSelection.js";
|
|
34
34
|
export * from "./application/experience/experienceArtifacts.js";
|
|
35
|
+
export * from "./application/experience/experienceLifecycle.js";
|
|
36
|
+
export * from "./application/experience/experienceRepository.js";
|
|
35
37
|
export * from "./application/workspace/unassignTool.js";
|
|
36
38
|
export * from "./application/workspace/parkedTool.js";
|
|
37
39
|
export * from "./application/workspace/workspacePresetCatalogAdmission.js";
|
package/package.json
CHANGED
package/reference/catalog.json
CHANGED
|
@@ -218,6 +218,32 @@
|
|
|
218
218
|
"application/shell/"
|
|
219
219
|
]
|
|
220
220
|
},
|
|
221
|
+
{
|
|
222
|
+
"id": "experience-application",
|
|
223
|
+
"title": "Experience lifecycle",
|
|
224
|
+
"summary": "Content-bound Experience revisions, drafts, lifecycle transitions, conflicts, and storage-neutral repository ports.",
|
|
225
|
+
"authority": "Reusable Experience identity, revision lineage, lifecycle and persistence boundary.",
|
|
226
|
+
"owns": [
|
|
227
|
+
"Experience drafts",
|
|
228
|
+
"head conflict detection",
|
|
229
|
+
"archive restore and tombstone transitions",
|
|
230
|
+
"repository ports"
|
|
231
|
+
],
|
|
232
|
+
"doesNotOwn": [
|
|
233
|
+
"product specialization",
|
|
234
|
+
"browser or remote storage",
|
|
235
|
+
"tenant authorization"
|
|
236
|
+
],
|
|
237
|
+
"workflow": [
|
|
238
|
+
"Draft",
|
|
239
|
+
"Save",
|
|
240
|
+
"Resolve",
|
|
241
|
+
"Transition"
|
|
242
|
+
],
|
|
243
|
+
"sourcePrefixes": [
|
|
244
|
+
"application/experience/"
|
|
245
|
+
]
|
|
246
|
+
},
|
|
221
247
|
{
|
|
222
248
|
"id": "runtime-application",
|
|
223
249
|
"title": "Runtime application",
|