@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.
- package/NOTICE +1 -1
- package/README.md +159 -151
- package/dist/access.d.ts +13 -0
- package/dist/access.js +13 -0
- package/dist/addon-index.d.ts +4 -0
- package/dist/addon-index.js +4 -0
- package/dist/addon.d.ts +53 -2
- package/dist/capabilities.d.ts +54 -87
- package/dist/catalog.d.ts +14 -0
- package/dist/catalog.js +14 -2
- package/dist/cms.d.ts +11 -0
- package/dist/cms.js +20 -0
- package/dist/conditions.d.ts +96 -0
- package/dist/conditions.js +163 -0
- package/dist/controls.d.ts +83 -0
- package/dist/controls.js +133 -0
- package/dist/core.d.ts +38 -0
- package/dist/core.js +39 -0
- package/dist/forms.d.ts +496 -0
- package/dist/forms.js +508 -0
- package/dist/http.d.ts +10 -1
- package/dist/http.js +10 -1
- package/dist/layout.d.ts +22 -0
- package/dist/layout.js +30 -0
- package/dist/media-storage.d.ts +256 -14
- package/dist/media-storage.js +30 -2
- package/dist/media.d.ts +47 -3
- package/dist/media.js +59 -0
- package/dist/record.d.ts +8 -0
- package/dist/record.js +24 -0
- package/dist/references.d.ts +130 -0
- package/dist/references.js +208 -0
- package/dist/resource-links.d.ts +94 -0
- package/dist/resource-links.js +27 -0
- package/dist/signals.d.ts +1 -0
- package/dist/signals.js +3 -0
- package/dist/site-groups.d.ts +15 -0
- package/dist/support.d.ts +390 -0
- package/dist/support.js +148 -0
- package/dist/threads.d.ts +363 -0
- package/dist/threads.js +146 -0
- package/dist/translation.d.ts +125 -0
- package/dist/translation.js +15 -0
- package/dist/user-state.d.ts +132 -0
- package/dist/user-state.js +92 -0
- package/package.json +47 -2
package/dist/controls.js
ADDED
|
@@ -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
|
};
|