@happyvertical/smrt-bookings 0.60.8 → 0.60.9

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/AGENTS.md CHANGED
@@ -7,3 +7,6 @@ and plan relationships. Commerce and payment state are separate.
7
7
  Use service commands for writes; keep this package out of generated CRUD. Apply
8
8
  its explicit PostgreSQL migration during deployment, outside request handling.
9
9
  See [README.md](README.md) for the transaction and verification contracts.
10
+
11
+ V2 is additive: a proposal's `windows` array is the reservation and the
12
+ allocation stores only its span.
package/README.md CHANGED
@@ -1,11 +1,14 @@
1
1
  # s-m-r-t Bookings
2
2
 
3
- `@happyvertical/smrt-bookings` reserves one elapsed developer hour or one
4
- operator-declared local supervision window. It does not start agents, collect
5
- payment, send notifications, or write Commerce records.
3
+ `@happyvertical/smrt-bookings` reserves dedicated developer time (an hour, day,
4
+ week or month) or one operator-declared local supervision window. It does not
5
+ start agents, collect payment, send notifications, or write Commerce records.
6
6
 
7
- Apply `migrateBookingsV1` once through the host's deployment migration ledger
8
- using a PostgreSQL database handle. Use an ordinary PostgreSQL connection or
7
+ Apply `migrateBookingsV1` and then `migrateBookingsV2` once each through the
8
+ host's deployment migration ledger using a PostgreSQL database handle. V2 is
9
+ additive: it adds offering units, prices and display fields, booking quantity,
10
+ and per-proposal windows and frozen prices, backfilling existing rows without
11
+ dropping data. Like V1 it is not idempotent. Use an ordinary PostgreSQL connection or
9
12
  pool for `BookingService`; the service owns its transaction and explicitly uses
10
13
  READ COMMITTED. It does not create schema on demand. No ORM collection, CRUD
11
14
  route, or generated CLI or MCP write is registered.
@@ -37,6 +40,38 @@ route, or generated CLI or MCP write is registered.
37
40
  Booking and historical proposal reads are scoped to the named contact or
38
41
  an operator. Booking audit history is operator-only.
39
42
 
43
+ ## Offerings, units and prices
44
+
45
+ An offering has a `kind` and a `unit` (`hour`, `day`, `week` or `month`). The
46
+ kind is the reservation semantics and the unit is the sales granularity; both
47
+ are frozen once the offering exists. `developer-hour` keeps its name for schema
48
+ compatibility but means dedicated, exclusive attention for any unit: it blocks
49
+ every overlapping reservation and needs no project or approved plan.
50
+ `supervision-day` is shared supervision capacity, always a one-day unit with
51
+ quantity one.
52
+
53
+ An offering may carry `priceCents` (integer minor units) and an ISO 4217
54
+ `currency`, set together or both null for an unpriced offering. Prices, `title`
55
+ (120 characters), `description` (2000 characters) and `public` may be changed
56
+ later. A request has a `quantity` (default 1, at most 1000). A proposal freezes
57
+ the offering's `unitPriceCents` and `currency` and the `totalCents`
58
+ (`unitPriceCents * quantity`) at proposal time, so later price changes do not
59
+ alter an offered or confirmed booking. Payment and invoicing remain host-owned.
60
+
61
+ A dedicated proposal gives either `window` (the v1 single window) or `windows`,
62
+ sorted, non-overlapping segments in one zone. The proposal `window` is the span
63
+ from the first start to the last end and `windows` are the reserved segments;
64
+ confirmation checks and the allocation reserves every segment. The segments must
65
+ measure exactly the quantity of the unit:
66
+
67
+ - `hour`: the segments' elapsed UTC time sums to `quantity` hours, so a split
68
+ hour is fine and a DST fold counts as elapsed time.
69
+ - `day`: every segment lies in the local span of `quantity` days from the first
70
+ segment's date and the segments touch exactly `quantity` distinct days.
71
+ - `week` and `month`: every segment lies in the half-open local span
72
+ `[D0, D0 + quantity weeks or months)`, with month ends constrained to the
73
+ target month's length. A segment may end at `T00:00` of the day after the span.
74
+
40
75
  Windows use an IANA zone and local timestamps of the form
41
76
  `2027-11-07T01:30-04:00`. For example, use that offset with America/New_York. The offset must be valid for that instant in the zone.
42
77
  During a DST fold the offsets identify different instants; a nonexistent wall
@@ -49,15 +84,50 @@ Use `listBookings` to find the same booking views returned by `get`. An
49
84
  optional project UUID narrows the results within the caller's existing scope.
50
85
  Operators see all bookings in their tenant. Other callers see only bookings
51
86
  for the contact the host maps to their actor. `listOfferings` exposes the
52
- offering ID, kind and enabled flag. Contacts only see enabled offerings.
87
+ offering ID, kind, unit, price, title, description, `public` and enabled flag.
88
+ There are three reader classes: operators see every offering, readers mapped to a
89
+ contact see enabled offerings, and a reader with no contact mapping (an anonymous
90
+ or prospect actor the host vouches for) sees only enabled public offerings.
91
+ `public` only affects discovery; `request` still needs a contact mapping.
92
+ `listBookings` also accepts a `contactId` filter: operators may narrow to any
93
+ contact and other callers may only name their own.
53
94
  Operators can use `listAvailability` to inspect existing provider windows
54
95
  before replacing a declaration. All three methods use the `read` authority.
55
96
 
97
+ `listOpenSlots` returns candidate slots for one offering between `from` and `to`
98
+ (at most 400 days apart), optionally for one provider and a `quantity`. It is a
99
+ pure read: it reserves nothing and takes no retry key, and confirmation still
100
+ re-checks capacity under the provider lock. Each slot carries the `windows` to
101
+ pass to `propose`. Hourly offerings step by the hour inside each declared
102
+ window; day, week and month offerings bundle the declared windows that measure
103
+ the quantity from each start date; supervision offerings list each declared
104
+ supervision window with its remaining shared units. Occupied segments are
105
+ excluded. Results are ordered by first start then provider and paged with the
106
+ opaque `nextCursor`, passed back as `after`. The host decides who may read via
107
+ `access(..., 'read')`; offerings follow the same three reader classes as
108
+ `listOfferings`, and an offering the reader cannot discover is `not-found`.
109
+
56
110
  Lists return up to 50 items by default and accept a limit from 1 to 100.
57
111
  Results are ordered by ascending UUID. Pass the returned `nextCursor` as
58
112
  `afterId` with the same filters to fetch another page. A null cursor means
59
113
  the page reached the end of the results.
60
114
 
115
+ ## Lead-bound requests
116
+
117
+ A dedicated booking may exist before the contact has a project. When the host's
118
+ authority implements the optional `verifyContact(context, contactId)`, a request
119
+ with no project calls it to confirm that the contact exists in the tenant and the
120
+ actor may act for it, and `verifyReferences` never receives that contact. The
121
+ same applies to propose, assent and confirm for such a booking, where
122
+ `verifyReferences` still checks the provider. Without `verifyContact` the contact
123
+ is passed to `verifyReferences` as before. Supervision always requires a project.
124
+ An operator later binds the project with
125
+ `attachProject(context, key, { bookingId, projectId })`: it needs an active
126
+ booking, verifies the contact and project together, is idempotent for the same
127
+ project, refuses a different one with `conflict`, and is recorded in history as
128
+ `attach-project`. Dedicated allocations never carry a project, so attaching does
129
+ not touch reserved capacity.
130
+
61
131
  Use an authenticated tenant/actor context from the host, never from a request
62
132
  body. The host's `BookingAuthority` supplies current operator permission and
63
133
  the actor's named contact mapping, and must verify that external references
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { BookingError } from './errors.js';
2
- export { migrateBookingsV1 } from './migration.js';
2
+ export { migrateBookingsV1, migrateBookingsV2 } from './migration.js';
3
3
  export { BookingService } from './service.js';
4
4
  export type * from './types.js';
5
5
  export { resolveBookingWindow } from './windows.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,mBAAmB,YAAY,CAAC;AAChC,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACtE,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAC9C,mBAAmB,YAAY,CAAC;AAChC,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC"}