@zeniai/client-epic-state 5.2.4 → 5.2.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.
Files changed (38) hide show
  1. package/lib/epic.js +12 -0
  2. package/lib/esm/epic.js +12 -0
  3. package/lib/esm/index.js +2 -2
  4. package/lib/esm/view/aiCfoView/aiCfoViewReducer.js +411 -9
  5. package/lib/esm/view/aiCfoView/aiCfoViewSelector.js +49 -0
  6. package/lib/esm/view/aiCfoView/epics/deleteSkillEpic.js +23 -0
  7. package/lib/esm/view/aiCfoView/epics/fetchSkillDetailEpic.js +35 -0
  8. package/lib/esm/view/aiCfoView/epics/fetchSkillVersionBodyEpic.js +45 -0
  9. package/lib/esm/view/aiCfoView/epics/fetchSkillVersionsEpic.js +37 -0
  10. package/lib/esm/view/aiCfoView/epics/fetchSkillsEpic.js +11 -6
  11. package/lib/esm/view/aiCfoView/epics/saveSkillEpic.js +72 -0
  12. package/lib/esm/view/aiCfoView/epics/setSkillVisibilityEpic.js +32 -0
  13. package/lib/esm/view/aiCfoView/epics/toSkillDetail.js +102 -0
  14. package/lib/index.d.ts +3 -3
  15. package/lib/index.js +26 -11
  16. package/lib/view/aiCfoView/aiCfoViewPayload.d.ts +85 -0
  17. package/lib/view/aiCfoView/aiCfoViewReducer.d.ts +67 -2
  18. package/lib/view/aiCfoView/aiCfoViewReducer.js +413 -10
  19. package/lib/view/aiCfoView/aiCfoViewSelector.d.ts +7 -1
  20. package/lib/view/aiCfoView/aiCfoViewSelector.js +54 -0
  21. package/lib/view/aiCfoView/aiCfoViewState.d.ts +143 -1
  22. package/lib/view/aiCfoView/epics/deleteSkillEpic.d.ts +8 -0
  23. package/lib/view/aiCfoView/epics/deleteSkillEpic.js +27 -0
  24. package/lib/view/aiCfoView/epics/fetchSkillDetailEpic.d.ts +8 -0
  25. package/lib/view/aiCfoView/epics/fetchSkillDetailEpic.js +39 -0
  26. package/lib/view/aiCfoView/epics/fetchSkillVersionBodyEpic.d.ts +13 -0
  27. package/lib/view/aiCfoView/epics/fetchSkillVersionBodyEpic.js +49 -0
  28. package/lib/view/aiCfoView/epics/fetchSkillVersionsEpic.d.ts +8 -0
  29. package/lib/view/aiCfoView/epics/fetchSkillVersionsEpic.js +41 -0
  30. package/lib/view/aiCfoView/epics/fetchSkillsEpic.js +11 -6
  31. package/lib/view/aiCfoView/epics/saveSkillEpic.d.ts +14 -0
  32. package/lib/view/aiCfoView/epics/saveSkillEpic.js +76 -0
  33. package/lib/view/aiCfoView/epics/setSkillVisibilityEpic.d.ts +14 -0
  34. package/lib/view/aiCfoView/epics/setSkillVisibilityEpic.js +36 -0
  35. package/lib/view/aiCfoView/epics/toSkillDetail.d.ts +88 -0
  36. package/lib/view/aiCfoView/epics/toSkillDetail.js +110 -0
  37. package/lib/view/taskManager/taskListView/taskList.d.ts +1 -1
  38. package/package.json +1 -1
@@ -0,0 +1,88 @@
1
+ import { SkillDetailDataPayload, SkillSummaryPayload } from '../aiCfoViewPayload';
2
+ import { Skill, SkillDetail, SkillVisibility } from '../aiCfoViewState';
3
+ /**
4
+ * A wire string narrowed to {@link SkillVisibility}, or null.
5
+ *
6
+ * A converter rather than passing the raw string through, because `Skill.visibility` is a
7
+ * union on the state type and this is the boundary where an unvalidated payload becomes
8
+ * it — the repo's rule about explicit converters at scalar boundaries instead of a bare
9
+ * cast.
10
+ *
11
+ * Unrecognised maps to NULL, not to `'private'`. Null already means "does not apply" for a
12
+ * Zeni default, and consumers show no audience badge for it — so an unknown tier renders
13
+ * as no badge. Defaulting to `'private'` would instead label a skill the whole tenant can
14
+ * run as author-only, which is a confident lie rather than a visible gap.
15
+ */
16
+ export declare const toSkillVisibility: (raw: string | null | undefined) => SkillVisibility | null;
17
+ /**
18
+ * What the version-body read needs, and nothing more.
19
+ *
20
+ * A `Pick` of the payload rather than a type of its own: `SkillDetailDataPayload` now
21
+ * marks the three unchecked fields optional itself, the same way `SkillSummaryPayload`
22
+ * does, so there is one idiom for "this endpoint is not schema-validated" instead of two.
23
+ */
24
+ type PromptBody = Pick<SkillDetailDataPayload, 'instructions'>;
25
+ /**
26
+ * Everything about a skill summary EXCEPT `exampleHints`, which no shared mapper can
27
+ * honestly supply.
28
+ *
29
+ * Only the LIST route returns `parameter_hints`; the detail and write routes do not. A
30
+ * shared mapper hardcoding `exampleHints: []` would put a "try: …" line under a skill
31
+ * that has hints, or drop them from the menu — so the field is `Omit`ted and each caller
32
+ * spreads in what its own route actually knows.
33
+ *
34
+ * Returning `Omit<Skill, 'exampleHints'>` rather than duplicating the mapper is what
35
+ * makes a new `Skill` field land in one place: this function stops compiling until the
36
+ * field is mapped, and both callers inherit it. The previous shape — two full copies —
37
+ * is how `isMine` was nearly added to only one of them, which would have silently
38
+ * withheld the edit and delete controls on whichever surface was missed.
39
+ */
40
+ export declare const toBaseSkill: (payload: SkillSummaryPayload) => Omit<Skill, "exampleHints">;
41
+ /**
42
+ * Whether a 2xx body carries a prompt.
43
+ *
44
+ * Narrows to {@link PromptBody} and NOT to the whole payload, because the only consumer —
45
+ * the version-body read — dereferences `instructions` and nothing else. Asserting the
46
+ * whole shape here would claim `skill` was checked when it was not, and tying the read to
47
+ * it would fail a response whose prompt was fine and whose `skill` block was malformed,
48
+ * which is exactly what happened when this was one predicate instead of two.
49
+ *
50
+ * Takes `unknown`: the input is an unvalidated network body, and typing the parameter as
51
+ * the payload interface would assume the very thing being checked.
52
+ */
53
+ export declare const hasPromptBody: (data: unknown) => data is PromptBody;
54
+ /**
55
+ * Whether a 2xx body can build a whole {@link SkillDetail}.
56
+ *
57
+ * Narrows to the payload type itself, which is now truthful: the two fields this checks
58
+ * are the two the interface still declares required, and the other three are optional
59
+ * there. Stricter than {@link hasPromptBody} by exactly one field. Four epics read this shape
60
+ * and each used to guard it differently — `data != null`, `data?.skill != null`, and a
61
+ * real `instructions` check — so one malformed response became a failure in one place, an
62
+ * empty prompt in another, and `undefined` in a third.
63
+ */
64
+ export declare const isSkillDetailPayload: (data: unknown) => data is SkillDetailDataPayload;
65
+ /**
66
+ * The detail payload every write route answers with.
67
+ *
68
+ * Shared because three epics need it — save, visibility, and the detail read — and a
69
+ * fourth copy is how one of them quietly stops carrying a field the others do.
70
+ *
71
+ * The coercions are not defensive habit; the payload type makes them mandatory — the
72
+ * three optional fields cannot be assigned to a non-optional `SkillDetail` without one.
73
+ * Which fields get coerced and which do not is the deliberate part:
74
+ *
75
+ * - `instructions` is load-bearing and already proven, so it passes through. Defaulting a
76
+ * missing prompt to `''` is the one thing this module exists to prevent: the user
77
+ * cannot tell an empty prompt from a skill that has one, and may save over the real
78
+ * thing.
79
+ * - `instructionsCharCount` falls back to the prompt's own length, NOT to 0. A 12,000
80
+ * character prompt displayed as "0 characters" is a lie the user can see, and the
81
+ * prompt is right here to measure. Approximate only for astral-plane characters, where
82
+ * UTF-16 units and the server's count differ — a rounding error, against a guaranteed
83
+ * wrong answer.
84
+ * - `artifact` and `artifactTitle` degrade to empty. They render a preview card, so
85
+ * losing them costs a card; failing the whole read would cost the user the skill.
86
+ */
87
+ export declare const toSkillDetail: (payload: SkillDetailDataPayload) => SkillDetail;
88
+ export {};
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toSkillDetail = exports.isSkillDetailPayload = exports.hasPromptBody = exports.toBaseSkill = exports.toSkillVisibility = void 0;
4
+ /**
5
+ * Every value `SkillVisibility` admits, as data so the converter below cannot drift from
6
+ * the type. Adding a tier to the union without adding it here is a compile error.
7
+ */
8
+ const SKILL_VISIBILITIES = ['private', 'tenant'];
9
+ /**
10
+ * A wire string narrowed to {@link SkillVisibility}, or null.
11
+ *
12
+ * A converter rather than passing the raw string through, because `Skill.visibility` is a
13
+ * union on the state type and this is the boundary where an unvalidated payload becomes
14
+ * it — the repo's rule about explicit converters at scalar boundaries instead of a bare
15
+ * cast.
16
+ *
17
+ * Unrecognised maps to NULL, not to `'private'`. Null already means "does not apply" for a
18
+ * Zeni default, and consumers show no audience badge for it — so an unknown tier renders
19
+ * as no badge. Defaulting to `'private'` would instead label a skill the whole tenant can
20
+ * run as author-only, which is a confident lie rather than a visible gap.
21
+ */
22
+ const toSkillVisibility = (raw) => SKILL_VISIBILITIES.find((known) => known === raw) ?? null;
23
+ exports.toSkillVisibility = toSkillVisibility;
24
+ /**
25
+ * Everything about a skill summary EXCEPT `exampleHints`, which no shared mapper can
26
+ * honestly supply.
27
+ *
28
+ * Only the LIST route returns `parameter_hints`; the detail and write routes do not. A
29
+ * shared mapper hardcoding `exampleHints: []` would put a "try: …" line under a skill
30
+ * that has hints, or drop them from the menu — so the field is `Omit`ted and each caller
31
+ * spreads in what its own route actually knows.
32
+ *
33
+ * Returning `Omit<Skill, 'exampleHints'>` rather than duplicating the mapper is what
34
+ * makes a new `Skill` field land in one place: this function stops compiling until the
35
+ * field is mapped, and both callers inherit it. The previous shape — two full copies —
36
+ * is how `isMine` was nearly added to only one of them, which would have silently
37
+ * withheld the edit and delete controls on whichever surface was missed.
38
+ */
39
+ const toBaseSkill = (payload) => ({
40
+ macroId: payload.macro_id,
41
+ name: payload.name,
42
+ displayName: payload.display_name,
43
+ description: payload.description,
44
+ semver: payload.semver,
45
+ hasScripts: payload.has_scripts === true,
46
+ // === true, so an absent field withholds the write controls rather than offering ones
47
+ // the API refuses.
48
+ isMine: payload.is_mine === true,
49
+ isOwnedByTenant: payload.is_owned_by_tenant === true,
50
+ updatedAt: payload.updated_at ?? null,
51
+ visibility: (0, exports.toSkillVisibility)(payload.visibility),
52
+ });
53
+ exports.toBaseSkill = toBaseSkill;
54
+ /**
55
+ * Whether a 2xx body carries a prompt.
56
+ *
57
+ * Narrows to {@link PromptBody} and NOT to the whole payload, because the only consumer —
58
+ * the version-body read — dereferences `instructions` and nothing else. Asserting the
59
+ * whole shape here would claim `skill` was checked when it was not, and tying the read to
60
+ * it would fail a response whose prompt was fine and whose `skill` block was malformed,
61
+ * which is exactly what happened when this was one predicate instead of two.
62
+ *
63
+ * Takes `unknown`: the input is an unvalidated network body, and typing the parameter as
64
+ * the payload interface would assume the very thing being checked.
65
+ */
66
+ const hasPromptBody = (data) => typeof data?.instructions ===
67
+ 'string';
68
+ exports.hasPromptBody = hasPromptBody;
69
+ /**
70
+ * Whether a 2xx body can build a whole {@link SkillDetail}.
71
+ *
72
+ * Narrows to the payload type itself, which is now truthful: the two fields this checks
73
+ * are the two the interface still declares required, and the other three are optional
74
+ * there. Stricter than {@link hasPromptBody} by exactly one field. Four epics read this shape
75
+ * and each used to guard it differently — `data != null`, `data?.skill != null`, and a
76
+ * real `instructions` check — so one malformed response became a failure in one place, an
77
+ * empty prompt in another, and `undefined` in a third.
78
+ */
79
+ const isSkillDetailPayload = (data) => (0, exports.hasPromptBody)(data) && data.skill != null;
80
+ exports.isSkillDetailPayload = isSkillDetailPayload;
81
+ /**
82
+ * The detail payload every write route answers with.
83
+ *
84
+ * Shared because three epics need it — save, visibility, and the detail read — and a
85
+ * fourth copy is how one of them quietly stops carrying a field the others do.
86
+ *
87
+ * The coercions are not defensive habit; the payload type makes them mandatory — the
88
+ * three optional fields cannot be assigned to a non-optional `SkillDetail` without one.
89
+ * Which fields get coerced and which do not is the deliberate part:
90
+ *
91
+ * - `instructions` is load-bearing and already proven, so it passes through. Defaulting a
92
+ * missing prompt to `''` is the one thing this module exists to prevent: the user
93
+ * cannot tell an empty prompt from a skill that has one, and may save over the real
94
+ * thing.
95
+ * - `instructionsCharCount` falls back to the prompt's own length, NOT to 0. A 12,000
96
+ * character prompt displayed as "0 characters" is a lie the user can see, and the
97
+ * prompt is right here to measure. Approximate only for astral-plane characters, where
98
+ * UTF-16 units and the server's count differ — a rounding error, against a guaranteed
99
+ * wrong answer.
100
+ * - `artifact` and `artifactTitle` degrade to empty. They render a preview card, so
101
+ * losing them costs a card; failing the whole read would cost the user the skill.
102
+ */
103
+ const toSkillDetail = (payload) => ({
104
+ skill: { ...(0, exports.toBaseSkill)(payload.skill), exampleHints: [] },
105
+ instructions: payload.instructions,
106
+ instructionsCharCount: payload.instructions_char_count ?? payload.instructions.length,
107
+ artifact: payload.artifact ?? '',
108
+ artifactTitle: payload.artifact_title ?? '',
109
+ });
110
+ exports.toSkillDetail = toSkillDetail;
@@ -58,7 +58,7 @@ export interface TaskListUIState extends SpendManagementUIState {
58
58
  searchText: string;
59
59
  sortKey: TaskListViewSortKey;
60
60
  }
61
- declare const toTaskListSortKeyType: (v: string) => "status" | "dueDate" | "name" | "priority" | "assignee" | "creationDate" | "tag" | "timeSpent" | "visibility";
61
+ declare const toTaskListSortKeyType: (v: string) => "status" | "dueDate" | "name" | "priority" | "assignee" | "creationDate" | "tag" | "visibility" | "timeSpent";
62
62
  export declare type TaskListViewSortKey = ReturnType<typeof toTaskListSortKeyType>;
63
63
  export declare const TASK_LIST_FILTER_CATEGORIES: TaskListFilterCategoryOption[];
64
64
  interface TaskListFilterCategoryOption {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zeniai/client-epic-state",
3
- "version": "5.2.4",
3
+ "version": "5.2.5",
4
4
  "description": "Shared module between Web & Mobile containing required abstractions for state management, async network communication. ",
5
5
  "main": "lib/index.js",
6
6
  "module": "lib/esm/index.js",