@jtakeit/kit 0.5.2 → 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.
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Whether an entry is out yet, and still out — the platform's scheduled
3
+ * publishing, read the way the platform reads it.
4
+ *
5
+ * An entry's file may carry `"show_at"` (hidden until then) and `"hide_at"`
6
+ * (hidden from then on), both RFC 3339 in UTC. A build the platform makes
7
+ * rewrites such an entry to `visible: false` in its checkout before the
8
+ * generator runs, so on the platform this changes nothing. On a laptop the
9
+ * files are read as they are, and every reader here that asks `visible` — the
10
+ * drafts in `_meta.json` (meta.mjs), the content the check reads (content.mjs,
11
+ * and so lint.mjs) — asks this instead, so a post scheduled for next week is a
12
+ * draft and not a listing's missing row.
13
+ *
14
+ * `visible: false` hides an entry whatever its dates say. A value that is not
15
+ * a time is no schedule: the platform refuses one where it is written, and a
16
+ * file edited by hand that says `"next week"` is shown rather than hidden for
17
+ * a reason nobody can see.
18
+ *
19
+ * The scaffold's entry loader asks the same question in
20
+ * `template/src/lib/schedule.ts` — a copy, not an import: a scaffold is built
21
+ * against the published kit, which may not have this file yet. The two are
22
+ * held to one answer by `test/schedule.test.mjs` at the repository's root.
23
+ */
24
+
25
+ /**
26
+ * Whether the site lists the entry at `now` (milliseconds since the epoch).
27
+ * @param {{ visible?: boolean; show_at?: unknown; hide_at?: unknown } | null | undefined} document
28
+ * @param {number} [now]
29
+ * @returns {boolean}
30
+ */
31
+ export function shownAt(document, now = Date.now()) {
32
+ if (document == null) return true;
33
+ if (document.visible === false) return false;
34
+ const show = instant(document.show_at);
35
+ if (show !== undefined && now < show) return false;
36
+ const hide = instant(document.hide_at);
37
+ if (hide !== undefined && now >= hide) return false;
38
+ return true;
39
+ }
40
+
41
+ /** A time written down, or `undefined` for none and for one that is not a time. */
42
+ function instant(value) {
43
+ if (typeof value !== 'string' || value.trim() === '') return undefined;
44
+ const at = Date.parse(value);
45
+ return Number.isNaN(at) ? undefined : at;
46
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jtakeit/kit",
3
- "version": "0.5.2",
3
+ "version": "0.7.0",
4
4
  "description": "The framework-free half of a site on jtakeit: the booking form as an element and as markup, for Astro through @jtakeit/astro and for any other generator directly",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -24,6 +24,10 @@
24
24
  "types": "./types/parts.d.ts",
25
25
  "default": "./lib/booking/parts.mjs"
26
26
  },
27
+ "./booking/hooks": {
28
+ "types": "./types/hooks.d.ts",
29
+ "default": "./lib/booking/hooks.mjs"
30
+ },
27
31
  "./lead": {
28
32
  "types": "./types/lead.d.ts",
29
33
  "default": "./lib/lead/render.mjs"
@@ -7,6 +7,7 @@ export interface Named {
7
7
  rates?: { day: string; from: number; until: number; costs: number }[];
8
8
  /** `'person'` for a price times the party — a class per place, shoes; the list says so and the running total multiplies. */
9
9
  per?: 'booking' | 'person';
10
+ /** The heading a service is listed under: the words — an option's label, a category's title — not the stored value. Services with none come after every heading. */
10
11
  group?: string;
11
12
  /** For a resource: the services they perform, by slug. The element greys the others out once they are chosen. */
12
13
  does?: string[];
@@ -34,5 +35,26 @@ export interface BookingFormProps {
34
35
 
35
36
  /** What the form sends to /api/book, as `data-sends` says it. */
36
37
  export const SENDS: readonly string[];
38
+ /** What the booking form is made of, for a page that writes the markup itself (the scaffold's BookingForm.astro). */
39
+ export interface BookingForm {
40
+ /** The form's words in the page's language; the platform's answer replaces the kind's. */
41
+ words: Record<string, string>;
42
+ /** The language tag prices and days are written in. */
43
+ lang: string;
44
+ /** A checklist, or one `<select>`. */
45
+ combine: boolean;
46
+ /** The checklist's runs: headings in order, the ones with none after them, add-ons last. `meta` is «45 min · CHF 95». */
47
+ runs: { group: string; items: (Named & { meta: string })[] }[];
48
+ /** For one `<select>`, as they came. */
49
+ services: Named[];
50
+ /** The choice of whom — empty for none; `does` is the slugs joined for `data-does`. */
51
+ resources: (Omit<Named, 'does'> & { does: string })[];
52
+ /** The day input's `min` and `max`. */
53
+ today: string;
54
+ last: string;
55
+ /** Every attribute of the `<form>` — `data-booking`, `data-api`, `data-sends`, `novalidate`… — to spread on it. */
56
+ attrs: Record<string, string>;
57
+ }
58
+ export function bookingForm(props: BookingFormProps): BookingForm;
37
59
  /** The booking form as HTML, wrapped in `<jtk-booking>`. */
38
60
  export function renderBookingForm(props: BookingFormProps): string;
@@ -1,5 +1,5 @@
1
1
  /** The published content as one document, from the files in `jtk/` — what the build renders from. */
2
- export function collectContent(root: string, options?: { dir?: string }): Promise<{
2
+ export function collectContent(root: string, options?: { dir?: string; now?: number }): Promise<{
3
3
  schema_version: number;
4
4
  content_rev: number;
5
5
  site: { slug: string; locale: string; media_base: string; business: Record<string, unknown> };
@@ -7,3 +7,4 @@ export class JtkBooking extends HTMLElement {
7
7
  connectedCallback(): void;
8
8
  }
9
9
  export function defineBooking(name?: string): void;
10
+ export const BOOKING_PROTOCOL: number;
@@ -0,0 +1,12 @@
1
+ export interface Hook {
2
+ hook: string;
3
+ says: string;
4
+ }
5
+ /** Every form has these, or it cannot book. */
6
+ export const ALWAYS: readonly Hook[];
7
+ /** Shown only when the platform's answer asks; preflight fails a form without one where the business asks. */
8
+ export const ASKED: readonly (Hook & { when: string })[];
9
+ /** Where the element writes what happened; left out, that line is not said. */
10
+ export const SAID: readonly Hook[];
11
+ export const HOOKS: readonly string[];
12
+ export function lacking(form: ParentNode): string[];
package/types/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
- export { renderBookingForm, SENDS } from './booking';
2
- export type { BookingFormProps, Named } from './booking';
1
+ export { renderBookingForm, bookingForm, SENDS } from './booking';
2
+ export type { BookingFormProps, BookingForm, Named } from './booking';
3
+ export { ALWAYS, ASKED, SAID, HOOKS } from './hooks';
4
+ export type { Hook } from './hooks';
3
5
  export { renderLeadForm, inputType } from './lead';
4
6
  export type { Ask, LeadCopy, LeadPaths, LeadFormProps } from './lead';
5
7
  export { wordsFor, LOCALES } from './words';
package/types/meta.d.ts CHANGED
@@ -21,6 +21,6 @@ export function specimenAddresses(
21
21
  blocks: { type: string; views?: { key: string }[] }[],
22
22
  locales?: string[],
23
23
  ): string[];
24
- export function hiddenEntries(root?: string): Promise<string[]>;
24
+ export function hiddenEntries(root?: string, now?: number): Promise<string[]>;
25
25
  export function buildMeta(what?: MetaInput): Promise<Meta>;
26
26
  export function writeMeta(dir: string, what?: MetaInput): Promise<Meta>;