@phis/contracts 0.5.0 → 0.7.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/README.md CHANGED
@@ -6,6 +6,7 @@ and change at different moments.
6
6
 
7
7
  ```
8
8
  @phis/contracts/addon what phis and a separately shipped Add-on promise each other
9
+ @phis/contracts/payments the approved Payment consumer, Provider and merchant-profile types
9
10
  @phis/contracts/access the authorization vocabulary phis and @phis/ui both evaluate
10
11
  @phis/contracts/signals the signal vocabulary and address grammar the UI declares in and phis validates
11
12
  @phis/contracts/catalog the Module categories an Add-on declares, the UI groups by and a market filters on
@@ -45,7 +46,7 @@ The types and constants an Add-on declares or implements. Types and constants on
45
46
  `server-only`, no React, no Next.
46
47
 
47
48
  An Add-on is compiled to a single ESM artifact and installed into a running phis through the `phis` CLI;
48
- it is never built into the server. This subpath is the only thing an Add-on compiles against, and it must
49
+ it is never built into the server. This subpath is the shared Add-on entrypoint it compiles against, and it must
49
50
  not depend on phis itself. For an Add-on author it is a build-time dependency: everything it exports is
50
51
  erased at compile time apart from a handful of constants.
51
52
 
@@ -61,10 +62,10 @@ What it covers:
61
62
  (`PhisMediaStorageAdapter`, upload plans, object I/O) and Directory. `PHIS_SERVICE_KINDS` names them
62
63
  and `PHIS_SERVICE_INTERFACE_DIGESTS` holds the digest of each kind's current interface; Core refuses an
63
64
  Add-on built against a different digest at install.
64
- - **Core capabilities** -- `PHIS_CORE_CAPABILITIES`, the capability ids an Add-on may require. Declare
65
- against these constants rather than spelling the ids out. The list is the vocabulary and may reserve a
66
- name before Core delivers it (`resource-links`, `support`); an Add-on that requires a capability this
67
- release does not deliver is refused rather than handed a missing object.
65
+ - **Core capabilities** -- `PHIS_CORE_CAPABILITIES`, the catalogue ids an Add-on may require. Declare
66
+ against these constants rather than spelling the ids out. Core separately validates delivery to
67
+ Add-ons; a catalogue id such as `authentication` may serve Site Modules without a corresponding
68
+ Add-on context field. Payment ids accompany the implemented Core capability/service/event catalogue.
68
69
 
69
70
  `PHIS_ADDON_ABI_VERSION` is the single number phis checks an Add-on against.
70
71
 
@@ -72,6 +73,23 @@ How an Add-on is laid out, compiled, installed, activated and upgraded is descri
72
73
  [`ADDON_HOWTO.md`](https://github.com/phis-dev/phis-server/blob/main/ADDON_HOWTO.md); the rules behind it
73
74
  are in [`SERVER_ADDONS.md`](https://github.com/phis-dev/phis-server/blob/main/SERVER_ADDONS.md).
74
75
 
76
+ ## `/payments`
77
+
78
+ The approved v1 consumer capability, Provider adapter, checkout plans, normalized money,
79
+ payment/operation/refund records, merchant profile/grant types and owning-consumer event facts.
80
+ All exports are types; there is no secret accessor implementation, checkout SDK, exchange-rate
81
+ conversion or Payment engine here. The same types are re-exported by `/addon`.
82
+
83
+ Consumers own their prices in CHF, EUR, USD or another supported currency. Every payment uses one
84
+ currency; capture and refunds inherit it. Core never infers conversion or settlement currency.
85
+
86
+ Core implements Payment execution separately. Its catalogue exports `payments`, `paymentNotifications`,
87
+ the Payment service kind/interface digest and `payment.changed`. Notification ingress is available
88
+ only in a bound hook context. The Add-on event context discriminates thread and Payment subjects;
89
+ thread producers remain explicitly typed as thread events.
90
+ See the normative [Payment contract](https://github.com/phis-dev/phis-server/blob/main/PAYMENTS.md)
91
+ and [Stripe/profile contract](https://github.com/phis-dev/phis-server/blob/main/PAYMENTS_STRIPE.md).
92
+
75
93
  ## `/access`
76
94
 
77
95
  Claim shapes, policy shapes, and the evaluator that decides them. Unlike `/addon`, this is runtime code
@@ -3,6 +3,7 @@ export * from "./assets.js";
3
3
  export * from "./capabilities.js";
4
4
  export * from "./core.js";
5
5
  export * from "./media-storage.js";
6
+ export * from "./payments.js";
6
7
  export * from "./queries.js";
7
8
  export * from "./resource-links.js";
8
9
  export * from "./schema.js";
@@ -3,6 +3,7 @@ export * from "./assets.js";
3
3
  export * from "./capabilities.js";
4
4
  export * from "./core.js";
5
5
  export * from "./media-storage.js";
6
+ export * from "./payments.js";
6
7
  export * from "./queries.js";
7
8
  export * from "./resource-links.js";
8
9
  export * from "./schema.js";
package/dist/addon.d.ts CHANGED
@@ -65,7 +65,9 @@ export type PhisAddonHookDescriptor = {
65
65
  *
66
66
  * Absent is not a lesser variant of present: an unverified hook writes nothing at all -- not through
67
67
  * `data:v1`, not by opening a thread -- because nothing distinguishes its caller from anybody who
68
- * found the address. A hook that needs to write declares how it can be believed.
68
+ * found the address. A hook that needs ambient write authority declares how it can be believed.
69
+ * Target v1 Payment notification ingress instead verifies through its bound Core capability and
70
+ * reconciles Core-owned payment state without granting ambient hook write authority.
69
71
  */
70
72
  verification?: PhisAddonHookVerificationDescriptor;
71
73
  /**
@@ -197,6 +199,46 @@ export type PhisAddonEventHandlerDescriptor = {
197
199
  event: PhisAddonEventId;
198
200
  handler: string;
199
201
  };
202
+ /**
203
+ * One sentence of an occasion, with the parameters it takes in order.
204
+ *
205
+ * `%1`, `%2`, ... in `text` stand for the named parameters, the way Core's own templates are written,
206
+ * so the sentence translates as one unit and the values are put in afterwards. A text parameter is
207
+ * escaped where it lands; a path parameter arrives as a link on the Site's own public address.
208
+ */
209
+ export type PhisAddonOccasionLine = {
210
+ text: string;
211
+ parameters?: string[];
212
+ };
213
+ /**
214
+ * One kind of message this Add-on may ask Core to write to a person.
215
+ *
216
+ * Declared rather than composed at the call: the words are part of the manifest and therefore of the
217
+ * digest, so what a Site can say in an Add-on's name is readable at install and cannot change under a
218
+ * running Site. Core renders the lines through its own layout, salutation and translation, and adds
219
+ * what the class owes -- which is why an Add-on cannot produce a mail without Core's frame.
220
+ *
221
+ * `class` is `asked-for` and nothing else this release: the person's own action is the reason for the
222
+ * message (an order they placed, a request they made), no consent bit applies and no unsubscribe link
223
+ * is offered. The sent class -- a message somebody decided to send to people who did not ask today --
224
+ * is Core's own bulk case and is not declarable here until it is.
225
+ */
226
+ export type PhisAddonOccasionDescriptor = {
227
+ /** `^[a-z][a-z0-9-]{0,63}$`; the name a `notify` call and the outbox row carry. */
228
+ name: string;
229
+ class: "asked-for";
230
+ description?: string;
231
+ subject: PhisAddonOccasionLine;
232
+ lines: PhisAddonOccasionLine[];
233
+ /**
234
+ * What a call must bring, by name. `text` is inserted as written; `path` is a Site-relative path
235
+ * (`/shop/order/1234`) that Core turns into an absolute link on the Site's public address.
236
+ */
237
+ parameters: {
238
+ name: string;
239
+ type: "text" | "path";
240
+ }[];
241
+ };
200
242
  export type PhisAddonManifestV1 = {
201
243
  manifestVersion: typeof PHIS_ADDON_MANIFEST_VERSION;
202
244
  addonId: string;
@@ -258,6 +300,13 @@ export type PhisAddonManifestV1 = {
258
300
  * provider, and every write here refuses a group carrying anybody else's.
259
301
  */
260
302
  groupWriter?: PhisAddonGroupWriterDescriptor;
303
+ /**
304
+ * The occasions this Add-on may write to a person about. Absent means none, and `notify` then has
305
+ * nothing it could be asked for.
306
+ *
307
+ * Optional for the reason `settings` is: it arrived after the manifest version was in the wild.
308
+ */
309
+ occasions?: PhisAddonOccasionDescriptor[];
261
310
  /**
262
311
  * The Modules this package's other half contributes. Absent means it has no Module half.
263
312
  *
@@ -12,7 +12,8 @@
12
12
  * at the point where it made the mistake.
13
13
  */
14
14
  import type { PhisAssetsCapabilityV1 } from "./assets.js";
15
- import type { PhisAddonEventId } from "./core.js";
15
+ import type { PhisAddonThreadEventId } from "./core.js";
16
+ import type { PhisPaymentChangedEventFacts, PhisPaymentNotificationsCapabilityV1, PhisPaymentsCapabilityV1 } from "./payments.js";
16
17
  import type { PhisDataCapabilityV1 } from "./queries.js";
17
18
  import type { PhisResourceLinksCapabilityV1 } from "./resource-links.js";
18
19
  import type { PhisSupportCapabilityV1 } from "./support.js";
@@ -102,6 +103,12 @@ export type PhisAddonCapabilities = {
102
103
  support?: PhisSupportCapabilityV1;
103
104
  settings?: PhisSettingsCapabilityV1;
104
105
  roles?: PhisRolesCapabilityV1;
106
+ /** Bound to the declaring consumer and Site when required. */
107
+ payments?: PhisPaymentsCapabilityV1;
108
+ /** Supplied only to an original, bound Payment Provider hook request. */
109
+ paymentNotifications?: PhisPaymentNotificationsCapabilityV1;
110
+ /** Writing to a person about an occasion this Add-on declared; Core owns the frame and the sending. */
111
+ notifications?: PhisNotificationsCapabilityV1;
105
112
  };
106
113
  /**
107
114
  * The roles capability: `@phis/server/roles:v1`.
@@ -206,8 +213,67 @@ export type PhisStorageCapabilityV1 = {
206
213
  * own would be a package that could plant one.
207
214
  */
208
215
  export type PhisSecretsCapabilityV1 = {
209
- /** The value, or null when the operator has not configured it. */
210
- read(name: string): Promise<string | null>;
216
+ /**
217
+ * The value, or null when the operator has not configured it.
218
+ *
219
+ * `scope` says which of the two an operator can set: `"site"` (the default) is the one set with
220
+ * `--site` for the Site this call runs on; `"addon"` is the one set without `--site`, shared by every
221
+ * Site the Add-on runs on -- one credential for one external account, such as a single app
222
+ * registration. The two are never substituted for each other: a Site secret that is missing is
223
+ * `null`, not the shared one.
224
+ */
225
+ read(name: string, scope?: PhisSecretScope): Promise<string | null>;
226
+ };
227
+ /** Which secret `PhisSecretsCapabilityV1.read` names: the Site's own, or the one shared by all Sites. */
228
+ export type PhisSecretScope = "site" | "addon";
229
+ /**
230
+ * The notifications capability: `@phis/server/notifications:v1`.
231
+ *
232
+ * `notify` names **who** and **what happened**, and nothing about how. The occasion is one this Add-on
233
+ * declared in its manifest (`occasions`), so its words are sealed by the digest; Core resolves the
234
+ * address, picks the language, writes the salutation, renders the lines through its own layout and
235
+ * translation, chooses the sender, and sends -- through the outbox, with retry and a record. There is no
236
+ * subject, body, `from`, header or attachment on the call, because every one of those is a decision
237
+ * about how a Site speaks, and a Site speaks with one voice.
238
+ *
239
+ * Two recipients. A `user` is a member of this Site, named by id: the address is resolved when the
240
+ * message is sent, so a corrected address is used and a membership that is gone is not written to.
241
+ * A `contact` is a person who has no account here and gave this Add-on their address for exactly this
242
+ * purpose -- a guest who placed an order, somebody who filled in a form. It is the one place an Add-on
243
+ * hands Core an address, and it is admitted on two conditions Core enforces: the occasion is one the
244
+ * person's own action asked for (every declared occasion is, this release), and the locale is stated,
245
+ * because there is no profile to ask.
246
+ *
247
+ * `params` are values about the event, never about the person: a `text` parameter is inserted as
248
+ * written, a `path` parameter is a Site-relative path Core turns into an absolute link on the Site's own
249
+ * public address. An occasion cannot put an arbitrary URL into a Site's outgoing mail.
250
+ *
251
+ * Answers the outbox row's id, or `null` when `dedupeKey` named a message already written -- which is
252
+ * what makes a repeated run safe. Refused outright: an undeclared occasion, a parameter the occasion
253
+ * did not declare or lacks, a `user` who is not a member of this Site, and any call from an
254
+ * unverified hook, which may change nothing.
255
+ */
256
+ export type PhisNotificationsCapabilityV1 = {
257
+ notify(input: PhisNotificationInput): Promise<{
258
+ notificationId: string | null;
259
+ }>;
260
+ };
261
+ export type PhisNotificationRecipient = {
262
+ kind: "user";
263
+ userId: number;
264
+ locale?: string | null;
265
+ } | {
266
+ kind: "contact";
267
+ address: string;
268
+ locale: string;
269
+ name?: string | null;
270
+ };
271
+ export type PhisNotificationInput = {
272
+ occasion: string;
273
+ recipient: PhisNotificationRecipient;
274
+ params: Readonly<Record<string, string>>;
275
+ /** Unique within the Site; a second call with the same key writes nothing and answers `null`. */
276
+ dedupeKey?: string | null;
211
277
  };
212
278
  /**
213
279
  * What an Add-on may learn about a person.
@@ -357,24 +423,26 @@ export type PhisAddonJobContext = {
357
423
  * this Site something, an event is this Site telling an Add-on something, and both run as the Add-on
358
424
  * rather than as a person.
359
425
  *
360
- * Delivery is bounded by the same link. An Add-on is told about a thread it is synced with and about no
361
- * other, because a Site-wide feed of everything anybody writes is the listing `threads:v1` refuses,
362
- * arriving by push instead of by pull.
426
+ * Thread delivery is bounded by a synced link. The Payment event reaches only its stored
427
+ * consuming Add-on. The subject kind and event discriminate those two ownership models; approved
428
+ * Payment facts are delivered through Core's durable owner-directed outbox.
363
429
  */
364
- export type PhisAddonEventContext = {
430
+ export type PhisAddonEventFacts = {
431
+ event: PhisAddonThreadEventId;
432
+ subject: {
433
+ kind: "thread";
434
+ threadId: number;
435
+ messageId: number | null;
436
+ };
437
+ } | PhisPaymentChangedEventFacts;
438
+ export type PhisAddonEventContext = PhisAddonEventFacts & {
365
439
  addonId: string;
366
- event: PhisAddonEventId;
367
440
  /** This delivery, stable across retries, so a handler can recognise one it already settled. */
368
441
  deliveryId: string;
369
442
  site: {
370
443
  id: number;
371
444
  key: string;
372
445
  };
373
- /** The thread the event happened in, and the message where the event is about one. */
374
- subject: {
375
- threadId: number;
376
- messageId: number | null;
377
- };
378
446
  capabilities: PhisAddonCapabilities;
379
447
  signal: AbortSignal;
380
448
  };
package/dist/cms.d.ts CHANGED
@@ -73,7 +73,9 @@ export declare const PhiCmsVisibilityContext: {
73
73
  * The flags a CMS row carries, in the one numbering both sides read them from.
74
74
  *
75
75
  * Not every flag reaches every kind of row -- `Collapsed` is a Region's answer and `MobileOnly` a
76
- * renderable's -- but they share one space, because a row is stored as a row whatever it holds.
76
+ * renderable's -- but they share one space, because a row is stored as a row whatever it holds. The
77
+ * same numbering serves a config's own `flags`, where a Region's and an Overlay's behaviour live
78
+ * (`Sticky`, `NoCloseButton`, ...): a yes-or-no answer in a stored record is a bit, never a boolean.
77
79
  *
78
80
  * `NoIndex` is a Page's, and it is the Site's answer rather than the preset's: a Module states what a
79
81
  * Page it ships starts out as, and the Builder's switch is what decides from then on. That is why it
@@ -88,6 +90,26 @@ export declare const PhiCmsFlags: {
88
90
  readonly Collapsed: number;
89
91
  readonly NoTranslate: number;
90
92
  readonly NoIndex: number;
93
+ /** Stays in view while the page scrolls (Header and Sider families). */
94
+ readonly Sticky: number;
95
+ /** A Sider that takes the viewport height below its offset. */
96
+ readonly FullHeight: number;
97
+ /** A Sider the viewer can fold to its collapsed width. */
98
+ readonly Collapsible: number;
99
+ /** The Header chrome renders no close button. */
100
+ readonly NoCloseButton: number;
101
+ /** Escape does not request dismissal. */
102
+ readonly NoEscapeClose: number;
103
+ /** A Modal centred in the viewport. */
104
+ readonly Centered: number;
105
+ /** A Drawer the viewer can resize. */
106
+ readonly Resizable: number;
107
+ /** A Drawer that pushes a nested Drawer aside (`pushDistance` states how far). */
108
+ readonly Push: number;
109
+ /** Pointer input reaches the page behind the Overlay. */
110
+ readonly MaskPassesPointer: number;
111
+ /** An outside pointer action does not request dismissal. */
112
+ readonly MaskKeepsOpen: number;
91
113
  };
92
114
  export declare const PhiCmsRevisionFlags: {
93
115
  readonly Draft: number;
package/dist/cms.js CHANGED
@@ -81,7 +81,9 @@ export const PhiCmsVisibilityContext = {
81
81
  * The flags a CMS row carries, in the one numbering both sides read them from.
82
82
  *
83
83
  * Not every flag reaches every kind of row -- `Collapsed` is a Region's answer and `MobileOnly` a
84
- * renderable's -- but they share one space, because a row is stored as a row whatever it holds.
84
+ * renderable's -- but they share one space, because a row is stored as a row whatever it holds. The
85
+ * same numbering serves a config's own `flags`, where a Region's and an Overlay's behaviour live
86
+ * (`Sticky`, `NoCloseButton`, ...): a yes-or-no answer in a stored record is a bit, never a boolean.
85
87
  *
86
88
  * `NoIndex` is a Page's, and it is the Site's answer rather than the preset's: a Module states what a
87
89
  * Page it ships starts out as, and the Builder's switch is what decides from then on. That is why it
@@ -96,6 +98,35 @@ export const PhiCmsFlags = {
96
98
  Collapsed: 1 << 4,
97
99
  NoTranslate: 1 << 5,
98
100
  NoIndex: 1 << 6,
101
+ /*
102
+ * A Region's behaviour, carried in its config's `flags`: a Region stored on a Page is a config under
103
+ * the Page's `layoutConfig` and has no row of its own, so the config is the one place both kinds have.
104
+ */
105
+ /** Stays in view while the page scrolls (Header and Sider families). */
106
+ Sticky: 1 << 7,
107
+ /** A Sider that takes the viewport height below its offset. */
108
+ FullHeight: 1 << 8,
109
+ /** A Sider the viewer can fold to its collapsed width. */
110
+ Collapsible: 1 << 9,
111
+ /*
112
+ * An Overlay's behaviour, carried in its config's `flags` the same way. The bits name the departure
113
+ * from the Overlay's defaults, so an unset flag set is the plain Overlay: a close button, Escape
114
+ * closes, the mask stops the pointer and closes on a click.
115
+ */
116
+ /** The Header chrome renders no close button. */
117
+ NoCloseButton: 1 << 10,
118
+ /** Escape does not request dismissal. */
119
+ NoEscapeClose: 1 << 11,
120
+ /** A Modal centred in the viewport. */
121
+ Centered: 1 << 12,
122
+ /** A Drawer the viewer can resize. */
123
+ Resizable: 1 << 13,
124
+ /** A Drawer that pushes a nested Drawer aside (`pushDistance` states how far). */
125
+ Push: 1 << 14,
126
+ /** Pointer input reaches the page behind the Overlay. */
127
+ MaskPassesPointer: 1 << 15,
128
+ /** An outside pointer action does not request dismissal. */
129
+ MaskKeepsOpen: 1 << 16,
99
130
  };
100
131
  export const PhiCmsRevisionFlags = {
101
132
  Draft: 1 << 0,
@@ -26,7 +26,15 @@ export type PhiControlOption<TValue extends string | number = string> = {
26
26
  * come, so the order has to make sense without the headings.
27
27
  */
28
28
  group?: string;
29
+ /**
30
+ * A line drawn between this option and the one before or after it, where the Control can draw one --
31
+ * a Select does, in its menu. It sets an option apart from the rest without a heading to name it: the
32
+ * Button's "Custom" above the actions it is not one of. A Control that cannot draw it lists the
33
+ * options as they come.
34
+ */
35
+ separator?: PhiControlOptionSeparator;
29
36
  };
37
+ export type PhiControlOptionSeparator = "before" | "after";
30
38
  export type PhiControlOptionPreview = PhiControlOptionBackgroundPreview | PhiControlOptionFontPreview;
31
39
  /**
32
40
  * The option's label set in the typeface it names, so a list of fonts shows the fonts.
package/dist/controls.js CHANGED
@@ -125,6 +125,7 @@ export function readPhiControlOptions(value) {
125
125
  icon: readString(record.icon),
126
126
  preview: readOptionPreview(record.preview),
127
127
  group: readString(record.group),
128
+ separator: record.separator === "before" || record.separator === "after" ? record.separator : undefined,
128
129
  };
129
130
  })
130
131
  .filter((item) => item != null);
package/dist/core.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { PhisPaymentChangedEventFacts } from "./payments.js";
1
2
  /**
2
3
  * What Core offers, named once so both sides can spell it the same way.
3
4
  *
@@ -45,6 +46,10 @@ export declare const PHIS_CORE_CAPABILITIES: {
45
46
  readonly settings: "@phis/server/settings:v1";
46
47
  /** Whether the acting user holds one of this Add-on's own declared roles. */
47
48
  readonly roles: "@phis/server/roles:v1";
49
+ readonly payments: "@phis/server/payments:v1";
50
+ readonly paymentNotifications: "@phis/server/payment-notifications:v1";
51
+ /** Writing to a person about an occasion the Add-on declared, with Core owning frame and transport. */
52
+ readonly notifications: "@phis/server/notifications:v1";
48
53
  };
49
54
  export type PhisCoreCapabilityId = (typeof PHIS_CORE_CAPABILITIES)[keyof typeof PHIS_CORE_CAPABILITIES];
50
55
  /**
@@ -67,8 +72,11 @@ export declare const PHIS_ADDON_EVENTS: {
67
72
  readonly threadStatusChanged: "thread.status.changed";
68
73
  /** The link itself is gone; what follows from that is the Add-on's own business. */
69
74
  readonly threadLinkRemoved: "thread.link.removed";
75
+ readonly paymentChanged: "payment.changed";
70
76
  };
71
- export type PhisAddonEventId = (typeof PHIS_ADDON_EVENTS)[keyof typeof PHIS_ADDON_EVENTS];
77
+ export type PhisAddonThreadEventId = Exclude<(typeof PHIS_ADDON_EVENTS)[keyof typeof PHIS_ADDON_EVENTS], "payment.changed">;
78
+ /** Delivered v1 event vocabulary, discriminated by its owned subject. */
79
+ export type PhisAddonEventId = PhisAddonThreadEventId | PhisPaymentChangedEventFacts["event"];
72
80
  /**
73
81
  * The Core-owned service kinds an Add-on may implement.
74
82
  *
@@ -78,6 +86,7 @@ export type PhisAddonEventId = (typeof PHIS_ADDON_EVENTS)[keyof typeof PHIS_ADDO
78
86
  */
79
87
  export declare const PHIS_SERVICE_KINDS: {
80
88
  readonly mediaStorage: "@phis/server/service/media-storage";
89
+ readonly payments: "@phis/server/service/payments";
81
90
  readonly directory: "@phis/server/service/directory";
82
91
  /**
83
92
  * Turning text in one language into text in another.
@@ -88,6 +97,15 @@ export declare const PHIS_SERVICE_KINDS: {
88
97
  * operator selects which one an installation uses.
89
98
  */
90
99
  readonly translation: "@phis/server/service/translation";
100
+ /**
101
+ * Handing a Support ticket's work to a system outside the Site -- an issue tracker, a ticket desk.
102
+ *
103
+ * A kind because Core owns the question: which ticket, which task, what may leave and what comes back.
104
+ * What the implementation talks to is its own business, and several may be installed at once; a
105
+ * Supporter chooses one per forward. The interface is `PhisSupportForwardingAdapterV1` in
106
+ * `@phis/contracts/support`.
107
+ */
108
+ readonly supportForwarding: "@phis/server/service/support-forwarding";
91
109
  };
92
110
  export type PhisServiceKind = (typeof PHIS_SERVICE_KINDS)[keyof typeof PHIS_SERVICE_KINDS];
93
111
  /**
package/dist/core.js CHANGED
@@ -45,6 +45,10 @@ export const PHIS_CORE_CAPABILITIES = {
45
45
  settings: "@phis/server/settings:v1",
46
46
  /** Whether the acting user holds one of this Add-on's own declared roles. */
47
47
  roles: "@phis/server/roles:v1",
48
+ payments: "@phis/server/payments:v1",
49
+ paymentNotifications: "@phis/server/payment-notifications:v1",
50
+ /** Writing to a person about an occasion the Add-on declared, with Core owning frame and transport. */
51
+ notifications: "@phis/server/notifications:v1",
48
52
  };
49
53
  /**
50
54
  * What Core tells an Add-on about after the fact.
@@ -66,6 +70,7 @@ export const PHIS_ADDON_EVENTS = {
66
70
  threadStatusChanged: "thread.status.changed",
67
71
  /** The link itself is gone; what follows from that is the Add-on's own business. */
68
72
  threadLinkRemoved: "thread.link.removed",
73
+ paymentChanged: "payment.changed",
69
74
  };
70
75
  /**
71
76
  * The Core-owned service kinds an Add-on may implement.
@@ -76,6 +81,7 @@ export const PHIS_ADDON_EVENTS = {
76
81
  */
77
82
  export const PHIS_SERVICE_KINDS = {
78
83
  mediaStorage: "@phis/server/service/media-storage",
84
+ payments: "@phis/server/service/payments",
79
85
  directory: "@phis/server/service/directory",
80
86
  /**
81
87
  * Turning text in one language into text in another.
@@ -86,6 +92,15 @@ export const PHIS_SERVICE_KINDS = {
86
92
  * operator selects which one an installation uses.
87
93
  */
88
94
  translation: "@phis/server/service/translation",
95
+ /**
96
+ * Handing a Support ticket's work to a system outside the Site -- an issue tracker, a ticket desk.
97
+ *
98
+ * A kind because Core owns the question: which ticket, which task, what may leave and what comes back.
99
+ * What the implementation talks to is its own business, and several may be installed at once; a
100
+ * Supporter chooses one per forward. The interface is `PhisSupportForwardingAdapterV1` in
101
+ * `@phis/contracts/support`.
102
+ */
103
+ supportForwarding: "@phis/server/service/support-forwarding",
89
104
  };
90
105
  /**
91
106
  * The digest of the interface each service kind currently has.
@@ -107,11 +122,15 @@ export const PHIS_SERVICE_KINDS = {
107
122
  */
108
123
  export const PHIS_SERVICE_INTERFACE_VERSIONS = {
109
124
  [PHIS_SERVICE_KINDS.mediaStorage]: 1,
125
+ [PHIS_SERVICE_KINDS.payments]: 1,
110
126
  [PHIS_SERVICE_KINDS.directory]: 1,
111
127
  [PHIS_SERVICE_KINDS.translation]: 1,
128
+ [PHIS_SERVICE_KINDS.supportForwarding]: 1,
112
129
  };
113
130
  export const PHIS_SERVICE_INTERFACE_DIGESTS = {
131
+ [PHIS_SERVICE_KINDS.payments]: "6b106818d5edbfeddd484e9f571509695a1a8dbb5b82435338a094216410fcf7",
114
132
  [PHIS_SERVICE_KINDS.mediaStorage]: "45a871522511f7d77841ca8dd0113b423b3cfd4cb921f143cd1d3d116cab6952",
115
133
  [PHIS_SERVICE_KINDS.directory]: "2cac7397c7beb1b8e71fc67f5cae3be89182dddf096895570a15deafa1adbfc9",
116
134
  [PHIS_SERVICE_KINDS.translation]: "9bae2e7b92ded5cdde9321939a3bb8cd589f6e1ef8bca7bc5f65608313b3207d",
135
+ [PHIS_SERVICE_KINDS.supportForwarding]: "e9d01711a5ebdfa37ea4c6828952c87935b6e755b98ab8e69cc397933dfbed6f",
117
136
  };