@phis/contracts 0.3.1 → 0.5.0

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 (46) hide show
  1. package/NOTICE +1 -1
  2. package/README.md +159 -151
  3. package/dist/access.d.ts +13 -0
  4. package/dist/access.js +13 -0
  5. package/dist/addon-index.d.ts +4 -0
  6. package/dist/addon-index.js +4 -0
  7. package/dist/addon.d.ts +53 -2
  8. package/dist/capabilities.d.ts +54 -87
  9. package/dist/catalog.d.ts +14 -0
  10. package/dist/catalog.js +14 -2
  11. package/dist/cms.d.ts +11 -0
  12. package/dist/cms.js +20 -0
  13. package/dist/conditions.d.ts +96 -0
  14. package/dist/conditions.js +163 -0
  15. package/dist/controls.d.ts +83 -0
  16. package/dist/controls.js +133 -0
  17. package/dist/core.d.ts +38 -0
  18. package/dist/core.js +39 -0
  19. package/dist/forms.d.ts +496 -0
  20. package/dist/forms.js +508 -0
  21. package/dist/http.d.ts +10 -1
  22. package/dist/http.js +10 -1
  23. package/dist/layout.d.ts +22 -0
  24. package/dist/layout.js +30 -0
  25. package/dist/media-storage.d.ts +256 -14
  26. package/dist/media-storage.js +30 -2
  27. package/dist/media.d.ts +47 -3
  28. package/dist/media.js +59 -0
  29. package/dist/record.d.ts +8 -0
  30. package/dist/record.js +24 -0
  31. package/dist/references.d.ts +130 -0
  32. package/dist/references.js +208 -0
  33. package/dist/resource-links.d.ts +94 -0
  34. package/dist/resource-links.js +27 -0
  35. package/dist/signals.d.ts +1 -0
  36. package/dist/signals.js +3 -0
  37. package/dist/site-groups.d.ts +15 -0
  38. package/dist/support.d.ts +390 -0
  39. package/dist/support.js +148 -0
  40. package/dist/threads.d.ts +363 -0
  41. package/dist/threads.js +146 -0
  42. package/dist/translation.d.ts +125 -0
  43. package/dist/translation.js +15 -0
  44. package/dist/user-state.d.ts +132 -0
  45. package/dist/user-state.js +92 -0
  46. package/package.json +47 -2
@@ -0,0 +1,133 @@
1
+ /**
2
+ * What a choice control offers, and where it asks for its choices when they are not written out.
3
+ *
4
+ * Here because stored descriptors carry both: a Form field lists its options or names the provider
5
+ * that answers for them, and phis-server reads that field as it is stored.
6
+ */
7
+ /**
8
+ * `<namespace>/<name>`: an identifier that says whose it is.
9
+ *
10
+ * Provider keys, item renderer keys and everything else a Module may contribute to a shared registry
11
+ * share this shape, so they share one check. A second spelling of it is the one that drifts.
12
+ */
13
+ export function isPhiNamespacedRuntimeKey(value) {
14
+ if (typeof value !== "string") {
15
+ return false;
16
+ }
17
+ const separatorIndex = value.lastIndexOf("/");
18
+ return separatorIndex > 0 && separatorIndex < value.length - 1;
19
+ }
20
+ export function isPhiRuntimeDataProviderKey(value) {
21
+ return isPhiNamespacedRuntimeKey(value);
22
+ }
23
+ function readString(value) {
24
+ return typeof value === "string" && value.trim().length > 0
25
+ ? value.trim()
26
+ : undefined;
27
+ }
28
+ function readRecord(value) {
29
+ return value && typeof value === "object" && !Array.isArray(value) ? value : undefined;
30
+ }
31
+ function readLoadMode(value) {
32
+ return value === "client" || value === "server" || value === "hybrid" ? value : undefined;
33
+ }
34
+ function readSearchConfig(value) {
35
+ const record = readRecord(value);
36
+ if (!record) {
37
+ return undefined;
38
+ }
39
+ const enabled = typeof record.enabled === "boolean" ? record.enabled : undefined;
40
+ const minChars = typeof record.minChars === "number" && Number.isFinite(record.minChars)
41
+ ? Math.max(0, Math.trunc(record.minChars))
42
+ : undefined;
43
+ if (enabled === undefined && minChars === undefined) {
44
+ return undefined;
45
+ }
46
+ return {
47
+ enabled,
48
+ minChars,
49
+ };
50
+ }
51
+ function readDependencies(value) {
52
+ if (!Array.isArray(value)) {
53
+ return undefined;
54
+ }
55
+ const dependencies = value.flatMap((entry) => {
56
+ const record = readRecord(entry);
57
+ const param = readString(record?.param);
58
+ const valuePath = readString(record?.valuePath);
59
+ const source = record?.source === "form" || record?.source === "config" ? record.source : undefined;
60
+ if (!param || !valuePath || !source) {
61
+ return [];
62
+ }
63
+ return [{
64
+ param,
65
+ source,
66
+ valuePath,
67
+ required: record?.required === true ? true : undefined,
68
+ }];
69
+ });
70
+ return dependencies.length > 0 ? dependencies : undefined;
71
+ }
72
+ function readOptionPreview(value) {
73
+ const record = readRecord(value);
74
+ if (record?.kind === "font") {
75
+ const fontFamily = readString(record.fontFamily);
76
+ return fontFamily ? { kind: "font", fontFamily } : undefined;
77
+ }
78
+ if (record?.kind !== "background")
79
+ return undefined;
80
+ return {
81
+ kind: "background",
82
+ backgroundColor: readString(record.backgroundColor),
83
+ backgroundImage: readString(record.backgroundImage),
84
+ backgroundSize: readString(record.backgroundSize),
85
+ backgroundPosition: readString(record.backgroundPosition),
86
+ backgroundRepeat: readString(record.backgroundRepeat),
87
+ };
88
+ }
89
+ export function parsePhiControlOptionsProviderConfig(value) {
90
+ const record = readRecord(value);
91
+ if (!record) {
92
+ return null;
93
+ }
94
+ const providerKey = readString(record.providerKey);
95
+ if (!isPhiRuntimeDataProviderKey(providerKey)) {
96
+ return null;
97
+ }
98
+ return {
99
+ providerKey,
100
+ scopeKey: readString(record.scopeKey),
101
+ area: readString(record.area),
102
+ params: readRecord(record.params),
103
+ loadMode: readLoadMode(record.loadMode),
104
+ search: readSearchConfig(record.search),
105
+ dependencies: readDependencies(record.dependencies),
106
+ };
107
+ }
108
+ export function readPhiControlOptions(value) {
109
+ if (Array.isArray(value)) {
110
+ return value
111
+ .map((item) => {
112
+ if (!item || typeof item !== "object" || Array.isArray(item)) {
113
+ return null;
114
+ }
115
+ const record = item;
116
+ const optionValue = readString(record.value);
117
+ if (!optionValue) {
118
+ return null;
119
+ }
120
+ return {
121
+ value: optionValue,
122
+ label: readString(record.label) ?? optionValue,
123
+ disabled: typeof record.disabled === "boolean" ? record.disabled : undefined,
124
+ description: readString(record.description),
125
+ icon: readString(record.icon),
126
+ preview: readOptionPreview(record.preview),
127
+ group: readString(record.group),
128
+ };
129
+ })
130
+ .filter((item) => item != null);
131
+ }
132
+ return [];
133
+ }
package/dist/core.d.ts CHANGED
@@ -47,6 +47,28 @@ export declare const PHIS_CORE_CAPABILITIES: {
47
47
  readonly roles: "@phis/server/roles:v1";
48
48
  };
49
49
  export type PhisCoreCapabilityId = (typeof PHIS_CORE_CAPABILITIES)[keyof typeof PHIS_CORE_CAPABILITIES];
50
+ /**
51
+ * What Core tells an Add-on about after the fact.
52
+ *
53
+ * An integration that mirrors a conversation cannot wait to be asked: somebody writes a reply here and
54
+ * it has to appear over there, and no request the Add-on serves is involved. Polling would be the
55
+ * alternative, and polling for "did anybody write anything" is the Site-wide read every capability here
56
+ * is shaped to prevent.
57
+ *
58
+ * An event is delivered only to an Add-on linked to the thread it happened in, and a message this same
59
+ * Add-on imported is never announced back to it -- which is what stops a mirror from echoing.
60
+ *
61
+ * The list is short on purpose and grows when an Add-on needs an entry, the way the capability catalogue
62
+ * does. Every event a Site could emit, declared in advance, would be a contract to get wrong.
63
+ */
64
+ export declare const PHIS_ADDON_EVENTS: {
65
+ readonly threadMessageCreated: "thread.message.created";
66
+ readonly threadMessageUpdated: "thread.message.updated";
67
+ readonly threadStatusChanged: "thread.status.changed";
68
+ /** The link itself is gone; what follows from that is the Add-on's own business. */
69
+ readonly threadLinkRemoved: "thread.link.removed";
70
+ };
71
+ export type PhisAddonEventId = (typeof PHIS_ADDON_EVENTS)[keyof typeof PHIS_ADDON_EVENTS];
50
72
  /**
51
73
  * The Core-owned service kinds an Add-on may implement.
52
74
  *
@@ -57,6 +79,15 @@ export type PhisCoreCapabilityId = (typeof PHIS_CORE_CAPABILITIES)[keyof typeof
57
79
  export declare const PHIS_SERVICE_KINDS: {
58
80
  readonly mediaStorage: "@phis/server/service/media-storage";
59
81
  readonly directory: "@phis/server/service/directory";
82
+ /**
83
+ * Turning text in one language into text in another.
84
+ *
85
+ * A kind rather than a capability for the same reason as the two above: Core owns the question and
86
+ * keeps asking it, and what an implementation talks to -- a vendor, a model on the machine, a
87
+ * catalogue it maintains itself -- is its own business. Several may be installed at once; the
88
+ * operator selects which one an installation uses.
89
+ */
90
+ readonly translation: "@phis/server/service/translation";
60
91
  };
61
92
  export type PhisServiceKind = (typeof PHIS_SERVICE_KINDS)[keyof typeof PHIS_SERVICE_KINDS];
62
93
  /**
@@ -69,6 +100,13 @@ export type PhisServiceKind = (typeof PHIS_SERVICE_KINDS)[keyof typeof PHIS_SERV
69
100
  *
70
101
  * The digest is `sha256("phi-server-service-kind:<kind>:v<interface version>")`. That recipe is
71
102
  * documented for whoever has to produce the next value; nothing needs to run it to read a manifest.
103
+ *
104
+ * **Before the v1 release every version here stays at 1, whatever changes shape.** There is no released
105
+ * Add-on to protect and nothing in the field built against an older interface, so raising a version buys
106
+ * no compatibility and costs the installation: the recorded manifest of every installed Add-on stops
107
+ * matching, `buildPhisCapabilitySnapshot` refuses the desired state, and every request that resolves a
108
+ * viewer answers 500 until each Add-on has been rebundled and upgraded. An interface that changes before
109
+ * v1 is simply the interface, and both sides are rebuilt from one tree.
72
110
  */
73
111
  export declare const PHIS_SERVICE_INTERFACE_VERSIONS: Readonly<Record<PhisServiceKind, number>>;
74
112
  export declare const PHIS_SERVICE_INTERFACE_DIGESTS: Readonly<Record<PhisServiceKind, string>>;
package/dist/core.js CHANGED
@@ -46,6 +46,27 @@ export const PHIS_CORE_CAPABILITIES = {
46
46
  /** Whether the acting user holds one of this Add-on's own declared roles. */
47
47
  roles: "@phis/server/roles:v1",
48
48
  };
49
+ /**
50
+ * What Core tells an Add-on about after the fact.
51
+ *
52
+ * An integration that mirrors a conversation cannot wait to be asked: somebody writes a reply here and
53
+ * it has to appear over there, and no request the Add-on serves is involved. Polling would be the
54
+ * alternative, and polling for "did anybody write anything" is the Site-wide read every capability here
55
+ * is shaped to prevent.
56
+ *
57
+ * An event is delivered only to an Add-on linked to the thread it happened in, and a message this same
58
+ * Add-on imported is never announced back to it -- which is what stops a mirror from echoing.
59
+ *
60
+ * The list is short on purpose and grows when an Add-on needs an entry, the way the capability catalogue
61
+ * does. Every event a Site could emit, declared in advance, would be a contract to get wrong.
62
+ */
63
+ export const PHIS_ADDON_EVENTS = {
64
+ threadMessageCreated: "thread.message.created",
65
+ threadMessageUpdated: "thread.message.updated",
66
+ threadStatusChanged: "thread.status.changed",
67
+ /** The link itself is gone; what follows from that is the Add-on's own business. */
68
+ threadLinkRemoved: "thread.link.removed",
69
+ };
49
70
  /**
50
71
  * The Core-owned service kinds an Add-on may implement.
51
72
  *
@@ -56,6 +77,15 @@ export const PHIS_CORE_CAPABILITIES = {
56
77
  export const PHIS_SERVICE_KINDS = {
57
78
  mediaStorage: "@phis/server/service/media-storage",
58
79
  directory: "@phis/server/service/directory",
80
+ /**
81
+ * Turning text in one language into text in another.
82
+ *
83
+ * A kind rather than a capability for the same reason as the two above: Core owns the question and
84
+ * keeps asking it, and what an implementation talks to -- a vendor, a model on the machine, a
85
+ * catalogue it maintains itself -- is its own business. Several may be installed at once; the
86
+ * operator selects which one an installation uses.
87
+ */
88
+ translation: "@phis/server/service/translation",
59
89
  };
60
90
  /**
61
91
  * The digest of the interface each service kind currently has.
@@ -67,12 +97,21 @@ export const PHIS_SERVICE_KINDS = {
67
97
  *
68
98
  * The digest is `sha256("phi-server-service-kind:<kind>:v<interface version>")`. That recipe is
69
99
  * documented for whoever has to produce the next value; nothing needs to run it to read a manifest.
100
+ *
101
+ * **Before the v1 release every version here stays at 1, whatever changes shape.** There is no released
102
+ * Add-on to protect and nothing in the field built against an older interface, so raising a version buys
103
+ * no compatibility and costs the installation: the recorded manifest of every installed Add-on stops
104
+ * matching, `buildPhisCapabilitySnapshot` refuses the desired state, and every request that resolves a
105
+ * viewer answers 500 until each Add-on has been rebundled and upgraded. An interface that changes before
106
+ * v1 is simply the interface, and both sides are rebuilt from one tree.
70
107
  */
71
108
  export const PHIS_SERVICE_INTERFACE_VERSIONS = {
72
109
  [PHIS_SERVICE_KINDS.mediaStorage]: 1,
73
110
  [PHIS_SERVICE_KINDS.directory]: 1,
111
+ [PHIS_SERVICE_KINDS.translation]: 1,
74
112
  };
75
113
  export const PHIS_SERVICE_INTERFACE_DIGESTS = {
76
114
  [PHIS_SERVICE_KINDS.mediaStorage]: "45a871522511f7d77841ca8dd0113b423b3cfd4cb921f143cd1d3d116cab6952",
77
115
  [PHIS_SERVICE_KINDS.directory]: "2cac7397c7beb1b8e71fc67f5cae3be89182dddf096895570a15deafa1adbfc9",
116
+ [PHIS_SERVICE_KINDS.translation]: "9bae2e7b92ded5cdde9321939a3bb8cd589f6e1ef8bca7bc5f65608313b3207d",
78
117
  };