@hollware/core 0.1.0 → 0.2.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/dist/index.d.mts CHANGED
@@ -288,6 +288,21 @@ interface SiteDefinition {
288
288
  readonly collections: Readonly<Record<string, CollectionDefinition>>;
289
289
  /** Guidance for whoever writes anywhere in the site, in Markdown. */
290
290
  readonly styleGuide?: string;
291
+ /** The public origin of the site, such as `https://example.com`: entries' URLs start with it. */
292
+ readonly url?: string;
293
+ /** Who hears about publications (ADR-025). */
294
+ readonly events?: {
295
+ /** Each gets every event, signed with HMAC-SHA256 by the Worker secret named in `secret`. */
296
+ readonly webhooks?: readonly {
297
+ readonly name: string;
298
+ readonly url: string;
299
+ readonly secret: string;
300
+ }[];
301
+ /** Tells IndexNow the URLs that changed; `key` names the Worker secret with the key. */
302
+ readonly indexNow?: {
303
+ readonly key: string;
304
+ };
305
+ };
291
306
  }
292
307
  /** What `hollware.config.ts` exports. */
293
308
  interface HollwareConfig {
@@ -318,6 +333,22 @@ interface ResolvedSite {
318
333
  readonly defaultLocale: string;
319
334
  readonly styleGuide: string | null;
320
335
  readonly collections: ReadonlyMap<string, ResolvedCollection>;
336
+ /** The public origin of the site, or `null`. */
337
+ readonly url: string | null;
338
+ readonly events: ResolvedEvents;
339
+ }
340
+ /** A webhook of a site; `secret` names the Worker secret that signs its deliveries. */
341
+ interface ResolvedWebhook {
342
+ readonly name: string;
343
+ readonly url: string;
344
+ readonly secret: string;
345
+ }
346
+ /** Who hears about the publications of a site. */
347
+ interface ResolvedEvents {
348
+ readonly webhooks: readonly ResolvedWebhook[];
349
+ readonly indexNow: {
350
+ readonly key: string;
351
+ } | null;
321
352
  }
322
353
  /** The config once resolved, at the start of the Worker. v0.1 holds exactly one site. */
323
354
  interface ResolvedConfig {
@@ -960,10 +991,13 @@ export declare class HollwareError extends Error {
960
991
  */
961
992
  /** The kinds of ids and their prefixes. A new kind adds its prefix here. */
962
993
  export declare const ID_PREFIXES: {
994
+ readonly delivery: 'dlv';
963
995
  readonly entry: 'ent';
996
+ readonly event: 'evt';
964
997
  readonly publication: 'pub';
965
998
  readonly revision: 'rev';
966
999
  readonly revisionTag: 'tag';
1000
+ readonly schedule: 'sch';
967
1001
  readonly token: 'tok';
968
1002
  readonly user: 'usr';
969
1003
  };
@@ -1450,6 +1484,11 @@ export declare const publish: OperationSpec<"publish", z.ZodObject<{
1450
1484
  contract: z.ZodString;
1451
1485
  path: z.ZodString;
1452
1486
  }, z.core.$strip>>>;
1487
+ group: z.ZodDefault<z.ZodArray<z.ZodObject<{
1488
+ id: z.ZodString;
1489
+ version: z.ZodNumber;
1490
+ }, z.core.$strict>>>;
1491
+ publishAt: z.ZodOptional<z.ZodISODateTime>;
1453
1492
  }, z.core.$strict>, z.ZodObject<{
1454
1493
  outcome: z.ZodEnum<{
1455
1494
  blocked: "blocked";
@@ -1457,6 +1496,7 @@ export declare const publish: OperationSpec<"publish", z.ZodObject<{
1457
1496
  needs_acknowledgement: "needs_acknowledgement";
1458
1497
  not_found: "not_found";
1459
1498
  published: "published";
1499
+ scheduled: "scheduled";
1460
1500
  }>;
1461
1501
  entry: z.ZodOptional<z.ZodObject<{
1462
1502
  id: z.ZodString;
@@ -1510,6 +1550,7 @@ export declare const publish: OperationSpec<"publish", z.ZodObject<{
1510
1550
  currentVersion: z.ZodNumber;
1511
1551
  headRevision: z.ZodString;
1512
1552
  }, z.core.$strip>>;
1553
+ failedEntry: z.ZodOptional<z.ZodString>;
1513
1554
  }, z.core.$strip>>;
1514
1555
  export declare const unpublish: OperationSpec<"unpublish", z.ZodObject<{
1515
1556
  id: z.ZodString;
@@ -1809,6 +1850,11 @@ export declare const OPERATIONS: readonly [OperationSpec<"create_draft", z.ZodOb
1809
1850
  contract: z.ZodString;
1810
1851
  path: z.ZodString;
1811
1852
  }, z.core.$strip>>>;
1853
+ group: z.ZodDefault<z.ZodArray<z.ZodObject<{
1854
+ id: z.ZodString;
1855
+ version: z.ZodNumber;
1856
+ }, z.core.$strict>>>;
1857
+ publishAt: z.ZodOptional<z.ZodISODateTime>;
1812
1858
  }, z.core.$strict>, z.ZodObject<{
1813
1859
  outcome: z.ZodEnum<{
1814
1860
  blocked: "blocked";
@@ -1816,6 +1862,7 @@ export declare const OPERATIONS: readonly [OperationSpec<"create_draft", z.ZodOb
1816
1862
  needs_acknowledgement: "needs_acknowledgement";
1817
1863
  not_found: "not_found";
1818
1864
  published: "published";
1865
+ scheduled: "scheduled";
1819
1866
  }>;
1820
1867
  entry: z.ZodOptional<z.ZodObject<{
1821
1868
  id: z.ZodString;
@@ -1869,6 +1916,7 @@ export declare const OPERATIONS: readonly [OperationSpec<"create_draft", z.ZodOb
1869
1916
  currentVersion: z.ZodNumber;
1870
1917
  headRevision: z.ZodString;
1871
1918
  }, z.core.$strip>>;
1919
+ failedEntry: z.ZodOptional<z.ZodString>;
1872
1920
  }, z.core.$strip>>, OperationSpec<"unpublish", z.ZodObject<{
1873
1921
  id: z.ZodString;
1874
1922
  }, z.core.$strict>, z.ZodObject<{
@@ -2026,6 +2074,33 @@ export declare function isPersonalToken(value: string): boolean;
2026
2074
  /** The SHA-256 of a token, in lowercase hex: what the `tokens` table stores and looks up. */
2027
2075
  export declare function hashPersonalToken(token: string): Promise<string>;
2028
2076
  //#endregion
2077
+ //#region src/webhooks.d.ts
2078
+ /**
2079
+ * The signature of Hollware's webhooks (ADR-025), for the Worker that sends them and for whoever
2080
+ * receives them: HMAC-SHA256 of `<timestamp>.<body>` with the webhook's secret, sent as
2081
+ * `hollware-signature: v1=<hex>` with `hollware-timestamp` (seconds since the epoch) and
2082
+ * `hollware-event-id`. Web Crypto only, so it runs in any runtime.
2083
+ */
2084
+ export declare const SIGNATURE_HEADER = "hollware-signature";
2085
+ export declare const TIMESTAMP_HEADER = "hollware-timestamp";
2086
+ export declare const EVENT_ID_HEADER = "hollware-event-id";
2087
+ /** The value of `hollware-signature` for a body sent at `timestamp` (seconds). */
2088
+ export declare function signWebhook(secret: string, timestamp: number, body: string): Promise<string>;
2089
+ export interface WebhookCheck {
2090
+ readonly secret: string;
2091
+ /** The raw body, as received: a parsed and re-serialized body no longer matches. */
2092
+ readonly body: string;
2093
+ readonly headers: Headers;
2094
+ /** Now, in seconds since the epoch; the system clock when absent. */
2095
+ readonly now?: number;
2096
+ readonly toleranceSeconds?: number;
2097
+ }
2098
+ /**
2099
+ * Whether a request is a webhook of Hollware: its signature matches the body and the secret, and
2100
+ * its timestamp is within the tolerance, so a captured request cannot be replayed later.
2101
+ */
2102
+ export declare function verifyWebhook(check: WebhookCheck): Promise<boolean>;
2103
+ //#endregion
2029
2104
  //#region src/index.d.ts
2030
2105
  export declare const name = "@hollware/core";
2031
2106
  //#endregion
package/dist/index.mjs CHANGED
@@ -69,7 +69,7 @@ const MAX_MARKDOWN_BYTES_CEILING = 786432;
69
69
  * letter, at most 63 characters. A component name is what Markdown writes after the colons
70
70
  * (`::callout`). A site key has its own rule (`site-key.ts`).
71
71
  */
72
- const NAME = /^[a-z][a-z0-9-]{0,62}$/;
72
+ const NAME$1 = /^[a-z][a-z0-9-]{0,62}$/;
73
73
  /** A language tag: a language, then an optional script and region: `es`, `pt-BR`, `zh-Hant-TW`. */
74
74
  const LOCALE_TAG = /^[a-z]{2,3}(?:-[A-Z][a-z]{3})?(?:-(?:[A-Z]{2}|[0-9]{3}))?$/;
75
75
  /** The id of a contract: lowercase words joined by single hyphens. */
@@ -1224,7 +1224,7 @@ function readTranslations(path, raw, schema, problems) {
1224
1224
  }
1225
1225
  const COMPONENT_FIX = "Declare it with defineComponent({ props: z.strictObject({ … }) }).";
1226
1226
  function readComponent(path, name, raw, problems) {
1227
- if (!NAME.test(name)) problems.add(path, `${quote(name)} is not a valid component name`, nameFix("callout"));
1227
+ if (!NAME$1.test(name)) problems.add(path, `${quote(name)} is not a valid component name`, nameFix("callout"));
1228
1228
  if (!isRecord$1(raw)) {
1229
1229
  problems.add(path, wrongType(raw, "a component"), COMPONENT_FIX);
1230
1230
  return null;
@@ -1347,7 +1347,7 @@ function readMaxMarkdownBytes(path, raw, problems) {
1347
1347
  * problem already: `resolveConfig` throws when there is any, so nothing else keeps track of them.
1348
1348
  */
1349
1349
  function readCollection(path, name, raw, site, problems) {
1350
- if (!NAME.test(name)) problems.add(path, `${quote(name)} is not a valid collection name`, nameFix("blog"));
1350
+ if (!NAME$1.test(name)) problems.add(path, `${quote(name)} is not a valid collection name`, nameFix("blog"));
1351
1351
  if (!isRecord$1(raw)) {
1352
1352
  problems.add(path, wrongType(raw, "a collection"), "Declare it with defineCollection({ schema, routes, … }).");
1353
1353
  return null;
@@ -1376,6 +1376,90 @@ function readCollection(path, name, raw, site, problems) {
1376
1376
  };
1377
1377
  }
1378
1378
  //#endregion
1379
+ //#region src/config/events.ts
1380
+ /** The name of a Worker secret or variable: what `wrangler secret put` takes. */
1381
+ const BINDING_NAME = /^[A-Z][A-Z0-9_]*$/;
1382
+ /** A subscriber's name: lowercase words joined by hyphens. */
1383
+ const NAME = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
1384
+ /** The public URL of a site: an absolute http or https URL with no path, query or fragment. */
1385
+ function readSiteUrl(path, raw, problems) {
1386
+ if (raw === void 0) return null;
1387
+ const fix = "Write the public origin of the site, such as \"https://example.com\".";
1388
+ if (typeof raw !== "string" || !URL.canParse(raw)) {
1389
+ problems.add(path, wrongType(raw, "an absolute URL"), fix);
1390
+ return null;
1391
+ }
1392
+ const url = new URL(raw);
1393
+ if (url.protocol !== "https:" && url.protocol !== "http:" || url.pathname !== "/" || url.search !== "" || url.hash !== "") {
1394
+ problems.add(path, `${quote(raw)} is not an origin`, fix);
1395
+ return null;
1396
+ }
1397
+ return url.origin;
1398
+ }
1399
+ function readBindingName(path, raw, problems) {
1400
+ if (typeof raw === "string" && BINDING_NAME.test(raw)) return raw;
1401
+ problems.add(path, wrongType(raw, "the name of a Worker secret"), "Name the secret that holds the value, such as \"WEBHOOK_SECRET\", and set it with wrangler secret put.");
1402
+ return null;
1403
+ }
1404
+ function readWebhook(path, raw, problems) {
1405
+ if (!isRecord$1(raw)) {
1406
+ problems.add(path, wrongType(raw, "a webhook"), "Declare it as { name: \"newsletter\", url: \"https://…\", secret: \"WEBHOOK_SECRET\" }.");
1407
+ return null;
1408
+ }
1409
+ const name = raw["name"];
1410
+ if (typeof name !== "string" || !NAME.test(name)) problems.add(keyPath(path, "name"), wrongType(name, "a name"), "Name it with lowercase words joined by hyphens, such as \"newsletter\".");
1411
+ const url = raw["url"];
1412
+ const urlOk = typeof url === "string" && URL.canParse(url) && new URL(url).protocol === "https:";
1413
+ if (!urlOk) problems.add(keyPath(path, "url"), wrongType(url, "an https URL"), "Give the https URL that receives the events.");
1414
+ const secret = readBindingName(keyPath(path, "secret"), raw["secret"], problems);
1415
+ if (typeof name !== "string" || !NAME.test(name) || !urlOk || secret === null) return null;
1416
+ return {
1417
+ name,
1418
+ url: String(url),
1419
+ secret
1420
+ };
1421
+ }
1422
+ /** Who hears about publications (ADR-025): webhooks, and IndexNow when the site has a URL. */
1423
+ function readEvents(path, raw, siteUrl, problems) {
1424
+ const events = {
1425
+ webhooks: [],
1426
+ indexNow: null
1427
+ };
1428
+ if (raw === void 0) return events;
1429
+ if (!isRecord$1(raw)) {
1430
+ problems.add(path, wrongType(raw, "an object"), "Declare { webhooks: [ … ], indexNow: { … } }.");
1431
+ return events;
1432
+ }
1433
+ const webhooks = raw["webhooks"];
1434
+ if (webhooks !== void 0) {
1435
+ if (!Array.isArray(webhooks)) problems.add(keyPath(path, "webhooks"), wrongType(webhooks, "a list"), "List the webhooks.");
1436
+ else {
1437
+ const seen = /* @__PURE__ */ new Set();
1438
+ webhooks.forEach((item, index) => {
1439
+ const webhook = readWebhook(itemPath(keyPath(path, "webhooks"), index), item, problems);
1440
+ if (webhook === null) return;
1441
+ if (seen.has(webhook.name)) {
1442
+ problems.add(itemPath(keyPath(path, "webhooks"), index), `repeats the name ${quote(webhook.name)}`, "Give each webhook a name of its own.");
1443
+ return;
1444
+ }
1445
+ seen.add(webhook.name);
1446
+ events.webhooks.push(webhook);
1447
+ });
1448
+ }
1449
+ }
1450
+ const indexNow = raw["indexNow"];
1451
+ if (indexNow !== void 0) {
1452
+ const indexPath = keyPath(path, "indexNow");
1453
+ if (!isRecord$1(indexNow)) problems.add(indexPath, wrongType(indexNow, "an object"), "Declare { key: \"INDEXNOW_KEY\" }.");
1454
+ else {
1455
+ const key = readBindingName(keyPath(indexPath, "key"), indexNow["key"], problems);
1456
+ if (siteUrl === null) problems.add(indexPath, "needs the URL of the site", "Set url on the site: IndexNow is told the public URLs of the entries.");
1457
+ else if (key !== null) events.indexNow = { key };
1458
+ }
1459
+ }
1460
+ return events;
1461
+ }
1462
+ //#endregion
1379
1463
  //#region src/config/site-key.ts
1380
1464
  /**
1381
1465
  * The key of a site, which is the id of its row in the database: lowercase letters and digits
@@ -1480,12 +1564,16 @@ function readSite(key, raw, problems) {
1480
1564
  localesOk: ok,
1481
1565
  routes: /* @__PURE__ */ new Map()
1482
1566
  }, problems);
1567
+ const styleGuide = readOptionalString(keyPath(path, "styleGuide"), raw["styleGuide"], "Write the guidance as a Markdown string, or remove it.", problems);
1568
+ const url = readSiteUrl(keyPath(path, "url"), raw["url"], problems);
1483
1569
  return {
1484
1570
  key,
1485
1571
  locales,
1486
1572
  defaultLocale,
1487
- styleGuide: readOptionalString(keyPath(path, "styleGuide"), raw["styleGuide"], "Write the guidance as a Markdown string, or remove it.", problems),
1488
- collections
1573
+ styleGuide,
1574
+ collections,
1575
+ url,
1576
+ events: readEvents(keyPath(path, "events"), raw["events"], url, problems)
1489
1577
  };
1490
1578
  }
1491
1579
  function readSites(raw, problems) {
@@ -1577,10 +1665,13 @@ var HollwareError = class extends Error {
1577
1665
  const CROCKFORD = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
1578
1666
  /** The kinds of ids and their prefixes. A new kind adds its prefix here. */
1579
1667
  const ID_PREFIXES = {
1668
+ delivery: "dlv",
1580
1669
  entry: "ent",
1670
+ event: "evt",
1581
1671
  publication: "pub",
1582
1672
  revision: "rev",
1583
1673
  revisionTag: "tag",
1674
+ schedule: "sch",
1584
1675
  token: "tok",
1585
1676
  user: "usr"
1586
1677
  };
@@ -3196,15 +3287,21 @@ const validate = defineOperation({
3196
3287
  const publish = defineOperation({
3197
3288
  name: "publish",
3198
3289
  title: "Publish an entry",
3199
- description: "Publishes the latest revision of an entry, if the entry is still at the version you read. It is blocked while the contracts report errors. Warnings have to be acknowledged: an answer of needs_acknowledgement lists them, and publish again with them in acknowledgeWarnings.",
3290
+ description: "Publishes the latest revision of an entry, if the entry is still at the version you read, and with group, other entries in the same step. It is blocked while the contracts report errors. Warnings have to be acknowledged: an answer of needs_acknowledgement lists them, and publish again with them in acknowledgeWarnings. Publish entries that link to each other as one group.",
3200
3291
  input: z.strictObject({
3201
3292
  id: entryId,
3202
3293
  version,
3203
- acknowledgeWarnings: z.array(acknowledgedWarning).default([]).describe("The warnings you accept, as needs_acknowledgement listed them.")
3294
+ acknowledgeWarnings: z.array(acknowledgedWarning).default([]).describe("The warnings you accept, as needs_acknowledgement listed them."),
3295
+ group: z.array(z.strictObject({
3296
+ id: entryId,
3297
+ version
3298
+ })).max(99).default([]).describe("Other entries to publish in the same step, each with the version you read: all of them are published, or none. A link between two of them counts as published, and an acknowledged warning holds for every entry that reports it."),
3299
+ publishAt: z.iso.datetime({ offset: true }).optional().describe("Publish at this time instead of now, as an ISO 8601 date and time: the entry is checked now and waits as scheduled, and it is published within a minute of the time, if nobody changes it before. One entry at a time, never with group.")
3204
3300
  }),
3205
3301
  output: z.object({
3206
3302
  outcome: z.enum([
3207
3303
  "published",
3304
+ "scheduled",
3208
3305
  "blocked",
3209
3306
  "needs_acknowledgement",
3210
3307
  "conflict",
@@ -3212,12 +3309,14 @@ const publish = defineOperation({
3212
3309
  ]),
3213
3310
  entry: entrySummary.optional(),
3214
3311
  verdict: verdict.optional(),
3215
- conflict: conflict.optional()
3312
+ conflict: conflict.optional(),
3313
+ failedEntry: entryId.optional().describe("In a group, the entry that kept the group from being published.")
3216
3314
  }),
3217
3315
  scopes: ["content:publish"],
3218
3316
  effect: "write",
3219
3317
  outcomes: {
3220
3318
  published: { isError: false },
3319
+ scheduled: { isError: false },
3221
3320
  blocked: { isError: true },
3222
3321
  needs_acknowledgement: { isError: false },
3223
3322
  conflict: { isError: true },
@@ -3303,7 +3402,51 @@ async function hashPersonalToken(token) {
3303
3402
  return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("");
3304
3403
  }
3305
3404
  //#endregion
3405
+ //#region src/webhooks.ts
3406
+ /**
3407
+ * The signature of Hollware's webhooks (ADR-025), for the Worker that sends them and for whoever
3408
+ * receives them: HMAC-SHA256 of `<timestamp>.<body>` with the webhook's secret, sent as
3409
+ * `hollware-signature: v1=<hex>` with `hollware-timestamp` (seconds since the epoch) and
3410
+ * `hollware-event-id`. Web Crypto only, so it runs in any runtime.
3411
+ */
3412
+ const SIGNATURE_HEADER = "hollware-signature";
3413
+ const TIMESTAMP_HEADER = "hollware-timestamp";
3414
+ const EVENT_ID_HEADER = "hollware-event-id";
3415
+ /** How far a timestamp may be from the receiver's clock, by default: five minutes. */
3416
+ const TOLERANCE_SECONDS = 300;
3417
+ const encoder = new TextEncoder();
3418
+ async function hmac(secret, text) {
3419
+ const key = await crypto.subtle.importKey("raw", encoder.encode(secret), {
3420
+ name: "HMAC",
3421
+ hash: "SHA-256"
3422
+ }, false, ["sign"]);
3423
+ return [...new Uint8Array(await crypto.subtle.sign("HMAC", key, encoder.encode(text)))].map((byte) => byte.toString(16).padStart(2, "0")).join("");
3424
+ }
3425
+ /** The value of `hollware-signature` for a body sent at `timestamp` (seconds). */
3426
+ async function signWebhook(secret, timestamp, body) {
3427
+ return `v1=${await hmac(secret, `${timestamp}.${body}`)}`;
3428
+ }
3429
+ /** Compares two strings in time that does not depend on where they differ. */
3430
+ function sameText(a, b) {
3431
+ if (a.length !== b.length) return false;
3432
+ let difference = 0;
3433
+ for (let index = 0; index < a.length; index++) difference |= a.charCodeAt(index) ^ b.charCodeAt(index);
3434
+ return difference === 0;
3435
+ }
3436
+ /**
3437
+ * Whether a request is a webhook of Hollware: its signature matches the body and the secret, and
3438
+ * its timestamp is within the tolerance, so a captured request cannot be replayed later.
3439
+ */
3440
+ async function verifyWebhook(check) {
3441
+ const signature = check.headers.get(SIGNATURE_HEADER);
3442
+ const timestamp = Number(check.headers.get(TIMESTAMP_HEADER));
3443
+ if (signature === null || !Number.isInteger(timestamp)) return false;
3444
+ const now = check.now ?? Math.floor(Date.now() / 1e3);
3445
+ if (Math.abs(now - timestamp) > (check.toleranceSeconds ?? TOLERANCE_SECONDS)) return false;
3446
+ return sameText(signature, await signWebhook(check.secret, timestamp, check.body));
3447
+ }
3448
+ //#endregion
3306
3449
  //#region src/index.ts
3307
3450
  const name = "@hollware/core";
3308
3451
  //#endregion
3309
- export { BUILT_IN_CONTRACT_IDS, COMARK_VERSION, ConfigError, DEFAULT_MAX_MARKDOWN_BYTES, ERROR_CODES, HollwareError, ID_PREFIXES, MAX_MARKDOWN_BYTES_CEILING, OPERATIONS, PERSONAL_TOKEN_PREFIX, PERSONAL_TOKEN_TTL_DAYS, PIPELINE_VERSION, REFUSAL_CONTRACT_IDS, SCOPES, SITE_KEY_MAX_LENGTH, SITE_KEY_PATTERN, contracts, createDraft, defineCollection, defineComponent, defineConfig, defineContract, defineOperation, describeBuiltInContracts, describeContract, describeResolvedSite, describeSite, generatePersonalToken, getEntry, getTranslations, hashPersonalToken, isBuiltInContract, isId, isPersonalToken, isScope, isSiteKey, listEntries, name, newId, processMarkdown, publish, renderHtml, resolveConfig, runContracts, schemas_exports as schemas, sizeLimitViolation, ulid, uniqueSlugViolation, uniqueTranslationViolation, unpublish, updateDraft, validate };
3452
+ export { BUILT_IN_CONTRACT_IDS, COMARK_VERSION, ConfigError, DEFAULT_MAX_MARKDOWN_BYTES, ERROR_CODES, EVENT_ID_HEADER, HollwareError, ID_PREFIXES, MAX_MARKDOWN_BYTES_CEILING, OPERATIONS, PERSONAL_TOKEN_PREFIX, PERSONAL_TOKEN_TTL_DAYS, PIPELINE_VERSION, REFUSAL_CONTRACT_IDS, SCOPES, SIGNATURE_HEADER, SITE_KEY_MAX_LENGTH, SITE_KEY_PATTERN, TIMESTAMP_HEADER, contracts, createDraft, defineCollection, defineComponent, defineConfig, defineContract, defineOperation, describeBuiltInContracts, describeContract, describeResolvedSite, describeSite, generatePersonalToken, getEntry, getTranslations, hashPersonalToken, isBuiltInContract, isId, isPersonalToken, isScope, isSiteKey, listEntries, name, newId, processMarkdown, publish, renderHtml, resolveConfig, runContracts, schemas_exports as schemas, signWebhook, sizeLimitViolation, ulid, uniqueSlugViolation, uniqueTranslationViolation, unpublish, updateDraft, validate, verifyWebhook };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hollware/core",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Contracts, operation specs, schemas and the Markdown pipeline of Hollware. No Cloudflare dependencies.",
5
5
  "homepage": "https://hollware.com",
6
6
  "bugs": {
@@ -33,7 +33,7 @@
33
33
  "comark": "0.7.0"
34
34
  },
35
35
  "devDependencies": {
36
- "@cloudflare/vitest-plugin": "1.4.0",
36
+ "@cloudflare/vitest-plugin": "1.4.1",
37
37
  "@types/node": "24.19.2",
38
38
  "publint": "0.3.25",
39
39
  "typescript": "7.0.2",