@geekmidas/manifest 9.2.0 → 9.3.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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["field: ManifestField<T> | undefined"],"sources":["../src/index.ts"],"sourcesContent":["/**\n * Deployment manifest types — the build output of `gkm build` that enumerates a\n * project's deployable units (routes, functions, crons, subscribers, queues)\n * with the metadata an infrastructure layer needs to provision them.\n *\n * `gkm build` writes a single TypeScript module per provider\n * (`<out>/manifest/aws.ts`) of the form:\n *\n * ```ts\n * export const manifest = { routes: [...], functions: [...], ... } as const;\n * export type Route = (typeof manifest.routes)[number];\n * // ...derived types\n * ```\n *\n * This is the dependency-free data contract shared between the producer\n * (`@geekmidas/cli`) and consumers (e.g. `@geekmidas/cloud/sst`'s `fromManifest`\n * integrators).\n */\n\n/**\n * A manifest field is either a flat list or, when the build is partitioned\n * (e.g. by authorizer), an object keyed by partition name. Readonly-tolerant so\n * the `as const` generated manifest assigns cleanly.\n */\nexport type ManifestField<T> =\n\t| readonly T[]\n\t| Readonly<Record<string, readonly T[]>>;\n\n/** Flatten a manifest field (array or partitioned) into a plain array. */\nexport function flattenManifestField<T>(\n\tfield: ManifestField<T> | undefined,\n): T[] {\n\tif (!field) return [];\n\treturn Array.isArray(field)\n\t\t? [...field]\n\t\t: Object.values(field as Record<string, readonly T[]>).flat();\n}\n\n/**\n * A monitor set with `.monitor()`, so infrastructure can provision what it\n * reports to, e.g. create a heartbeat and set its URL in `env`.\n */\nexport interface MonitorInfo {\n\t/** The kind of monitor, e.g. `heartbeat`. */\n\ttype: string;\n\t/** The service it reports to, e.g. `betterStack`. Absent for a custom one. */\n\tprovider?: string;\n\t/** The environment variable it reads its target from. */\n\tenv?: string;\n}\n\n/** A single HTTP route. */\nexport interface RouteInfo {\n\t/** Route path, e.g. `/users/{id}`. */\n\tpath: string;\n\t/** HTTP method, e.g. `GET`. */\n\tmethod: string;\n\t/** Bundled handler entrypoint. */\n\thandler: string;\n\ttimeout?: number;\n\tmemorySize?: number;\n\t/** Required environment variables (a trailing `?` marks an optional var). */\n\tenvironment?: readonly string[];\n\t/** Authorizer name: `none`, `iam`, or a declared authorizer. */\n\tauthorizer: string;\n\t/** Monitors set with `.monitor()`; absent when there are none. */\n\tmonitoring?: readonly MonitorInfo[];\n}\n\n/** A standalone Lambda function. */\nexport interface FunctionInfo {\n\tname: string;\n\thandler: string;\n\ttimeout?: number;\n\tmemorySize?: number;\n\tenvironment?: readonly string[];\n\t/** Monitors set with `.monitor()`; absent when there are none. */\n\tmonitoring?: readonly MonitorInfo[];\n}\n\n/** A scheduled (cron) function. */\nexport interface CronInfo {\n\tname: string;\n\thandler: string;\n\t/** Schedule expression, e.g. `rate(1 day)` or `cron(0 12 * * ? *)`. */\n\tschedule: string;\n\ttimeout?: number;\n\tmemorySize?: number;\n\tenvironment?: readonly string[];\n\t/** Monitors set with `.monitor()`; absent when there are none. */\n\tmonitoring?: readonly MonitorInfo[];\n}\n\n/** An event subscriber function (topic/queue resolved by `transport`). */\nexport interface SubscriberInfo {\n\tname: string;\n\thandler: string;\n\tsubscribedEvents: readonly string[];\n\t/** Delivery transport — `topic` (SNS fan-out) or `queue` (SQS). */\n\ttransport?: 'topic' | 'queue';\n\t/** The {@link TopicInfo.name} this subscriber binds to (via `s.topic(topic)`). */\n\ttopic?: string;\n\ttimeout?: number;\n\tmemorySize?: number;\n\tenvironment?: readonly string[];\n}\n\n/**\n * A pub/sub topic — fan-out. A *resource* (no handler): it declares the event\n * contract; producers publish via the derived publisher and {@link SubscriberInfo}s\n * bind to it. Infra provisions an SNS topic.\n */\nexport interface TopicInfo {\n\tname: string;\n\t/** The event type names this topic carries. */\n\tevents: readonly string[];\n\t/** Whether the topic is FIFO. */\n\tfifo?: boolean;\n}\n\n/** A queue worker — a queue and its single consumer. */\nexport interface QueueInfo {\n\tname: string;\n\thandler: string;\n\t/** SQS event-source batch size. */\n\tbatchSize?: number;\n\t/** Whether the queue is FIFO. */\n\tfifo?: boolean;\n\ttimeout?: number;\n\tmemorySize?: number;\n\tenvironment?: readonly string[];\n}\n\n/**\n * The full deployment manifest — the shape of `export const manifest` in a\n * generated `manifest/<provider>.ts`. Each field is a {@link ManifestField}\n * (flat or partitioned).\n */\nexport interface Manifest {\n\troutes: ManifestField<RouteInfo>;\n\tfunctions?: ManifestField<FunctionInfo>;\n\tcrons?: ManifestField<CronInfo>;\n\tsubscribers?: ManifestField<SubscriberInfo>;\n\tqueues?: ManifestField<QueueInfo>;\n\ttopics?: ManifestField<TopicInfo>;\n}\n"],"mappings":";;;AA6BA,SAAgB,qBACfA,OACM;AACN,MAAK,MAAO,QAAO,CAAE;AACrB,QAAO,MAAM,QAAQ,MAAM,GACxB,CAAC,GAAG,KAAM,IACV,OAAO,OAAO,MAAsC,CAAC,MAAM;AAC9D"}
1
+ {"version":3,"file":"index.cjs","names":["field: ManifestField<T> | undefined"],"sources":["../src/index.ts"],"sourcesContent":["/**\n * Deployment manifest types — the build output of `gkm build` that enumerates a\n * project's deployable units (routes, functions, crons, subscribers, queues)\n * with the metadata an infrastructure layer needs to provision them.\n *\n * `gkm build` writes a single TypeScript module per provider\n * (`<out>/manifest/aws.ts`) of the form:\n *\n * ```ts\n * export const manifest = { routes: [...], functions: [...], ... } as const;\n * export type Route = (typeof manifest.routes)[number];\n * // ...derived types\n * ```\n *\n * This is the dependency-free data contract shared between the producer\n * (`@geekmidas/cli`) and consumers (e.g. `@geekmidas/cloud/sst`'s `fromManifest`\n * integrators).\n */\n\n/**\n * A manifest field is either a flat list or, when the build is partitioned\n * (e.g. by authorizer), an object keyed by partition name. Readonly-tolerant so\n * the `as const` generated manifest assigns cleanly.\n */\nexport type ManifestField<T> =\n\t| readonly T[]\n\t| Readonly<Record<string, readonly T[]>>;\n\n/** Flatten a manifest field (array or partitioned) into a plain array. */\nexport function flattenManifestField<T>(\n\tfield: ManifestField<T> | undefined,\n): T[] {\n\tif (!field) return [];\n\treturn Array.isArray(field)\n\t\t? [...field]\n\t\t: Object.values(field as Record<string, readonly T[]>).flat();\n}\n\n/**\n * A monitor set with `.monitor()`, so infrastructure can provision what it\n * reports to, e.g. create a heartbeat and set its URL in `env`.\n */\nexport interface MonitorInfo {\n\t/** The kind of monitor, e.g. `heartbeat`. */\n\ttype: string;\n\t/** The service it reports to, e.g. `betterStack`. Absent for a custom one. */\n\tprovider?: string;\n\t/** The environment variable it reads its target from. */\n\tenv?: string;\n}\n\n/** A single HTTP route. */\nexport interface RouteInfo {\n\t/** Route path, e.g. `/users/{id}`. */\n\tpath: string;\n\t/** HTTP method, e.g. `GET`. */\n\tmethod: string;\n\t/** Bundled handler entrypoint. */\n\thandler: string;\n\ttimeout?: number;\n\tmemorySize?: number;\n\t/** Required environment variables (a trailing `?` marks an optional var). */\n\tenvironment?: readonly string[];\n\t/** Authorizer name: `none`, `iam`, or a declared authorizer. */\n\tauthorizer: string;\n\t/** Monitors set with `.monitor()`; absent when there are none. */\n\tmonitoring?: readonly MonitorInfo[];\n}\n\n/** A standalone Lambda function. */\nexport interface FunctionInfo {\n\tname: string;\n\thandler: string;\n\ttimeout?: number;\n\tmemorySize?: number;\n\tenvironment?: readonly string[];\n\t/** Monitors set with `.monitor()`; absent when there are none. */\n\tmonitoring?: readonly MonitorInfo[];\n}\n\n/** A scheduled (cron) function. */\nexport interface CronInfo {\n\tname: string;\n\thandler: string;\n\t/** Schedule expression, e.g. `rate(1 day)` or `cron(0 12 * * ? *)`. */\n\tschedule: string;\n\ttimeout?: number;\n\tmemorySize?: number;\n\tenvironment?: readonly string[];\n\t/** Monitors set with `.monitor()`; absent when there are none. */\n\tmonitoring?: readonly MonitorInfo[];\n}\n\n/** An event subscriber function (topic/queue resolved by `transport`). */\nexport interface SubscriberInfo {\n\tname: string;\n\thandler: string;\n\tsubscribedEvents: readonly string[];\n\t/** Delivery transport — `topic` (SNS fan-out) or `queue` (SQS). */\n\ttransport?: 'topic' | 'queue';\n\t/** The {@link TopicInfo.name} this subscriber binds to (via `s.topic(topic)`). */\n\ttopic?: string;\n\ttimeout?: number;\n\tmemorySize?: number;\n\tenvironment?: readonly string[];\n}\n\n/**\n * A pub/sub topic — fan-out. A *resource* (no handler): it declares the event\n * contract; producers publish via the derived publisher and {@link SubscriberInfo}s\n * bind to it. Infra provisions an SNS topic.\n */\nexport interface TopicInfo {\n\tname: string;\n\t/** The event type names this topic carries. */\n\tevents: readonly string[];\n\t/** Whether the topic is FIFO. */\n\tfifo?: boolean;\n}\n\n/** A queue worker — a queue and its single consumer. */\nexport interface QueueInfo {\n\tname: string;\n\thandler: string;\n\t/** SQS event-source batch size. */\n\tbatchSize?: number;\n\t/** Whether the queue is FIFO. */\n\tfifo?: boolean;\n\ttimeout?: number;\n\tmemorySize?: number;\n\tenvironment?: readonly string[];\n}\n\n/**\n * The full deployment manifest — the shape of `export const manifest` in a\n * generated `manifest/<provider>.ts`. Each field is a {@link ManifestField}\n * (flat or partitioned).\n */\nexport interface Manifest {\n\troutes: ManifestField<RouteInfo>;\n\tfunctions?: ManifestField<FunctionInfo>;\n\tcrons?: ManifestField<CronInfo>;\n\tsubscribers?: ManifestField<SubscriberInfo>;\n\tqueues?: ManifestField<QueueInfo>;\n\ttopics?: ManifestField<TopicInfo>;\n}\n\nexport * from './cron';\n"],"mappings":";;;;AA6BA,SAAgB,qBACfA,OACM;AACN,MAAK,MAAO,QAAO,CAAE;AACrB,QAAO,MAAM,QAAQ,MAAM,GACxB,CAAC,GAAG,KAAM,IACV,OAAO,OAAO,MAAsC,CAAC,MAAM;AAC9D"}
package/dist/index.d.cts CHANGED
@@ -1,4 +1,7 @@
1
+ import { CronConversion, CronConversionWarning, CronDialect, CronField, CronPeriodError, InvalidCronExpressionError, ParsedCron, UnconvertibleCronExpressionError, cronPeriod, parseCron, toAwsCron, toUnixCron } from "./cron-CjhmidCo.cjs";
2
+
1
3
  //#region src/index.d.ts
4
+
2
5
  /**
3
6
  * Deployment manifest types — the build output of `gkm build` that enumerates a
4
7
  * project's deployable units (routes, functions, crons, subscribers, queues)
@@ -126,7 +129,6 @@ interface Manifest {
126
129
  queues?: ManifestField<QueueInfo>;
127
130
  topics?: ManifestField<TopicInfo>;
128
131
  }
129
- //# sourceMappingURL=index.d.ts.map
130
132
  //#endregion
131
- export { CronInfo, FunctionInfo, Manifest, ManifestField, MonitorInfo, QueueInfo, RouteInfo, SubscriberInfo, TopicInfo, flattenManifestField };
133
+ export { CronConversion, CronConversionWarning, CronDialect, CronField, CronInfo, CronPeriodError, FunctionInfo, InvalidCronExpressionError, Manifest, ManifestField, MonitorInfo, ParsedCron, QueueInfo, RouteInfo, SubscriberInfo, TopicInfo, UnconvertibleCronExpressionError, cronPeriod, flattenManifestField, parseCron, toAwsCron, toUnixCron };
132
134
  //# sourceMappingURL=index.d.cts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;AAwBA;;;;;;AAEW;AAGX;;;;;AAEI;AAWJ;AAUA;AAkBA;AAWA;AAaA;AAkBA;AASA;AAiBA;;AACuB,KAnHX,aAmHW,CAAA,CAAA,CAAA,GAAA,SAlHX,CAkHW,EAAA,GAjHpB,QAiHoB,CAjHX,MAiHW,CAAA,MAAA,EAAA,SAjHa,CAiHb,EAAA,CAAA,CAAA;;AACI,iBA/GX,oBA+GW,CAAA,CAAA,CAAA,CAAA,KAAA,EA9GnB,aA8GmB,CA9GL,CA8GK,CAAA,GAAA,SAAA,CAAA,EA7GxB,CA6GwB,EAAA;;;;;AAEZ,UApGE,WAAA,CAoGF;EAAa;EACK,IAAvB,EAAA,MAAA;EAAa;EACU,QAAvB,CAAA,EAAA,MAAA;EAAa;;;;UA5FN,SAAA;;;;;;;;;;;;;;wBAcM;;;UAIN,YAAA;;;;;;;wBAOM;;;UAIN,QAAA;;;;;;;;;wBASM;;;UAIN,cAAA;;;;;;;;;;;;;;;;;UAkBA,SAAA;;;;;;;;UASA,SAAA;;;;;;;;;;;;;;;;UAiBA,QAAA;UACR,cAAc;cACV,cAAc;UAClB,cAAc;gBACR,cAAc;WACnB,cAAc;WACd,cAAc"}
1
+ {"version":3,"file":"index.d.cts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;AAwBA;;;;;;AAEW;AAGX;;;;;AAEI;AAWJ;AAUA;AAkBA;AAWA;AAaA;AAkBA;AASA;AAiBiB,KAlHL,aAkHa,CAAA,CAAA,CAAA,GAAA,SAjHb,CAiHa,EAAA,GAhHtB,QAgHsB,CAhHb,MAgHa,CAAA,MAAA,EAAA,SAhHW,CAgHX,EAAA,CAAA,CAAA;;AACF,iBA9GP,oBA8GO,CAAA,CAAA,CAAA,CAAA,KAAA,EA7Gf,aA6Ge,CA7GD,CA6GC,CAAA,GAAA,SAAA,CAAA,EA5GpB,CA4GoB,EAAA;;;;;AAEd,UAnGQ,WAAA,CAmGR;EAAa;EACqB,IAA5B,EAAA,MAAA;EAAa;EACK,QAAvB,CAAA,EAAA,MAAA;EAAa;EACU,GAAvB,CAAA,EAAA,MAAA;AAAa;;UA5FN,SAAA;;;;;;;;;;;;;;wBAcM;;;UAIN,YAAA;;;;;;;wBAOM;;;UAIN,QAAA;;;;;;;;;wBASM;;;UAIN,cAAA;;;;;;;;;;;;;;;;;UAkBA,SAAA;;;;;;;;UASA,SAAA;;;;;;;;;;;;;;;;UAiBA,QAAA;UACR,cAAc;cACV,cAAc;UAClB,cAAc;gBACR,cAAc;WACnB,cAAc;WACd,cAAc"}
package/dist/index.d.mts CHANGED
@@ -1,4 +1,7 @@
1
+ import { CronConversion, CronConversionWarning, CronDialect, CronField, CronPeriodError, InvalidCronExpressionError, ParsedCron, UnconvertibleCronExpressionError, cronPeriod, parseCron, toAwsCron, toUnixCron } from "./cron-DlCX5PWN.mjs";
2
+
1
3
  //#region src/index.d.ts
4
+
2
5
  /**
3
6
  * Deployment manifest types — the build output of `gkm build` that enumerates a
4
7
  * project's deployable units (routes, functions, crons, subscribers, queues)
@@ -126,7 +129,6 @@ interface Manifest {
126
129
  queues?: ManifestField<QueueInfo>;
127
130
  topics?: ManifestField<TopicInfo>;
128
131
  }
129
- //# sourceMappingURL=index.d.ts.map
130
132
  //#endregion
131
- export { CronInfo, FunctionInfo, Manifest, ManifestField, MonitorInfo, QueueInfo, RouteInfo, SubscriberInfo, TopicInfo, flattenManifestField };
133
+ export { CronConversion, CronConversionWarning, CronDialect, CronField, CronInfo, CronPeriodError, FunctionInfo, InvalidCronExpressionError, Manifest, ManifestField, MonitorInfo, ParsedCron, QueueInfo, RouteInfo, SubscriberInfo, TopicInfo, UnconvertibleCronExpressionError, cronPeriod, flattenManifestField, parseCron, toAwsCron, toUnixCron };
132
134
  //# sourceMappingURL=index.d.mts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;AAwBA;;;;;;AAEW;AAGX;;;;;AAEI;AAWJ;AAUA;AAkBA;AAWA;AAaA;AAkBA;AASA;AAiBA;;AACuB,KAnHX,aAmHW,CAAA,CAAA,CAAA,GAAA,SAlHX,CAkHW,EAAA,GAjHpB,QAiHoB,CAjHX,MAiHW,CAAA,MAAA,EAAA,SAjHa,CAiHb,EAAA,CAAA,CAAA;;AACI,iBA/GX,oBA+GW,CAAA,CAAA,CAAA,CAAA,KAAA,EA9GnB,aA8GmB,CA9GL,CA8GK,CAAA,GAAA,SAAA,CAAA,EA7GxB,CA6GwB,EAAA;;;;;AAEZ,UApGE,WAAA,CAoGF;EAAa;EACK,IAAvB,EAAA,MAAA;EAAa;EACU,QAAvB,CAAA,EAAA,MAAA;EAAa;;;;UA5FN,SAAA;;;;;;;;;;;;;;wBAcM;;;UAIN,YAAA;;;;;;;wBAOM;;;UAIN,QAAA;;;;;;;;;wBASM;;;UAIN,cAAA;;;;;;;;;;;;;;;;;UAkBA,SAAA;;;;;;;;UASA,SAAA;;;;;;;;;;;;;;;;UAiBA,QAAA;UACR,cAAc;cACV,cAAc;UAClB,cAAc;gBACR,cAAc;WACnB,cAAc;WACd,cAAc"}
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;AAwBA;;;;;;AAEW;AAGX;;;;;AAEI;AAWJ;AAUA;AAkBA;AAWA;AAaA;AAkBA;AASA;AAiBiB,KAlHL,aAkHa,CAAA,CAAA,CAAA,GAAA,SAjHb,CAiHa,EAAA,GAhHtB,QAgHsB,CAhHb,MAgHa,CAAA,MAAA,EAAA,SAhHW,CAgHX,EAAA,CAAA,CAAA;;AACF,iBA9GP,oBA8GO,CAAA,CAAA,CAAA,CAAA,KAAA,EA7Gf,aA6Ge,CA7GD,CA6GC,CAAA,GAAA,SAAA,CAAA,EA5GpB,CA4GoB,EAAA;;;;;AAEd,UAnGQ,WAAA,CAmGR;EAAa;EACqB,IAA5B,EAAA,MAAA;EAAa;EACK,QAAvB,CAAA,EAAA,MAAA;EAAa;EACU,GAAvB,CAAA,EAAA,MAAA;AAAa;;UA5FN,SAAA;;;;;;;;;;;;;;wBAcM;;;UAIN,YAAA;;;;;;;wBAOM;;;UAIN,QAAA;;;;;;;;;wBASM;;;UAIN,cAAA;;;;;;;;;;;;;;;;;UAkBA,SAAA;;;;;;;;UASA,SAAA;;;;;;;;;;;;;;;;UAiBA,QAAA;UACR,cAAc;cACV,cAAc;UAClB,cAAc;gBACR,cAAc;WACnB,cAAc;WACd,cAAc"}
package/dist/index.mjs CHANGED
@@ -1,3 +1,5 @@
1
+ import { CronPeriodError, InvalidCronExpressionError, UnconvertibleCronExpressionError, cronPeriod, parseCron, toAwsCron, toUnixCron } from "./cron-DC4hQ1oa.mjs";
2
+
1
3
  //#region src/index.ts
2
4
  /** Flatten a manifest field (array or partitioned) into a plain array. */
3
5
  function flattenManifestField(field) {
@@ -6,5 +8,5 @@ function flattenManifestField(field) {
6
8
  }
7
9
 
8
10
  //#endregion
9
- export { flattenManifestField };
11
+ export { CronPeriodError, InvalidCronExpressionError, UnconvertibleCronExpressionError, cronPeriod, flattenManifestField, parseCron, toAwsCron, toUnixCron };
10
12
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["field: ManifestField<T> | undefined"],"sources":["../src/index.ts"],"sourcesContent":["/**\n * Deployment manifest types — the build output of `gkm build` that enumerates a\n * project's deployable units (routes, functions, crons, subscribers, queues)\n * with the metadata an infrastructure layer needs to provision them.\n *\n * `gkm build` writes a single TypeScript module per provider\n * (`<out>/manifest/aws.ts`) of the form:\n *\n * ```ts\n * export const manifest = { routes: [...], functions: [...], ... } as const;\n * export type Route = (typeof manifest.routes)[number];\n * // ...derived types\n * ```\n *\n * This is the dependency-free data contract shared between the producer\n * (`@geekmidas/cli`) and consumers (e.g. `@geekmidas/cloud/sst`'s `fromManifest`\n * integrators).\n */\n\n/**\n * A manifest field is either a flat list or, when the build is partitioned\n * (e.g. by authorizer), an object keyed by partition name. Readonly-tolerant so\n * the `as const` generated manifest assigns cleanly.\n */\nexport type ManifestField<T> =\n\t| readonly T[]\n\t| Readonly<Record<string, readonly T[]>>;\n\n/** Flatten a manifest field (array or partitioned) into a plain array. */\nexport function flattenManifestField<T>(\n\tfield: ManifestField<T> | undefined,\n): T[] {\n\tif (!field) return [];\n\treturn Array.isArray(field)\n\t\t? [...field]\n\t\t: Object.values(field as Record<string, readonly T[]>).flat();\n}\n\n/**\n * A monitor set with `.monitor()`, so infrastructure can provision what it\n * reports to, e.g. create a heartbeat and set its URL in `env`.\n */\nexport interface MonitorInfo {\n\t/** The kind of monitor, e.g. `heartbeat`. */\n\ttype: string;\n\t/** The service it reports to, e.g. `betterStack`. Absent for a custom one. */\n\tprovider?: string;\n\t/** The environment variable it reads its target from. */\n\tenv?: string;\n}\n\n/** A single HTTP route. */\nexport interface RouteInfo {\n\t/** Route path, e.g. `/users/{id}`. */\n\tpath: string;\n\t/** HTTP method, e.g. `GET`. */\n\tmethod: string;\n\t/** Bundled handler entrypoint. */\n\thandler: string;\n\ttimeout?: number;\n\tmemorySize?: number;\n\t/** Required environment variables (a trailing `?` marks an optional var). */\n\tenvironment?: readonly string[];\n\t/** Authorizer name: `none`, `iam`, or a declared authorizer. */\n\tauthorizer: string;\n\t/** Monitors set with `.monitor()`; absent when there are none. */\n\tmonitoring?: readonly MonitorInfo[];\n}\n\n/** A standalone Lambda function. */\nexport interface FunctionInfo {\n\tname: string;\n\thandler: string;\n\ttimeout?: number;\n\tmemorySize?: number;\n\tenvironment?: readonly string[];\n\t/** Monitors set with `.monitor()`; absent when there are none. */\n\tmonitoring?: readonly MonitorInfo[];\n}\n\n/** A scheduled (cron) function. */\nexport interface CronInfo {\n\tname: string;\n\thandler: string;\n\t/** Schedule expression, e.g. `rate(1 day)` or `cron(0 12 * * ? *)`. */\n\tschedule: string;\n\ttimeout?: number;\n\tmemorySize?: number;\n\tenvironment?: readonly string[];\n\t/** Monitors set with `.monitor()`; absent when there are none. */\n\tmonitoring?: readonly MonitorInfo[];\n}\n\n/** An event subscriber function (topic/queue resolved by `transport`). */\nexport interface SubscriberInfo {\n\tname: string;\n\thandler: string;\n\tsubscribedEvents: readonly string[];\n\t/** Delivery transport — `topic` (SNS fan-out) or `queue` (SQS). */\n\ttransport?: 'topic' | 'queue';\n\t/** The {@link TopicInfo.name} this subscriber binds to (via `s.topic(topic)`). */\n\ttopic?: string;\n\ttimeout?: number;\n\tmemorySize?: number;\n\tenvironment?: readonly string[];\n}\n\n/**\n * A pub/sub topic — fan-out. A *resource* (no handler): it declares the event\n * contract; producers publish via the derived publisher and {@link SubscriberInfo}s\n * bind to it. Infra provisions an SNS topic.\n */\nexport interface TopicInfo {\n\tname: string;\n\t/** The event type names this topic carries. */\n\tevents: readonly string[];\n\t/** Whether the topic is FIFO. */\n\tfifo?: boolean;\n}\n\n/** A queue worker — a queue and its single consumer. */\nexport interface QueueInfo {\n\tname: string;\n\thandler: string;\n\t/** SQS event-source batch size. */\n\tbatchSize?: number;\n\t/** Whether the queue is FIFO. */\n\tfifo?: boolean;\n\ttimeout?: number;\n\tmemorySize?: number;\n\tenvironment?: readonly string[];\n}\n\n/**\n * The full deployment manifest — the shape of `export const manifest` in a\n * generated `manifest/<provider>.ts`. Each field is a {@link ManifestField}\n * (flat or partitioned).\n */\nexport interface Manifest {\n\troutes: ManifestField<RouteInfo>;\n\tfunctions?: ManifestField<FunctionInfo>;\n\tcrons?: ManifestField<CronInfo>;\n\tsubscribers?: ManifestField<SubscriberInfo>;\n\tqueues?: ManifestField<QueueInfo>;\n\ttopics?: ManifestField<TopicInfo>;\n}\n"],"mappings":";;AA6BA,SAAgB,qBACfA,OACM;AACN,MAAK,MAAO,QAAO,CAAE;AACrB,QAAO,MAAM,QAAQ,MAAM,GACxB,CAAC,GAAG,KAAM,IACV,OAAO,OAAO,MAAsC,CAAC,MAAM;AAC9D"}
1
+ {"version":3,"file":"index.mjs","names":["field: ManifestField<T> | undefined"],"sources":["../src/index.ts"],"sourcesContent":["/**\n * Deployment manifest types — the build output of `gkm build` that enumerates a\n * project's deployable units (routes, functions, crons, subscribers, queues)\n * with the metadata an infrastructure layer needs to provision them.\n *\n * `gkm build` writes a single TypeScript module per provider\n * (`<out>/manifest/aws.ts`) of the form:\n *\n * ```ts\n * export const manifest = { routes: [...], functions: [...], ... } as const;\n * export type Route = (typeof manifest.routes)[number];\n * // ...derived types\n * ```\n *\n * This is the dependency-free data contract shared between the producer\n * (`@geekmidas/cli`) and consumers (e.g. `@geekmidas/cloud/sst`'s `fromManifest`\n * integrators).\n */\n\n/**\n * A manifest field is either a flat list or, when the build is partitioned\n * (e.g. by authorizer), an object keyed by partition name. Readonly-tolerant so\n * the `as const` generated manifest assigns cleanly.\n */\nexport type ManifestField<T> =\n\t| readonly T[]\n\t| Readonly<Record<string, readonly T[]>>;\n\n/** Flatten a manifest field (array or partitioned) into a plain array. */\nexport function flattenManifestField<T>(\n\tfield: ManifestField<T> | undefined,\n): T[] {\n\tif (!field) return [];\n\treturn Array.isArray(field)\n\t\t? [...field]\n\t\t: Object.values(field as Record<string, readonly T[]>).flat();\n}\n\n/**\n * A monitor set with `.monitor()`, so infrastructure can provision what it\n * reports to, e.g. create a heartbeat and set its URL in `env`.\n */\nexport interface MonitorInfo {\n\t/** The kind of monitor, e.g. `heartbeat`. */\n\ttype: string;\n\t/** The service it reports to, e.g. `betterStack`. Absent for a custom one. */\n\tprovider?: string;\n\t/** The environment variable it reads its target from. */\n\tenv?: string;\n}\n\n/** A single HTTP route. */\nexport interface RouteInfo {\n\t/** Route path, e.g. `/users/{id}`. */\n\tpath: string;\n\t/** HTTP method, e.g. `GET`. */\n\tmethod: string;\n\t/** Bundled handler entrypoint. */\n\thandler: string;\n\ttimeout?: number;\n\tmemorySize?: number;\n\t/** Required environment variables (a trailing `?` marks an optional var). */\n\tenvironment?: readonly string[];\n\t/** Authorizer name: `none`, `iam`, or a declared authorizer. */\n\tauthorizer: string;\n\t/** Monitors set with `.monitor()`; absent when there are none. */\n\tmonitoring?: readonly MonitorInfo[];\n}\n\n/** A standalone Lambda function. */\nexport interface FunctionInfo {\n\tname: string;\n\thandler: string;\n\ttimeout?: number;\n\tmemorySize?: number;\n\tenvironment?: readonly string[];\n\t/** Monitors set with `.monitor()`; absent when there are none. */\n\tmonitoring?: readonly MonitorInfo[];\n}\n\n/** A scheduled (cron) function. */\nexport interface CronInfo {\n\tname: string;\n\thandler: string;\n\t/** Schedule expression, e.g. `rate(1 day)` or `cron(0 12 * * ? *)`. */\n\tschedule: string;\n\ttimeout?: number;\n\tmemorySize?: number;\n\tenvironment?: readonly string[];\n\t/** Monitors set with `.monitor()`; absent when there are none. */\n\tmonitoring?: readonly MonitorInfo[];\n}\n\n/** An event subscriber function (topic/queue resolved by `transport`). */\nexport interface SubscriberInfo {\n\tname: string;\n\thandler: string;\n\tsubscribedEvents: readonly string[];\n\t/** Delivery transport — `topic` (SNS fan-out) or `queue` (SQS). */\n\ttransport?: 'topic' | 'queue';\n\t/** The {@link TopicInfo.name} this subscriber binds to (via `s.topic(topic)`). */\n\ttopic?: string;\n\ttimeout?: number;\n\tmemorySize?: number;\n\tenvironment?: readonly string[];\n}\n\n/**\n * A pub/sub topic — fan-out. A *resource* (no handler): it declares the event\n * contract; producers publish via the derived publisher and {@link SubscriberInfo}s\n * bind to it. Infra provisions an SNS topic.\n */\nexport interface TopicInfo {\n\tname: string;\n\t/** The event type names this topic carries. */\n\tevents: readonly string[];\n\t/** Whether the topic is FIFO. */\n\tfifo?: boolean;\n}\n\n/** A queue worker — a queue and its single consumer. */\nexport interface QueueInfo {\n\tname: string;\n\thandler: string;\n\t/** SQS event-source batch size. */\n\tbatchSize?: number;\n\t/** Whether the queue is FIFO. */\n\tfifo?: boolean;\n\ttimeout?: number;\n\tmemorySize?: number;\n\tenvironment?: readonly string[];\n}\n\n/**\n * The full deployment manifest — the shape of `export const manifest` in a\n * generated `manifest/<provider>.ts`. Each field is a {@link ManifestField}\n * (flat or partitioned).\n */\nexport interface Manifest {\n\troutes: ManifestField<RouteInfo>;\n\tfunctions?: ManifestField<FunctionInfo>;\n\tcrons?: ManifestField<CronInfo>;\n\tsubscribers?: ManifestField<SubscriberInfo>;\n\tqueues?: ManifestField<QueueInfo>;\n\ttopics?: ManifestField<TopicInfo>;\n}\n\nexport * from './cron';\n"],"mappings":";;;;AA6BA,SAAgB,qBACfA,OACM;AACN,MAAK,MAAO,QAAO,CAAE;AACrB,QAAO,MAAM,QAAQ,MAAM,GACxB,CAAC,GAAG,KAAM,IACV,OAAO,OAAO,MAAsC,CAAC,MAAM;AAC9D"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geekmidas/manifest",
3
- "version": "9.2.0",
3
+ "version": "9.3.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -0,0 +1,253 @@
1
+ import { describe, expect, it } from 'vitest';
2
+ import {
3
+ CronPeriodError,
4
+ cronPeriod,
5
+ InvalidCronExpressionError,
6
+ parseCron,
7
+ toAwsCron,
8
+ toUnixCron,
9
+ UnconvertibleCronExpressionError,
10
+ } from '../cron';
11
+
12
+ describe('parseCron', () => {
13
+ it.each([
14
+ ['cron(0 8 * * *)', 'unix'],
15
+ ['cron(*/15 * * * *)', 'unix'],
16
+ ['cron(0 9 * * 1-5)', 'unix'],
17
+ ['cron(0 9 * * MON-FRI)', 'unix'],
18
+ ['cron(0 6,18 * * *)', 'unix'],
19
+ ['cron(0 0 1 * 0)', 'unix'],
20
+ ['cron(0 9 1 * MON)', 'unix'],
21
+ ['cron(0 0 * * 7)', 'unix'],
22
+ ['cron(0 08 * * ? *)', 'aws'],
23
+ ['cron(0/30 * * * ? *)', 'aws'],
24
+ ['cron(0 0 * * ? *)', 'aws'],
25
+ ['cron(0 9 ? * MON-FRI *)', 'aws'],
26
+ ['cron(0 9 ? * 2-6 *)', 'aws'],
27
+ ['cron(0 9 L * ? *)', 'aws'],
28
+ ['cron(0 9 15W * ? *)', 'aws'],
29
+ ['cron(0 9 ? * 6#3 *)', 'aws'],
30
+ ['cron(0 9 ? * FRIL 2026-2027)', 'aws'],
31
+ ])('accepts %s as %s', (expression, dialect) => {
32
+ expect(parseCron(expression).dialect).toBe(dialect);
33
+ });
34
+
35
+ it('splits the fields', () => {
36
+ expect(parseCron('cron(0 9 ? * MON-FRI 2026)')).toEqual({
37
+ dialect: 'aws',
38
+ minute: '0',
39
+ hour: '9',
40
+ dayOfMonth: '?',
41
+ month: '*',
42
+ dayOfWeek: 'MON-FRI',
43
+ year: '2026',
44
+ });
45
+ });
46
+
47
+ it.each([
48
+ ['4 fields', 'cron(0 8 * *)', undefined, '0 8 * *'],
49
+ ['7 fields', 'cron(0 0 8 * * ? *)', undefined, '0 0 8 * * ? *'],
50
+ ['rate()', 'rate(1 day)', undefined, 'rate(1 day)'],
51
+ ['`?` in Unix', 'cron(0 8 * * ?)', 'dayOfWeek', '?'],
52
+ ['AWS without `?`', 'cron(0 8 * * * *)', 'dayOfWeek', '*'],
53
+ ['AWS with `?` in both day fields', 'cron(0 8 ? * ? *)', 'dayOfWeek', '?'],
54
+ [
55
+ 'AWS with both day fields restricted',
56
+ 'cron(0 9 1 * MON *)',
57
+ 'dayOfWeek',
58
+ 'MON',
59
+ ],
60
+ ['AWS day-of-week 0', 'cron(0 9 ? * 0 *)', 'dayOfWeek', '0'],
61
+ ['hour 25', 'cron(0 25 * * *)', 'hour', '25'],
62
+ ['minute 60', 'cron(60 * * * ? *)', 'minute', '60'],
63
+ ['`L` in Unix', 'cron(0 9 L * *)', 'dayOfMonth', 'L'],
64
+ ['a double space', 'cron(0 8 * * *)', 'hour', ''],
65
+ ['an unknown month', 'cron(0 8 * FOO *)', 'month', 'FOO'],
66
+ ['a year before 1970', 'cron(0 8 * * ? 1969)', 'year', '1969'],
67
+ ['an invalid atom in a list', 'cron(0 6,24 * * *)', 'hour', '24'],
68
+ ])('rejects %s', (_, expression, field, token) => {
69
+ expect(() => parseCron(expression)).toThrow(
70
+ expect.objectContaining({
71
+ name: 'InvalidCronExpressionError',
72
+ message: 'Invalid cron expression',
73
+ cause: { expression, field, token },
74
+ }),
75
+ );
76
+ });
77
+
78
+ it('throws InvalidCronExpressionError', () => {
79
+ expect(() => parseCron('cron(0 25 * * *)')).toThrow(
80
+ InvalidCronExpressionError,
81
+ );
82
+ });
83
+ });
84
+
85
+ describe('toAwsCron', () => {
86
+ it.each([
87
+ ['both day fields `*`', 'cron(0 8 * * *)', 'cron(0 8 * * ? *)'],
88
+ ['only day-of-month restricted', 'cron(0 0 1 * *)', 'cron(0 0 1 * ? *)'],
89
+ ['only day-of-week restricted', 'cron(0 9 * * 1-5)', 'cron(0 9 ? * 2-6 *)'],
90
+ ['day-of-week names', 'cron(0 9 * * MON-FRI)', 'cron(0 9 ? * MON-FRI *)'],
91
+ ['SUN as 0', 'cron(0 0 * * 0)', 'cron(0 0 ? * 1 *)'],
92
+ ['SUN as 7', 'cron(0 0 * * 7)', 'cron(0 0 ? * 1 *)'],
93
+ ['a range wrapping past SUN', 'cron(0 0 * * 5-7)', 'cron(0 0 ? * 1,6,7 *)'],
94
+ ['a step', 'cron(0 0 * * */2)', 'cron(0 0 ? * 1,3,5,7 *)'],
95
+ ['every day of the week', 'cron(0 0 * * 0-6)', 'cron(0 0 ? * * *)'],
96
+ ])('converts %s', (_, unix, aws) => {
97
+ expect(toAwsCron(unix)).toEqual({ expression: aws, warnings: [] });
98
+ });
99
+
100
+ it('keeps day-of-week and warns when both day fields are restricted', () => {
101
+ expect(toAwsCron('cron(0 9 1 * 1)')).toEqual({
102
+ expression: 'cron(0 9 ? * 2 *)',
103
+ warnings: [
104
+ {
105
+ code: 'dayOfMonthDropped',
106
+ message:
107
+ 'Both day fields are restricted, so day-of-month was dropped and the cron runs on fewer days',
108
+ token: '1',
109
+ },
110
+ ],
111
+ });
112
+ });
113
+
114
+ it('returns an AWS expression unchanged', () => {
115
+ expect(toAwsCron('cron(0 9 15W * ? *)')).toEqual({
116
+ expression: 'cron(0 9 15W * ? *)',
117
+ warnings: [],
118
+ });
119
+ });
120
+
121
+ it('rejects an invalid expression', () => {
122
+ expect(() => toAwsCron('cron(0 25 * * *)')).toThrow(
123
+ InvalidCronExpressionError,
124
+ );
125
+ });
126
+ });
127
+
128
+ describe('toUnixCron', () => {
129
+ it.each([
130
+ ['`?` in day-of-week', 'cron(0 8 * * ? *)', 'cron(0 8 * * *)'],
131
+ ['`?` in day-of-month', 'cron(0 9 ? * 2-6 *)', 'cron(0 9 * * 1-5)'],
132
+ ['day-of-week names', 'cron(0 9 ? * MON-FRI *)', 'cron(0 9 * * MON-FRI)'],
133
+ ['SUN as 1', 'cron(0 0 ? * 1 *)', 'cron(0 0 * * 0)'],
134
+ ['SAT as 7', 'cron(0 0 ? * 7 *)', 'cron(0 0 * * 6)'],
135
+ ['`L` in day-of-month', 'cron(0 9 L * ? *)', 'cron(0 9 L * *)'],
136
+ ['`nL` in day-of-week', 'cron(0 9 ? * 6L *)', 'cron(0 9 * * 5L)'],
137
+ ['`n#k` in day-of-week', 'cron(0 9 ? * 6#3 *)', 'cron(0 9 * * 5#3)'],
138
+ ['a named `nL`', 'cron(0 9 ? * FRIL *)', 'cron(0 9 * * FRIL)'],
139
+ ])('converts %s', (_, aws, unix) => {
140
+ expect(toUnixCron(aws)).toEqual({ expression: unix, warnings: [] });
141
+ });
142
+
143
+ it('drops a specific year with a warning', () => {
144
+ expect(toUnixCron('cron(0 9 ? * FRI 2026-2027)')).toEqual({
145
+ expression: 'cron(0 9 * * FRI)',
146
+ warnings: [
147
+ {
148
+ code: 'yearDropped',
149
+ message: 'The year was dropped, so the cron runs every year',
150
+ token: '2026-2027',
151
+ },
152
+ ],
153
+ });
154
+ });
155
+
156
+ it('returns a Unix expression unchanged', () => {
157
+ expect(toUnixCron('cron(0 9 1 * MON)')).toEqual({
158
+ expression: 'cron(0 9 1 * MON)',
159
+ warnings: [],
160
+ });
161
+ });
162
+
163
+ it.each(['15W', 'LW', 'L-3'])('rejects %s in day-of-month', (token) => {
164
+ const expression = `cron(0 9 ${token} * ? *)`;
165
+ expect(() => toUnixCron(expression)).toThrow(
166
+ expect.objectContaining({
167
+ name: 'UnconvertibleCronExpressionError',
168
+ message: 'Cron expression cannot be converted',
169
+ cause: { expression, field: 'dayOfMonth', token },
170
+ }),
171
+ );
172
+ expect(() => toUnixCron(expression)).toThrow(
173
+ UnconvertibleCronExpressionError,
174
+ );
175
+ });
176
+ });
177
+
178
+ describe('round trip', () => {
179
+ it.each([
180
+ 'cron(0 8 * * *)',
181
+ 'cron(*/15 * * * *)',
182
+ 'cron(0 9 * * 1-5)',
183
+ 'cron(0 9 * * MON-FRI)',
184
+ 'cron(0 6,18 1 * *)',
185
+ 'cron(0 0 * * 0)',
186
+ ])('%s converts to AWS and back unchanged', (unix) => {
187
+ expect(toUnixCron(toAwsCron(unix).expression).expression).toBe(unix);
188
+ });
189
+ });
190
+
191
+ describe('cronPeriod', () => {
192
+ const MINUTE = 60;
193
+ const HOUR = 60 * MINUTE;
194
+ const DAY = 24 * HOUR;
195
+
196
+ it.each([
197
+ ['cron(0 8 * * ? *)', DAY, 'once a day'],
198
+ ['cron(0 8 * * *)', DAY, 'once a day, Unix'],
199
+ ['cron(0/15 * * * ? *)', 15 * MINUTE, 'every 15 minutes'],
200
+ ['cron(* * * * *)', MINUTE, 'every minute'],
201
+ ['cron(0 */2 * * *)', 2 * HOUR, 'every 2 hours'],
202
+ ['cron(0 6,18 * * ? *)', 12 * HOUR, 'twice a day, evenly'],
203
+ ['cron(0 6,9 * * ? *)', 21 * HOUR, 'twice a day, unevenly'],
204
+ ['cron(0 9 ? * MON-FRI *)', 3 * DAY, 'weekdays, over the weekend'],
205
+ ['cron(0 9 * * 1-5)', 3 * DAY, 'weekdays, Unix numbers'],
206
+ ['cron(0 9 ? * MON *)', 7 * DAY, 'weekly'],
207
+ ['cron(0 0 1 * ? *)', 31 * DAY, 'monthly, over a 31-day month'],
208
+ ['cron(0 0 L * ? *)', 31 * DAY, 'the last day of the month'],
209
+ ['cron(0 0 L-2 * ? *)', 31 * DAY, 'two days before the last'],
210
+ ['cron(0 0 ? * 6#3 *)', 35 * DAY, 'the third Friday of the month'],
211
+ ['cron(0 0 ? * 6L *)', 35 * DAY, 'the last Friday of the month'],
212
+ ['cron(0 0 1 JAN ? *)', 366 * DAY, 'yearly, over a leap year'],
213
+ ['cron(0 0 29 2 ? *)', (4 * 365 + 1) * DAY, 'leap days only'],
214
+ ])('%s is %i seconds (%s)', (expression, period) => {
215
+ expect(cronPeriod(expression)).toBe(period);
216
+ });
217
+
218
+ it('runs on the nearest weekday for nW', () => {
219
+ // The 15th falls on a weekend in some months, moving the run a day, so
220
+ // the longest gap is a month plus up to two days.
221
+ const period = cronPeriod('cron(0 9 15W * ? *)');
222
+ expect(period).toBeGreaterThan(31 * DAY);
223
+ expect(period).toBeLessThanOrEqual(33 * DAY);
224
+ });
225
+
226
+ it('runs on the last weekday for LW', () => {
227
+ const period = cronPeriod('cron(0 9 LW * ? *)');
228
+ expect(period).toBeGreaterThan(31 * DAY);
229
+ expect(period).toBeLessThanOrEqual(33 * DAY);
230
+ });
231
+
232
+ it('uses the AWS form when a Unix expression restricts both day fields', () => {
233
+ // AWS keeps day-of-week, so this runs on Mondays, not also on the 1st.
234
+ expect(cronPeriod('cron(0 9 1 * MON)')).toBe(7 * DAY);
235
+ });
236
+
237
+ it('rejects an invalid expression', () => {
238
+ expect(() => cronPeriod('cron(0 25 * * *)')).toThrow(
239
+ InvalidCronExpressionError,
240
+ );
241
+ });
242
+
243
+ it('rejects an expression that runs less than twice', () => {
244
+ const expression = 'cron(0 0 30 2 ? *)';
245
+ expect(() => cronPeriod(expression)).toThrow(CronPeriodError);
246
+ expect(() => cronPeriod(expression)).toThrow(
247
+ expect.objectContaining({
248
+ message: 'Cron expression runs less than twice in eight years',
249
+ cause: { expression },
250
+ }),
251
+ );
252
+ });
253
+ });