@phis/contracts 0.7.3 → 0.7.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 CHANGED
@@ -26,6 +26,15 @@ and change at different moments.
26
26
  @phis/contracts/server-capabilities the capability snapshot phis reports and the UI acts on
27
27
  @phis/contracts/site-groups the membership levels of a Site group, as both ends count them
28
28
  @phis/contracts/user-state the shapes a Module may keep a person's decisions in, and the key grammar
29
+ @phis/contracts/references the Page reference grammar both sides derive and compare
30
+ @phis/contracts/resource-links the links from a Phi record into an Add-on's own system
31
+ @phis/contracts/support the Support ticket vocabulary
32
+ @phis/contracts/threads the thread vocabulary
33
+ @phis/contracts/conditions the runtime condition descriptors
34
+ @phis/contracts/controls the Control descriptors
35
+ @phis/contracts/forms the Form descriptors and their parsers
36
+ @phis/contracts/layout the layout vocabulary
37
+ @phis/contracts/timeline the timeline entries
29
38
  ```
30
39
 
31
40
  ## Before the v1 release, interface versions stay at 1
@@ -61,13 +70,21 @@ erased at compile time apart from a handful of constants.
61
70
  What it covers:
62
71
 
63
72
  - **Manifest** -- `PhisAddonManifestV1`: identity, version, required Core capabilities, the routes an
64
- Add-on claims, the services it provides.
73
+ Add-on claims, the services it provides, the jobs it runs and the events it listens to or emits.
74
+ - **Jobs and events** -- a job carries a `PhisAddonJobSchedule` (`every` one of
75
+ `PHIS_ADDON_JOB_INTERVALS`, five minutes at the shortest, with `at` and `weekday` for the daily and
76
+ weekly steps) that Core keeps: each moment of a schedule is run by exactly one instance. An event an
77
+ Add-on emits is a `PhisAddonEmittedEventId`, its own id in front (`@scope/name:what.happened`),
78
+ declared under `emits` and sent through the `events` capability (`PhisEventsCapabilityV1`); a listener
79
+ receives a `PhisAddonEmittedEventSubject` with emitter, time, hop count, dedupe key and facts. Core's
80
+ own events keep their bare names. `PHIS_ADDON_EVENT_MAX_HOPS` bounds a chain of handlers emitting.
65
81
  - **Runtime** -- `PhisAddonRuntimeV1`, `PhisAddonHandler`, `PhisAddonRequestContext`. The artifact
66
82
  exports it under the name `phisAddon` (`PhisAddonRuntimeModuleV1`).
67
83
  - **Schema** -- `PhisAddonSchemaDescriptor` and its column, index and constraint descriptors. Tables are
68
84
  declared, not migrated by hand; phis applies them.
69
85
  - **Service kinds** -- the Core-owned kinds an Add-on may supply an implementation of: Media Storage
70
- (`PhisMediaStorageAdapter`, upload plans, object I/O) and Directory. `PHIS_SERVICE_KINDS` names them
86
+ (`PhisMediaStorageAdapter`, upload plans, object I/O), Directory, Payments, Translation and Support
87
+ Forwarding. `PHIS_SERVICE_KINDS` names them
71
88
  and `PHIS_SERVICE_INTERFACE_DIGESTS` holds the digest of each kind's current interface; Core refuses an
72
89
  Add-on built against a different digest at install.
73
90
  - **Core capabilities** -- `PHIS_CORE_CAPABILITIES`, the catalogue ids an Add-on may require. Declare
@@ -175,7 +192,8 @@ URL is built stays with each side, and phis's numeric Space kind never reaches a
175
192
  ## `/http`
176
193
 
177
194
  The request header names phis reads and the site UI writes: `PHIS_TOKEN_HEADER` (`x-phis-token`),
178
- `PHIS_SITE_KEY_HEADER`, `PHIS_AREA_HEADER`, `PHIS_REQUEST_PATH_HEADER` and `PHIS_REQUEST_SEARCH_HEADER`.
195
+ `PHIS_SITE_KEY_HEADER`, `PHIS_AREA_HEADER`, `PHIS_REQUEST_PATH_HEADER`, `PHIS_REQUEST_SEARCH_HEADER` and
196
+ `PHIS_FORM_RELAY_HEADER`.
179
197
  A disagreement here is not a type error but a 403 or 400 at runtime.
180
198
 
181
199
  ## `/locale`
package/dist/access.d.ts CHANGED
@@ -85,7 +85,6 @@ export declare const PhiUserEmailSource: {
85
85
  readonly GitHub: 23;
86
86
  };
87
87
  export type PhiUserEmailSourceValue = (typeof PhiUserEmailSource)[keyof typeof PhiUserEmailSource];
88
- export declare function isPhiUserEmailSource(value: unknown): value is PhiUserEmailSourceValue;
89
88
  /**
90
89
  * Which source a login provider's key stands for.
91
90
  *
@@ -168,6 +167,10 @@ export declare function canPhiAccessSubjectReach(subject: PhiAccessSubject, poli
168
167
  * keeping stable.
169
168
  */
170
169
  export declare function isPhiAccessPolicyProviderOwned(policy: PhiViewerAccessPolicy, ownerProviderId?: PhiRoleProviderId | null): boolean;
170
+ /** Whether a value is a provider id: `@scope/name`, with further segments allowed. */
171
+ export declare function isPhiProviderId(value: unknown): value is `@${string}/${string}`;
172
+ /** Whether a value is a Site group key, as `phis.site_groups.key` accepts it. */
173
+ export declare function isPhiSiteGroupKey(value: unknown): value is string;
171
174
  /**
172
175
  * Reads a stored descriptor back into a policy, or null.
173
176
  *
package/dist/access.js CHANGED
@@ -84,10 +84,6 @@ export const PhiUserEmailSource = {
84
84
  Microsoft: 22,
85
85
  GitHub: 23,
86
86
  };
87
- export function isPhiUserEmailSource(value) {
88
- return typeof value === "number"
89
- && Object.values(PhiUserEmailSource).includes(value);
90
- }
91
87
  /**
92
88
  * Which source a login provider's key stands for.
93
89
  *
@@ -182,6 +178,14 @@ export function isPhiAccessPolicyProviderOwned(policy, ownerProviderId) {
182
178
  }
183
179
  const PROVIDER_ID_PATTERN = /^@[^/]+\/[^/]+(?:\/[^/]+)*$/;
184
180
  const GROUP_KEY_PATTERN = /^[a-z0-9][a-z0-9._-]{0,159}$/u;
181
+ /** Whether a value is a provider id: `@scope/name`, with further segments allowed. */
182
+ export function isPhiProviderId(value) {
183
+ return typeof value === "string" && PROVIDER_ID_PATTERN.test(value);
184
+ }
185
+ /** Whether a value is a Site group key, as `phis.site_groups.key` accepts it. */
186
+ export function isPhiSiteGroupKey(value) {
187
+ return typeof value === "string" && GROUP_KEY_PATTERN.test(value);
188
+ }
185
189
  /** The same shape an Add-on manifest may declare a role under. */
186
190
  const ADDON_ROLE_NAME_PATTERN = /^[a-z][a-z0-9-]{0,63}$/;
187
191
  /**
package/dist/addon.d.ts CHANGED
@@ -12,7 +12,7 @@
12
12
  * back a constant, it is in the wrong package.
13
13
  */
14
14
  import type { PhisAddonCapabilities, PhisAddonEventHandler, PhisAddonJobHandler, PhisSiteAccess } from "./capabilities.js";
15
- import type { PhisAddonEventId, PhisCoreCapabilityId, PhisServiceKind } from "./core.js";
15
+ import type { PhisAddonEmittedEventId, PhisAddonEventId, PhisCoreCapabilityId, PhisServiceKind } from "./core.js";
16
16
  import type { PhisAddonQueryCatalog } from "./queries.js";
17
17
  import type { PhisAddonSchemaDescriptor } from "./schema.js";
18
18
  export declare const PHIS_ADDON_MANIFEST_VERSION: 1;
@@ -101,17 +101,61 @@ export type PhisAddonServiceProviderDescriptor = {
101
101
  factoryExport: string;
102
102
  requiredCoreCapabilities: PhisCoreCapabilityId[];
103
103
  };
104
+ /**
105
+ * The steps a job may run at. Every step divides the hour or the day, so the moments a schedule names
106
+ * fall on the clock -- `5m` is :00, :05, :10 -- and every instance of Core computes the same moment
107
+ * from the same clock without asking another.
108
+ *
109
+ * Nothing shorter than five minutes: a job that has to look more often than that is polling for an
110
+ * event, and an event is what it should be given. Jobs run per Site, so a short step multiplies by
111
+ * the Sites of a stage, and the services a job reads from have quotas of their own.
112
+ */
113
+ export declare const PHIS_ADDON_JOB_INTERVALS: readonly ["5m", "15m", "30m", "hourly", "daily", "weekly"];
114
+ export type PhisAddonJobInterval = (typeof PHIS_ADDON_JOB_INTERVALS)[number];
115
+ /**
116
+ * When a job wants to run. Core keeps it: the schedule is a promise, not a note for the operator.
117
+ *
118
+ * A schedule is a series of moments, and each moment is claimed by exactly one instance of Core --
119
+ * the claim is a row whose key is the moment, so the database, not a leader, decides who runs. A
120
+ * moment nobody claimed while every instance was down is run once at the next start, the latest one
121
+ * only; a job does not receive a week of missed runs after a week of downtime. A job still running
122
+ * when its next moment comes has that moment recorded as skipped rather than run beside itself, which
123
+ * is why a handler must finish well inside its own step.
124
+ */
125
+ export type PhisAddonJobSchedule = {
126
+ every: PhisAddonJobInterval;
127
+ /**
128
+ * The time of day, `HH:MM` in UTC, for `daily` and `weekly`; absent means `00:00`. Not for the shorter
129
+ * steps, which fall on the clock by themselves.
130
+ */
131
+ at?: string;
132
+ /** The ISO weekday for `weekly`, 1 (Monday) to 7 (Sunday); absent means Monday. */
133
+ weekday?: number;
134
+ };
104
135
  export type PhisAddonJobDescriptor = {
105
136
  id: string;
106
137
  handler: string;
107
138
  /**
108
- * How often this job expects to run, for the operator to read -- never a schedule Core keeps.
139
+ * When Core runs this job. Absent means it runs only when an operator starts it with `phis addon
140
+ * job`, which also runs a scheduled job out of turn.
109
141
  *
110
- * phi-server serves from several instances, so an in-process timer would fire once per instance for
111
- * one intended run. The operator schedules `phis addon job run`, which is one runner by construction,
112
- * and this field tells them what the Add-on had in mind.
142
+ * Optional like every field added after this manifest version was in the wild.
113
143
  */
114
- suggestedInterval?: "hourly" | "daily" | "weekly";
144
+ schedule?: PhisAddonJobSchedule;
145
+ };
146
+ /**
147
+ * One event this Add-on announces under its own name.
148
+ *
149
+ * Declared like a job or a route: the id, its emitter's own id in front, is part of the manifest and
150
+ * therefore of the digest, so what an installed Add-on can say to the others is readable at install.
151
+ * Emitting an undeclared event is refused. `audience` says who may listen: every Add-on of the Site
152
+ * that declares a handler for it, which is the default, because the operator installed each of them;
153
+ * or `self`, for an Add-on that uses the outbox to hand work to its own handler after the fact.
154
+ */
155
+ export type PhisAddonEmittedEventDescriptor = {
156
+ id: PhisAddonEmittedEventId;
157
+ description?: string;
158
+ audience?: "stage" | "self";
115
159
  };
116
160
  /**
117
161
  * One setting an operator may configure for this Add-on.
@@ -259,6 +303,13 @@ export type PhisAddonManifestV1 = {
259
303
  * Add-on that declares them and links nothing is told nothing.
260
304
  */
261
305
  eventHandlers?: PhisAddonEventHandlerDescriptor[];
306
+ /**
307
+ * The events this Add-on emits. Absent means none, and an emit of anything is refused.
308
+ *
309
+ * Optional like `eventHandlers`. Declaring an event is not delivering it: a row is written only for
310
+ * an Add-on of the Site that declares a handler for the id, and none at all where nobody listens.
311
+ */
312
+ emits?: PhisAddonEmittedEventDescriptor[];
262
313
  /**
263
314
  * What an operator may configure. Absent means nothing is.
264
315
  *
package/dist/addon.js CHANGED
@@ -13,3 +13,13 @@
13
13
  */
14
14
  export const PHIS_ADDON_MANIFEST_VERSION = 1;
15
15
  export const PHIS_ADDON_ABI_VERSION = 1;
16
+ /**
17
+ * The steps a job may run at. Every step divides the hour or the day, so the moments a schedule names
18
+ * fall on the clock -- `5m` is :00, :05, :10 -- and every instance of Core computes the same moment
19
+ * from the same clock without asking another.
20
+ *
21
+ * Nothing shorter than five minutes: a job that has to look more often than that is polling for an
22
+ * event, and an event is what it should be given. Jobs run per Site, so a short step multiplies by
23
+ * the Sites of a stage, and the services a job reads from have quotas of their own.
24
+ */
25
+ export const PHIS_ADDON_JOB_INTERVALS = ["5m", "15m", "30m", "hourly", "daily", "weekly"];
@@ -12,7 +12,7 @@
12
12
  * at the point where it made the mistake.
13
13
  */
14
14
  import type { PhisAssetsCapabilityV1 } from "./assets.js";
15
- import type { PhisAddonThreadEventId } from "./core.js";
15
+ import type { PhisAddonEmittedEventId, PhisAddonThreadEventId } from "./core.js";
16
16
  import type { PhisPaymentChangedEventFacts, PhisPaymentNotificationsCapabilityV1, PhisPaymentsCapabilityV1 } from "./payments.js";
17
17
  import type { PhisDataCapabilityV1 } from "./queries.js";
18
18
  import type { PhisResourceLinksCapabilityV1 } from "./resource-links.js";
@@ -109,6 +109,52 @@ export type PhisAddonCapabilities = {
109
109
  paymentNotifications?: PhisPaymentNotificationsCapabilityV1;
110
110
  /** Writing to a person about an occasion this Add-on declared; Core owns the frame and the sending. */
111
111
  notifications?: PhisNotificationsCapabilityV1;
112
+ /** Announcing an event this Add-on declared under its own name; Core owns the outbox and the delivery. */
113
+ events?: PhisEventsCapabilityV1;
114
+ };
115
+ /**
116
+ * The events capability: `@phis/server/events:v1`.
117
+ *
118
+ * An Add-on says that something happened; Core writes one outbox row per Add-on of this Site that
119
+ * declares a handler for the event, and delivers them the way it delivers its own events -- after the
120
+ * fact, in order, retried until they arrive. Emitting is only a write to the outbox, so it is cheap and
121
+ * never waits for a listener.
122
+ *
123
+ * `facts` are values about what happened, as JSON, bounded in size, and never references into another
124
+ * Add-on's tables: a listener runs with authority over its own tables only, and a fact it cannot read
125
+ * is a fact it should have been handed. `dedupeKey` makes a repeated emit safe -- a second call with
126
+ * the same key writes nothing for a listener already owed the first.
127
+ *
128
+ * Refused: an event the manifest does not declare under `emits`, a call from an unverified hook, and
129
+ * an emit from a handler already at `PHIS_ADDON_EVENT_MAX_HOPS`. Answers how many listeners were
130
+ * written for; zero means nobody on this Site listens, which is not a failure.
131
+ */
132
+ export type PhisEventsCapabilityV1 = {
133
+ emit(input: PhisAddonEmitEventInput): Promise<{
134
+ recipients: number;
135
+ }>;
136
+ };
137
+ export type PhisAddonEmitEventInput = {
138
+ event: PhisAddonEmittedEventId;
139
+ facts?: Readonly<Record<string, unknown>>;
140
+ /** Unique within the Site and the event; a second emit with the same key writes nothing new. */
141
+ dedupeKey?: string;
142
+ };
143
+ /**
144
+ * What a listener is handed about an event another Add-on -- or the same one -- emitted.
145
+ *
146
+ * The emitter's id says who, `occurredAt` when (the moment the outbox row was written), `hop` how many
147
+ * handlers the chain has passed through so far, and `facts` what the emitter said. `dedupeKey` is the
148
+ * emitter's, where it gave one; the delivery's own key, stable across retries, is `deliveryId` on the
149
+ * context, as for every event.
150
+ */
151
+ export type PhisAddonEmittedEventSubject = {
152
+ kind: "emitted";
153
+ emitter: string;
154
+ occurredAt: string;
155
+ hop: number;
156
+ dedupeKey: string | null;
157
+ facts: Readonly<Record<string, unknown>>;
112
158
  };
113
159
  /**
114
160
  * The roles capability: `@phis/server/roles:v1`.
@@ -399,8 +445,9 @@ export type PhisSettingsCapabilityV1 = {
399
445
  *
400
446
  * No request, no headers, and above all no actor: a job is the Add-on acting on its own data, not a
401
447
  * person acting through it. That is why the row-level ladder does not apply inside one, and why a job
402
- * is reachable only from `phis addon job run` and never from a route -- an Add-on that could start its
403
- * own job from a request would have built itself a way around the ladder.
448
+ * is started only by Core's scheduler at the moments its `schedule` names, or by an operator with
449
+ * `phis addon job`, and never from a route -- an Add-on that could start its own job from a request
450
+ * would have built itself a way around the ladder.
404
451
  */
405
452
  export type PhisAddonJobContext = {
406
453
  addonId: string;
@@ -434,7 +481,10 @@ export type PhisAddonEventFacts = {
434
481
  threadId: number;
435
482
  messageId: number | null;
436
483
  };
437
- } | PhisPaymentChangedEventFacts;
484
+ } | PhisPaymentChangedEventFacts | {
485
+ event: PhisAddonEmittedEventId;
486
+ subject: PhisAddonEmittedEventSubject;
487
+ };
438
488
  export type PhisAddonEventContext = PhisAddonEventFacts & {
439
489
  addonId: string;
440
490
  /** This delivery, stable across retries, so a handler can recognise one it already settled. */
package/dist/core.d.ts CHANGED
@@ -50,6 +50,8 @@ export declare const PHIS_CORE_CAPABILITIES: {
50
50
  readonly paymentNotifications: "@phis/server/payment-notifications:v1";
51
51
  /** Writing to a person about an occasion the Add-on declared, with Core owning frame and transport. */
52
52
  readonly notifications: "@phis/server/notifications:v1";
53
+ /** Announcing an event this Add-on declared under its own name, for the Add-ons of this Site that listen. */
54
+ readonly events: "@phis/server/events:v1";
53
55
  };
54
56
  export type PhisCoreCapabilityId = (typeof PHIS_CORE_CAPABILITIES)[keyof typeof PHIS_CORE_CAPABILITIES];
55
57
  /**
@@ -75,8 +77,27 @@ export declare const PHIS_ADDON_EVENTS: {
75
77
  readonly paymentChanged: "payment.changed";
76
78
  };
77
79
  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"];
80
+ /** The events Core itself emits: the thread events and the Payment event. */
81
+ export type PhisCoreEventId = PhisAddonThreadEventId | PhisPaymentChangedEventFacts["event"];
82
+ /**
83
+ * An event an Add-on emits, named under the Add-on that emits it: `@scope/name:what.happened`.
84
+ *
85
+ * The emitter's own id is the first half, so two Add-ons cannot name the same event and a reader of
86
+ * a manifest sees who announces it without looking anything up. The second half is the Add-on's own
87
+ * word for it, `^[a-z][a-z0-9.-]*$`, the shape Core's own event names have. Core's names stay bare:
88
+ * a bare name is Core's, a prefixed one is an Add-on's, and the two can never be confused.
89
+ */
90
+ export type PhisAddonEmittedEventId = `@${string}/${string}:${string}`;
91
+ /** Delivered v1 event vocabulary: Core's own events, and what an installed Add-on emits. */
92
+ export type PhisAddonEventId = PhisCoreEventId | PhisAddonEmittedEventId;
93
+ /**
94
+ * How many Add-ons an event may pass through.
95
+ *
96
+ * An Add-on that emits inside an event handler writes an event with the incoming hop plus one. A
97
+ * handler that answers every event with an event, met by another doing the same, would otherwise
98
+ * write forever; at this depth the emit is refused and logged instead of delivered.
99
+ */
100
+ export declare const PHIS_ADDON_EVENT_MAX_HOPS = 4;
80
101
  /**
81
102
  * The Core-owned service kinds an Add-on may implement.
82
103
  *
package/dist/core.js CHANGED
@@ -49,6 +49,8 @@ export const PHIS_CORE_CAPABILITIES = {
49
49
  paymentNotifications: "@phis/server/payment-notifications:v1",
50
50
  /** Writing to a person about an occasion the Add-on declared, with Core owning frame and transport. */
51
51
  notifications: "@phis/server/notifications:v1",
52
+ /** Announcing an event this Add-on declared under its own name, for the Add-ons of this Site that listen. */
53
+ events: "@phis/server/events:v1",
52
54
  };
53
55
  /**
54
56
  * What Core tells an Add-on about after the fact.
@@ -72,6 +74,14 @@ export const PHIS_ADDON_EVENTS = {
72
74
  threadLinkRemoved: "thread.link.removed",
73
75
  paymentChanged: "payment.changed",
74
76
  };
77
+ /**
78
+ * How many Add-ons an event may pass through.
79
+ *
80
+ * An Add-on that emits inside an event handler writes an event with the incoming hop plus one. A
81
+ * handler that answers every event with an event, met by another doing the same, would otherwise
82
+ * write forever; at this depth the emit is refused and logged instead of delivered.
83
+ */
84
+ export const PHIS_ADDON_EVENT_MAX_HOPS = 4;
75
85
  /**
76
86
  * The Core-owned service kinds an Add-on may implement.
77
87
  *
@@ -294,7 +294,7 @@ export type PhiMediaUploadCompletionInput = {
294
294
  */
295
295
  export declare const PHIS_MEDIA_CHECKSUM_ALGORITHMS: readonly ["sha256", "sha512", "sha256-composite", "xxhash128", "crc64nvme", "crc32c", "crc32", "md5"];
296
296
  export type PhisMediaChecksumAlgorithm = (typeof PHIS_MEDIA_CHECKSUM_ALGORITHMS)[number];
297
- export { PHIS_MEDIA_UPLOAD_MAX_PART_COUNT, PHIS_MEDIA_UPLOAD_PART_SIZE_BYTES, formatPhiMediaCompositeChecksum, parsePhiMediaCompositeChecksum, resolvePhiMediaUploadPartCount, resolvePhiMediaUploadPartSizeBytes, } from "./media.js";
297
+ export { PHIS_MEDIA_UPLOAD_MAX_PART_COUNT, PHIS_MEDIA_UPLOAD_PART_SIZE_BYTES, formatPhiMediaCompositeChecksum, resolvePhiMediaUploadPartCount, resolvePhiMediaUploadPartSizeBytes, } from "./media.js";
298
298
  /**
299
299
  * The digests strong enough to answer "is this the same file", rather than only "did it arrive intact".
300
300
  *
@@ -36,7 +36,7 @@ export const PHIS_MEDIA_CHECKSUM_ALGORITHMS = [
36
36
  * that request is signed. So the rule cannot sit behind the Add-on surface, and it must not be written
37
37
  * twice -- two copies of a division are two divisions the day one of them is edited.
38
38
  */
39
- export { PHIS_MEDIA_UPLOAD_MAX_PART_COUNT, PHIS_MEDIA_UPLOAD_PART_SIZE_BYTES, formatPhiMediaCompositeChecksum, parsePhiMediaCompositeChecksum, resolvePhiMediaUploadPartCount, resolvePhiMediaUploadPartSizeBytes, } from "./media.js";
39
+ export { PHIS_MEDIA_UPLOAD_MAX_PART_COUNT, PHIS_MEDIA_UPLOAD_PART_SIZE_BYTES, formatPhiMediaCompositeChecksum, resolvePhiMediaUploadPartCount, resolvePhiMediaUploadPartSizeBytes, } from "./media.js";
40
40
  /**
41
41
  * The digests strong enough to answer "is this the same file", rather than only "did it arrive intact".
42
42
  *
package/dist/media.d.ts CHANGED
@@ -265,8 +265,3 @@ export declare function resolvePhiMediaUploadPartCount(sizeBytes: number): numbe
265
265
  * SHA-256 that simply failed to match. Naming the division makes a mismatch legible instead of puzzling.
266
266
  */
267
267
  export declare function formatPhiMediaCompositeChecksum(digestHex: string, partCount: number, partSizeBytes: number): string;
268
- export declare function parsePhiMediaCompositeChecksum(value: string): {
269
- digest: string;
270
- partCount: number;
271
- partSizeBytes: number;
272
- } | null;
package/dist/media.js CHANGED
@@ -409,13 +409,3 @@ export function resolvePhiMediaUploadPartCount(sizeBytes) {
409
409
  export function formatPhiMediaCompositeChecksum(digestHex, partCount, partSizeBytes) {
410
410
  return `${digestHex.toLowerCase()}-${partCount}-${partSizeBytes}`;
411
411
  }
412
- export function parsePhiMediaCompositeChecksum(value) {
413
- const match = /^([0-9a-f]{64})-(\d{1,5})-(\d{1,12})$/u.exec(value.trim().toLowerCase());
414
- if (!match)
415
- return null;
416
- const partCount = Number(match[2]);
417
- const partSizeBytes = Number(match[3]);
418
- if (partCount < 1 || partSizeBytes < 1)
419
- return null;
420
- return { digest: match[1], partCount, partSizeBytes };
421
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phis/contracts",
3
- "version": "0.7.3",
3
+ "version": "0.7.5",
4
4
  "license": "Apache-2.0",
5
5
  "type": "module",
6
6
  "publishConfig": {