@infinitetoken/lumber-kit 0.7.0 → 1.0.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
@@ -22,8 +22,10 @@ declare const LOG_KEY_VERSION = 1;
22
22
  /**
23
23
  * How a plugin delivers logs: one POST /logs per log, in the order they were logged. A network error or a 5xx is a
24
24
  * failed attempt, tried again after `baseDelayMs`, doubling up to `maxDelayMs`, and dropped after `maxAttempts`. Any
25
- * other 4xx is dropped at once, since sending it again won't help. A 429 (the team's logs for the month are used up)
26
- * drops what's queued and every new log until its Retry-After, or for `pauseMs` when it has none.
25
+ * other 4xx (a 413 for a log over MAX_LOG_PAYLOAD_LENGTH among them) is dropped at once, since sending it again won't
26
+ * help. Any 2xx is sent, including the 200 the API answers when it drops a log below ERROR past the team's monthly
27
+ * logs (monthlyLogsCap). A 429 (the team is at its hard stop, twice its plan's monthly logs) drops what's queued and
28
+ * every new log until its Retry-After, or for `pauseMs` when it has none.
27
29
  */
28
30
  declare const logDelivery: {
29
31
  readonly maxAttempts: 5;
@@ -31,6 +33,12 @@ declare const logDelivery: {
31
33
  readonly maxDelayMs: 30000;
32
34
  readonly pauseMs: number;
33
35
  };
36
+ /**
37
+ * The longest `encrypted.payload` POST /logs takes, in characters: 16 KB of base64, about 12 KB of log as written (its
38
+ * message, context and metadata). A longer one is refused with a 413, which a plugin drops at once with a warning
39
+ * (Jay, 2026-11-13). The server can only measure the ciphertext.
40
+ */
41
+ declare const MAX_LOG_PAYLOAD_LENGTH: number;
34
42
 
35
43
  /** Log severity level. Numeric values match the existing Lumber-API level field. */
36
44
  declare const enum LogLevel {
@@ -49,48 +57,6 @@ declare const logLevelNameToLevel: Record<LogLevelName, LogLevel>;
49
57
  /** All valid log levels, ordered from least to most severe. */
50
58
  declare const logLevels: readonly LogLevel[];
51
59
 
52
- /** A push threshold one past Fatal, which no log reaches: log pushes are off (the account's, or one app's). */
53
- declare const PUSH_OFF = 5;
54
- /** The lowest log level that pushes, or PUSH_OFF. Stored as a notification preference's `minimumLevel`. */
55
- type PushThreshold = LogLevel | typeof PUSH_OFF;
56
- /** The threshold a user gets when none is set. */
57
- declare const DEFAULT_PUSH_THRESHOLD: PushThreshold;
58
-
59
- /** User subscription level. Mirrors User.subscriptionLevel in Lumber-API. */
60
- declare const enum SubscriptionLevel {
61
- Hobby = 0,
62
- Dev = 1,
63
- Pro = 2,
64
- Enterprise = 3
65
- }
66
- /** String name for each subscription level, suitable for display and Redux state. */
67
- type SubscriptionLevelName = 'hobby' | 'dev' | 'pro' | 'enterprise';
68
- /** Maps numeric subscription level to its string level name. */
69
- declare const subscriptionLevelToName: Record<SubscriptionLevel, SubscriptionLevelName>;
70
- /** All valid subscription levels, ordered from least to most severe. */
71
- declare const subscriptionLevels: readonly SubscriptionLevel[];
72
- type SubscriptionLimits = {
73
- apps: number;
74
- seats: number;
75
- apiKeys: number;
76
- logRetentionDays: number;
77
- monthlyLogs: number;
78
- /** Whether real-time WebSocket log streaming is available. */
79
- realtimeLogs: boolean;
80
- /** Minimum log level that triggers a push notification. Infinity means push is disabled. */
81
- minPushLevel: number;
82
- };
83
- /** Per-level resource limits. Use Infinity to mean unlimited or disabled. */
84
- declare const subscriptionLimits: Record<SubscriptionLevel, SubscriptionLimits>;
85
- type SubscriptionPlan = {
86
- /** Display name, as every surface shows it. */
87
- name: string;
88
- /** Monthly price in US dollars: 0 for free, null when priced per customer. */
89
- monthlyPrice: number | null;
90
- };
91
- /** Each level's name and price. Stripe holds the prices themselves; these must match what it charges. */
92
- declare const subscriptionPlans: Record<SubscriptionLevel, SubscriptionPlan>;
93
-
94
60
  /**
95
61
  * An organization that owns apps and pays for member usage.
96
62
  * Team subscriptionLevel takes precedence over individual member levels.
@@ -177,8 +143,16 @@ interface AppRecord {
177
143
  teamAccess?: AppTeamAccess;
178
144
  /** How many days of its logs the app's team's plan keeps, null for no limit (and when missing). */
179
145
  retentionDays?: number | null;
180
- /** While the team has sent its plan's monthly logs, when new ones are taken again (the next UTC month); else null. */
146
+ /**
147
+ * While the team has sent its plan's monthly logs, when every level is taken again (the next UTC month); else null.
148
+ * Until then only ERROR and FATAL are taken (monthlyLogsCap).
149
+ */
181
150
  logsCappedUntil?: string | null;
151
+ /**
152
+ * While the team is at its hard stop (twice its plan's monthly logs, monthlyLogsCap), when any log is taken again
153
+ * (the next UTC month); else null. Set only alongside logsCappedUntil.
154
+ */
155
+ logsStoppedUntil?: string | null;
182
156
  }
183
157
  /**
184
158
  * App creation response.
@@ -588,7 +562,10 @@ interface ResponseMetadata {
588
562
  cursor?: string | null;
589
563
  hasMore?: boolean;
590
564
  count?: number;
591
- hiddenCount?: number;
565
+ /** POST /logs past the team's monthly logs: how many logs it dropped (1), below ERROR (monthlyLogsCap). */
566
+ dropped?: number;
567
+ /** With `dropped`: when every level is taken again (the next UTC month), an ISO time. */
568
+ cappedUntil?: string;
592
569
  }
593
570
  interface ApiResponse<T> {
594
571
  data: T;
@@ -667,6 +644,18 @@ interface LogTimeseriesQuery {
667
644
  }
668
645
 
669
646
  type Appearance = 'system' | 'dark' | 'light';
647
+ /** A 24-hour clock (`19:15:44`) or a 12-hour one (`7:15:44 PM`). */
648
+ type TimeClock = '24h' | '12h';
649
+ /** The device's own time zone, or UTC. */
650
+ type TimeZoneChoice = 'local' | 'utc';
651
+ /**
652
+ * How a user reads times, on every device: the clock, and the zone that times, days and hours are in (the log list, the
653
+ * heatmap, pushes).
654
+ */
655
+ interface TimeFormat {
656
+ clock: TimeClock;
657
+ zone: TimeZoneChoice;
658
+ }
670
659
  interface PushToken {
671
660
  token: string;
672
661
  deviceId?: string;
@@ -701,6 +690,8 @@ interface UserRecord {
701
690
  appearance?: Appearance;
702
691
  color?: string;
703
692
  imageUrl?: string;
693
+ /** Null until the user picks one (DEFAULT_TIME_FORMAT). Missing from an API before it was kept. */
694
+ timeFormat?: TimeFormat | null;
704
695
  agreedToTermsVersion?: string;
705
696
  notificationPreferences: NotificationPreferences;
706
697
  pushTokens: PushToken[];
@@ -710,6 +701,62 @@ interface UserRecord {
710
701
  notesReadAt?: Record<string, string>;
711
702
  }
712
703
 
704
+ /** A push threshold one past Fatal, which no log reaches: log pushes are off (the account's, or one app's). */
705
+ declare const PUSH_OFF = 5;
706
+ /** The lowest log level that pushes, or PUSH_OFF. Stored as a notification preference's `minimumLevel`. */
707
+ type PushThreshold = LogLevel | typeof PUSH_OFF;
708
+ /** The threshold a user gets when none is set. */
709
+ declare const DEFAULT_PUSH_THRESHOLD: PushThreshold;
710
+ /** The time format a user gets until they pick one: 24-hour, in the device's own time zone. */
711
+ declare const DEFAULT_TIME_FORMAT: TimeFormat;
712
+
713
+ /** User subscription level. Mirrors User.subscriptionLevel in Lumber-API. */
714
+ declare const enum SubscriptionLevel {
715
+ Hobby = 0,
716
+ Dev = 1,
717
+ Pro = 2,
718
+ Enterprise = 3
719
+ }
720
+ /** String name for each subscription level, suitable for display and Redux state. */
721
+ type SubscriptionLevelName = 'hobby' | 'dev' | 'pro' | 'enterprise';
722
+ /** Maps numeric subscription level to its string level name. */
723
+ declare const subscriptionLevelToName: Record<SubscriptionLevel, SubscriptionLevelName>;
724
+ /** All valid subscription levels, ordered from least to most severe. */
725
+ declare const subscriptionLevels: readonly SubscriptionLevel[];
726
+ type SubscriptionLimits = {
727
+ apps: number;
728
+ seats: number;
729
+ apiKeys: number;
730
+ logRetentionDays: number;
731
+ monthlyLogs: number;
732
+ /** Whether real-time WebSocket log streaming is available. */
733
+ realtimeLogs: boolean;
734
+ /** Minimum log level that triggers a push notification. Infinity means push is disabled. */
735
+ minPushLevel: number;
736
+ };
737
+ /** Per-level resource limits. Use Infinity to mean unlimited or disabled. */
738
+ declare const subscriptionLimits: Record<SubscriptionLevel, SubscriptionLimits>;
739
+ /**
740
+ * What happens past a team's monthly logs (Jay, 2026-11-13: errors always get through). Past its plan's monthlyLogs,
741
+ * POST /logs still takes ERROR and FATAL and drops lower levels, answering a dropped log 200 with `metadata.dropped`
742
+ * so a plugin carries on, until `hardStopMultiple` times monthlyLogs, when it refuses every log with a 429 until the
743
+ * next UTC month. Only the logs it takes count in usage, so the hard stop measures what every device holds.
744
+ */
745
+ declare const monthlyLogsCap: {
746
+ /** The lowest level still taken past the cap. */
747
+ readonly minLevelPastCap: LogLevel.Error;
748
+ /** Where every log is refused, as a multiple of the plan's monthlyLogs. */
749
+ readonly hardStopMultiple: 2;
750
+ };
751
+ type SubscriptionPlan = {
752
+ /** Display name, as every surface shows it. */
753
+ name: string;
754
+ /** Monthly price in US dollars: 0 for free, null when priced per customer. */
755
+ monthlyPrice: number | null;
756
+ };
757
+ /** Each level's name and price. Stripe holds the prices themselves; these must match what it charges. */
758
+ declare const subscriptionPlans: Record<SubscriptionLevel, SubscriptionPlan>;
759
+
713
760
  /**
714
761
  * The plaintext a plugin encrypts for one log call: its level and message, requestId and traceId when given, the
715
762
  * plugin's automatic context (pid, hostname and the like) with the call's context over it, and the call's other keys
@@ -719,14 +766,19 @@ interface UserRecord {
719
766
  */
720
767
  declare const buildLogPayload: (level: LogLevel, message: string, data?: LogData, autoContext?: Record<string, unknown>) => LogPayload;
721
768
  /**
722
- * What to do with a log once POST /logs answers with `status`, or null for no answer (a network error): sent on a 2xx,
723
- * pause on a 429, drop on any other 4xx, and retry on anything else.
769
+ * What to do with a log once POST /logs answers with `status`, or null for no answer (a network error): sent on a 2xx
770
+ * (the API's 200 for a log it dropped past the monthly cap too), pause on a 429, drop on any other 4xx, and retry on
771
+ * anything else.
724
772
  */
725
773
  declare const logDeliveryOutcome: (status: number | null) => LogDeliveryOutcome;
726
774
  /** How long to wait before trying a log again after its `failedAttempts`th failure: 1s, 2s, 4s and so on, up to 30s. */
727
775
  declare const logRetryDelayMs: (failedAttempts: number) => number;
728
776
 
729
777
  declare const isValidAppearance: (value: unknown) => value is Appearance;
778
+ /** A time format as sent or stored: an object with a known clock and zone, and nothing else. */
779
+ declare const isValidTimeFormat: (value: unknown) => value is TimeFormat;
780
+ /** A stored or sent time format, each part the default (DEFAULT_TIME_FORMAT) when it's missing or unknown. */
781
+ declare const normalizeTimeFormat: (value: unknown) => TimeFormat;
730
782
  /** Accepts 3- and 6-digit hex color strings (#rgb, #rrggbb). */
731
783
  declare const isValidColor: (value: string) => boolean;
732
784
  /**
@@ -766,4 +818,5 @@ declare const apiErrorCodeOf: (body: unknown) => ApiErrorCode | null;
766
818
  /** A push notification's data as PushData, or null for data no Lumber push sends. */
767
819
  declare const parsePushData: (data: unknown) => PushData | null;
768
820
 
769
- export { APP_COLORS, type ApiErrorBody, type ApiErrorCode, type ApiKeyRecord, type ApiResponse, type AppCreatedRecord, type AppEncryptedKey, type AppKeyUpsertBody, type AppPermission, type AppPersonRecord, type AppRecord, type AppTeamAccess, type Appearance, type AppsQuery, type ClaimantKeyCheck, type ClientToServerEvents, DEFAULT_ACCENT, DEFAULT_API_ENDPOINT, DEFAULT_PUSH_THRESHOLD, type DeviceLinkApproveBody, type DeviceLinkCreateBody, type DeviceLinkCreatedRecord, type DeviceLinkPlatform, type DeviceLinkRecord, type DeviceLinkStatus, type FlagRecord, type FlagsQuery, type GuestRecord, type HolderKeyCheck, type InvitationRecord, type KeyCheckResult, type KeyHolder, type KeyPin, LOG_KEY_VERSION, type LogContext, type LogData, type LogDeliveryOutcome, type LogEncrypted, type LogIngestBody, LogLevel, type LogLevelName, type LogPayload, type LogRecord, type LogTimeseriesQuery, type LogsQuery, type NoteEncrypted, type NoteRecord, type NotesQuery, type NotificationPreference, type NotificationPreferences, PUSH_OFF, type PendingAppKey, type PendingDeviceLink, type PushData, type PushThreshold, type PushToken, type PushType, type ResponseMetadata, type ServerToClientEvents, type SocketAppPayload, SubscriptionLevel, type SubscriptionLevelName, type SubscriptionLimits, type SubscriptionPlan, type SubscriptionPlanRecord, type TeamMemberRecord, type TeamRecord, type TeamsQuery, type TimeseriesBucket, type TimeseriesResponse, type UserRecord, apiErrorCodeOf, apiErrorCodes, buildLogPayload, isApiErrorCode, isValidAppearance, isValidColor, isValidDeviceLinkStatus, isValidImageUrl, isValidLogLevel, isValidSubscriptionLevel, isValidTeamMemberRole, joinApiKey, logDelivery, logDeliveryOutcome, logLevelColors, logLevelNameToLevel, logLevelToName, logLevels, logRetryDelayMs, nameColor, normalizePushThreshold, parsePushData, personColor, resolveAppColor, splitApiKey, subscriptionLevelToName, subscriptionLevels, subscriptionLimits, subscriptionPlans };
821
+ export { APP_COLORS, DEFAULT_ACCENT, DEFAULT_API_ENDPOINT, DEFAULT_PUSH_THRESHOLD, DEFAULT_TIME_FORMAT, LOG_KEY_VERSION, LogLevel, MAX_LOG_PAYLOAD_LENGTH, PUSH_OFF, SubscriptionLevel, apiErrorCodeOf, apiErrorCodes, buildLogPayload, isApiErrorCode, isValidAppearance, isValidColor, isValidDeviceLinkStatus, isValidImageUrl, isValidLogLevel, isValidSubscriptionLevel, isValidTeamMemberRole, isValidTimeFormat, joinApiKey, logDelivery, logDeliveryOutcome, logLevelColors, logLevelNameToLevel, logLevelToName, logLevels, logRetryDelayMs, monthlyLogsCap, nameColor, normalizePushThreshold, normalizeTimeFormat, parsePushData, personColor, resolveAppColor, splitApiKey, subscriptionLevelToName, subscriptionLevels, subscriptionLimits, subscriptionPlans };
822
+ export type { ApiErrorBody, ApiErrorCode, ApiKeyRecord, ApiResponse, AppCreatedRecord, AppEncryptedKey, AppKeyUpsertBody, AppPermission, AppPersonRecord, AppRecord, AppTeamAccess, Appearance, AppsQuery, ClaimantKeyCheck, ClientToServerEvents, DeviceLinkApproveBody, DeviceLinkCreateBody, DeviceLinkCreatedRecord, DeviceLinkPlatform, DeviceLinkRecord, DeviceLinkStatus, FlagRecord, FlagsQuery, GuestRecord, HolderKeyCheck, InvitationRecord, KeyCheckResult, KeyHolder, KeyPin, LogContext, LogData, LogDeliveryOutcome, LogEncrypted, LogIngestBody, LogLevelName, LogPayload, LogRecord, LogTimeseriesQuery, LogsQuery, NoteEncrypted, NoteRecord, NotesQuery, NotificationPreference, NotificationPreferences, PendingAppKey, PendingDeviceLink, PushData, PushThreshold, PushToken, PushType, ResponseMetadata, ServerToClientEvents, SocketAppPayload, SubscriptionLevelName, SubscriptionLimits, SubscriptionPlan, SubscriptionPlanRecord, TeamMemberRecord, TeamRecord, TeamsQuery, TimeClock, TimeFormat, TimeZoneChoice, TimeseriesBucket, TimeseriesResponse, UserRecord };
package/dist/index.d.ts CHANGED
@@ -22,8 +22,10 @@ declare const LOG_KEY_VERSION = 1;
22
22
  /**
23
23
  * How a plugin delivers logs: one POST /logs per log, in the order they were logged. A network error or a 5xx is a
24
24
  * failed attempt, tried again after `baseDelayMs`, doubling up to `maxDelayMs`, and dropped after `maxAttempts`. Any
25
- * other 4xx is dropped at once, since sending it again won't help. A 429 (the team's logs for the month are used up)
26
- * drops what's queued and every new log until its Retry-After, or for `pauseMs` when it has none.
25
+ * other 4xx (a 413 for a log over MAX_LOG_PAYLOAD_LENGTH among them) is dropped at once, since sending it again won't
26
+ * help. Any 2xx is sent, including the 200 the API answers when it drops a log below ERROR past the team's monthly
27
+ * logs (monthlyLogsCap). A 429 (the team is at its hard stop, twice its plan's monthly logs) drops what's queued and
28
+ * every new log until its Retry-After, or for `pauseMs` when it has none.
27
29
  */
28
30
  declare const logDelivery: {
29
31
  readonly maxAttempts: 5;
@@ -31,6 +33,12 @@ declare const logDelivery: {
31
33
  readonly maxDelayMs: 30000;
32
34
  readonly pauseMs: number;
33
35
  };
36
+ /**
37
+ * The longest `encrypted.payload` POST /logs takes, in characters: 16 KB of base64, about 12 KB of log as written (its
38
+ * message, context and metadata). A longer one is refused with a 413, which a plugin drops at once with a warning
39
+ * (Jay, 2026-11-13). The server can only measure the ciphertext.
40
+ */
41
+ declare const MAX_LOG_PAYLOAD_LENGTH: number;
34
42
 
35
43
  /** Log severity level. Numeric values match the existing Lumber-API level field. */
36
44
  declare const enum LogLevel {
@@ -49,48 +57,6 @@ declare const logLevelNameToLevel: Record<LogLevelName, LogLevel>;
49
57
  /** All valid log levels, ordered from least to most severe. */
50
58
  declare const logLevels: readonly LogLevel[];
51
59
 
52
- /** A push threshold one past Fatal, which no log reaches: log pushes are off (the account's, or one app's). */
53
- declare const PUSH_OFF = 5;
54
- /** The lowest log level that pushes, or PUSH_OFF. Stored as a notification preference's `minimumLevel`. */
55
- type PushThreshold = LogLevel | typeof PUSH_OFF;
56
- /** The threshold a user gets when none is set. */
57
- declare const DEFAULT_PUSH_THRESHOLD: PushThreshold;
58
-
59
- /** User subscription level. Mirrors User.subscriptionLevel in Lumber-API. */
60
- declare const enum SubscriptionLevel {
61
- Hobby = 0,
62
- Dev = 1,
63
- Pro = 2,
64
- Enterprise = 3
65
- }
66
- /** String name for each subscription level, suitable for display and Redux state. */
67
- type SubscriptionLevelName = 'hobby' | 'dev' | 'pro' | 'enterprise';
68
- /** Maps numeric subscription level to its string level name. */
69
- declare const subscriptionLevelToName: Record<SubscriptionLevel, SubscriptionLevelName>;
70
- /** All valid subscription levels, ordered from least to most severe. */
71
- declare const subscriptionLevels: readonly SubscriptionLevel[];
72
- type SubscriptionLimits = {
73
- apps: number;
74
- seats: number;
75
- apiKeys: number;
76
- logRetentionDays: number;
77
- monthlyLogs: number;
78
- /** Whether real-time WebSocket log streaming is available. */
79
- realtimeLogs: boolean;
80
- /** Minimum log level that triggers a push notification. Infinity means push is disabled. */
81
- minPushLevel: number;
82
- };
83
- /** Per-level resource limits. Use Infinity to mean unlimited or disabled. */
84
- declare const subscriptionLimits: Record<SubscriptionLevel, SubscriptionLimits>;
85
- type SubscriptionPlan = {
86
- /** Display name, as every surface shows it. */
87
- name: string;
88
- /** Monthly price in US dollars: 0 for free, null when priced per customer. */
89
- monthlyPrice: number | null;
90
- };
91
- /** Each level's name and price. Stripe holds the prices themselves; these must match what it charges. */
92
- declare const subscriptionPlans: Record<SubscriptionLevel, SubscriptionPlan>;
93
-
94
60
  /**
95
61
  * An organization that owns apps and pays for member usage.
96
62
  * Team subscriptionLevel takes precedence over individual member levels.
@@ -177,8 +143,16 @@ interface AppRecord {
177
143
  teamAccess?: AppTeamAccess;
178
144
  /** How many days of its logs the app's team's plan keeps, null for no limit (and when missing). */
179
145
  retentionDays?: number | null;
180
- /** While the team has sent its plan's monthly logs, when new ones are taken again (the next UTC month); else null. */
146
+ /**
147
+ * While the team has sent its plan's monthly logs, when every level is taken again (the next UTC month); else null.
148
+ * Until then only ERROR and FATAL are taken (monthlyLogsCap).
149
+ */
181
150
  logsCappedUntil?: string | null;
151
+ /**
152
+ * While the team is at its hard stop (twice its plan's monthly logs, monthlyLogsCap), when any log is taken again
153
+ * (the next UTC month); else null. Set only alongside logsCappedUntil.
154
+ */
155
+ logsStoppedUntil?: string | null;
182
156
  }
183
157
  /**
184
158
  * App creation response.
@@ -588,7 +562,10 @@ interface ResponseMetadata {
588
562
  cursor?: string | null;
589
563
  hasMore?: boolean;
590
564
  count?: number;
591
- hiddenCount?: number;
565
+ /** POST /logs past the team's monthly logs: how many logs it dropped (1), below ERROR (monthlyLogsCap). */
566
+ dropped?: number;
567
+ /** With `dropped`: when every level is taken again (the next UTC month), an ISO time. */
568
+ cappedUntil?: string;
592
569
  }
593
570
  interface ApiResponse<T> {
594
571
  data: T;
@@ -667,6 +644,18 @@ interface LogTimeseriesQuery {
667
644
  }
668
645
 
669
646
  type Appearance = 'system' | 'dark' | 'light';
647
+ /** A 24-hour clock (`19:15:44`) or a 12-hour one (`7:15:44 PM`). */
648
+ type TimeClock = '24h' | '12h';
649
+ /** The device's own time zone, or UTC. */
650
+ type TimeZoneChoice = 'local' | 'utc';
651
+ /**
652
+ * How a user reads times, on every device: the clock, and the zone that times, days and hours are in (the log list, the
653
+ * heatmap, pushes).
654
+ */
655
+ interface TimeFormat {
656
+ clock: TimeClock;
657
+ zone: TimeZoneChoice;
658
+ }
670
659
  interface PushToken {
671
660
  token: string;
672
661
  deviceId?: string;
@@ -701,6 +690,8 @@ interface UserRecord {
701
690
  appearance?: Appearance;
702
691
  color?: string;
703
692
  imageUrl?: string;
693
+ /** Null until the user picks one (DEFAULT_TIME_FORMAT). Missing from an API before it was kept. */
694
+ timeFormat?: TimeFormat | null;
704
695
  agreedToTermsVersion?: string;
705
696
  notificationPreferences: NotificationPreferences;
706
697
  pushTokens: PushToken[];
@@ -710,6 +701,62 @@ interface UserRecord {
710
701
  notesReadAt?: Record<string, string>;
711
702
  }
712
703
 
704
+ /** A push threshold one past Fatal, which no log reaches: log pushes are off (the account's, or one app's). */
705
+ declare const PUSH_OFF = 5;
706
+ /** The lowest log level that pushes, or PUSH_OFF. Stored as a notification preference's `minimumLevel`. */
707
+ type PushThreshold = LogLevel | typeof PUSH_OFF;
708
+ /** The threshold a user gets when none is set. */
709
+ declare const DEFAULT_PUSH_THRESHOLD: PushThreshold;
710
+ /** The time format a user gets until they pick one: 24-hour, in the device's own time zone. */
711
+ declare const DEFAULT_TIME_FORMAT: TimeFormat;
712
+
713
+ /** User subscription level. Mirrors User.subscriptionLevel in Lumber-API. */
714
+ declare const enum SubscriptionLevel {
715
+ Hobby = 0,
716
+ Dev = 1,
717
+ Pro = 2,
718
+ Enterprise = 3
719
+ }
720
+ /** String name for each subscription level, suitable for display and Redux state. */
721
+ type SubscriptionLevelName = 'hobby' | 'dev' | 'pro' | 'enterprise';
722
+ /** Maps numeric subscription level to its string level name. */
723
+ declare const subscriptionLevelToName: Record<SubscriptionLevel, SubscriptionLevelName>;
724
+ /** All valid subscription levels, ordered from least to most severe. */
725
+ declare const subscriptionLevels: readonly SubscriptionLevel[];
726
+ type SubscriptionLimits = {
727
+ apps: number;
728
+ seats: number;
729
+ apiKeys: number;
730
+ logRetentionDays: number;
731
+ monthlyLogs: number;
732
+ /** Whether real-time WebSocket log streaming is available. */
733
+ realtimeLogs: boolean;
734
+ /** Minimum log level that triggers a push notification. Infinity means push is disabled. */
735
+ minPushLevel: number;
736
+ };
737
+ /** Per-level resource limits. Use Infinity to mean unlimited or disabled. */
738
+ declare const subscriptionLimits: Record<SubscriptionLevel, SubscriptionLimits>;
739
+ /**
740
+ * What happens past a team's monthly logs (Jay, 2026-11-13: errors always get through). Past its plan's monthlyLogs,
741
+ * POST /logs still takes ERROR and FATAL and drops lower levels, answering a dropped log 200 with `metadata.dropped`
742
+ * so a plugin carries on, until `hardStopMultiple` times monthlyLogs, when it refuses every log with a 429 until the
743
+ * next UTC month. Only the logs it takes count in usage, so the hard stop measures what every device holds.
744
+ */
745
+ declare const monthlyLogsCap: {
746
+ /** The lowest level still taken past the cap. */
747
+ readonly minLevelPastCap: LogLevel.Error;
748
+ /** Where every log is refused, as a multiple of the plan's monthlyLogs. */
749
+ readonly hardStopMultiple: 2;
750
+ };
751
+ type SubscriptionPlan = {
752
+ /** Display name, as every surface shows it. */
753
+ name: string;
754
+ /** Monthly price in US dollars: 0 for free, null when priced per customer. */
755
+ monthlyPrice: number | null;
756
+ };
757
+ /** Each level's name and price. Stripe holds the prices themselves; these must match what it charges. */
758
+ declare const subscriptionPlans: Record<SubscriptionLevel, SubscriptionPlan>;
759
+
713
760
  /**
714
761
  * The plaintext a plugin encrypts for one log call: its level and message, requestId and traceId when given, the
715
762
  * plugin's automatic context (pid, hostname and the like) with the call's context over it, and the call's other keys
@@ -719,14 +766,19 @@ interface UserRecord {
719
766
  */
720
767
  declare const buildLogPayload: (level: LogLevel, message: string, data?: LogData, autoContext?: Record<string, unknown>) => LogPayload;
721
768
  /**
722
- * What to do with a log once POST /logs answers with `status`, or null for no answer (a network error): sent on a 2xx,
723
- * pause on a 429, drop on any other 4xx, and retry on anything else.
769
+ * What to do with a log once POST /logs answers with `status`, or null for no answer (a network error): sent on a 2xx
770
+ * (the API's 200 for a log it dropped past the monthly cap too), pause on a 429, drop on any other 4xx, and retry on
771
+ * anything else.
724
772
  */
725
773
  declare const logDeliveryOutcome: (status: number | null) => LogDeliveryOutcome;
726
774
  /** How long to wait before trying a log again after its `failedAttempts`th failure: 1s, 2s, 4s and so on, up to 30s. */
727
775
  declare const logRetryDelayMs: (failedAttempts: number) => number;
728
776
 
729
777
  declare const isValidAppearance: (value: unknown) => value is Appearance;
778
+ /** A time format as sent or stored: an object with a known clock and zone, and nothing else. */
779
+ declare const isValidTimeFormat: (value: unknown) => value is TimeFormat;
780
+ /** A stored or sent time format, each part the default (DEFAULT_TIME_FORMAT) when it's missing or unknown. */
781
+ declare const normalizeTimeFormat: (value: unknown) => TimeFormat;
730
782
  /** Accepts 3- and 6-digit hex color strings (#rgb, #rrggbb). */
731
783
  declare const isValidColor: (value: string) => boolean;
732
784
  /**
@@ -766,4 +818,5 @@ declare const apiErrorCodeOf: (body: unknown) => ApiErrorCode | null;
766
818
  /** A push notification's data as PushData, or null for data no Lumber push sends. */
767
819
  declare const parsePushData: (data: unknown) => PushData | null;
768
820
 
769
- export { APP_COLORS, type ApiErrorBody, type ApiErrorCode, type ApiKeyRecord, type ApiResponse, type AppCreatedRecord, type AppEncryptedKey, type AppKeyUpsertBody, type AppPermission, type AppPersonRecord, type AppRecord, type AppTeamAccess, type Appearance, type AppsQuery, type ClaimantKeyCheck, type ClientToServerEvents, DEFAULT_ACCENT, DEFAULT_API_ENDPOINT, DEFAULT_PUSH_THRESHOLD, type DeviceLinkApproveBody, type DeviceLinkCreateBody, type DeviceLinkCreatedRecord, type DeviceLinkPlatform, type DeviceLinkRecord, type DeviceLinkStatus, type FlagRecord, type FlagsQuery, type GuestRecord, type HolderKeyCheck, type InvitationRecord, type KeyCheckResult, type KeyHolder, type KeyPin, LOG_KEY_VERSION, type LogContext, type LogData, type LogDeliveryOutcome, type LogEncrypted, type LogIngestBody, LogLevel, type LogLevelName, type LogPayload, type LogRecord, type LogTimeseriesQuery, type LogsQuery, type NoteEncrypted, type NoteRecord, type NotesQuery, type NotificationPreference, type NotificationPreferences, PUSH_OFF, type PendingAppKey, type PendingDeviceLink, type PushData, type PushThreshold, type PushToken, type PushType, type ResponseMetadata, type ServerToClientEvents, type SocketAppPayload, SubscriptionLevel, type SubscriptionLevelName, type SubscriptionLimits, type SubscriptionPlan, type SubscriptionPlanRecord, type TeamMemberRecord, type TeamRecord, type TeamsQuery, type TimeseriesBucket, type TimeseriesResponse, type UserRecord, apiErrorCodeOf, apiErrorCodes, buildLogPayload, isApiErrorCode, isValidAppearance, isValidColor, isValidDeviceLinkStatus, isValidImageUrl, isValidLogLevel, isValidSubscriptionLevel, isValidTeamMemberRole, joinApiKey, logDelivery, logDeliveryOutcome, logLevelColors, logLevelNameToLevel, logLevelToName, logLevels, logRetryDelayMs, nameColor, normalizePushThreshold, parsePushData, personColor, resolveAppColor, splitApiKey, subscriptionLevelToName, subscriptionLevels, subscriptionLimits, subscriptionPlans };
821
+ export { APP_COLORS, DEFAULT_ACCENT, DEFAULT_API_ENDPOINT, DEFAULT_PUSH_THRESHOLD, DEFAULT_TIME_FORMAT, LOG_KEY_VERSION, LogLevel, MAX_LOG_PAYLOAD_LENGTH, PUSH_OFF, SubscriptionLevel, apiErrorCodeOf, apiErrorCodes, buildLogPayload, isApiErrorCode, isValidAppearance, isValidColor, isValidDeviceLinkStatus, isValidImageUrl, isValidLogLevel, isValidSubscriptionLevel, isValidTeamMemberRole, isValidTimeFormat, joinApiKey, logDelivery, logDeliveryOutcome, logLevelColors, logLevelNameToLevel, logLevelToName, logLevels, logRetryDelayMs, monthlyLogsCap, nameColor, normalizePushThreshold, normalizeTimeFormat, parsePushData, personColor, resolveAppColor, splitApiKey, subscriptionLevelToName, subscriptionLevels, subscriptionLimits, subscriptionPlans };
822
+ export type { ApiErrorBody, ApiErrorCode, ApiKeyRecord, ApiResponse, AppCreatedRecord, AppEncryptedKey, AppKeyUpsertBody, AppPermission, AppPersonRecord, AppRecord, AppTeamAccess, Appearance, AppsQuery, ClaimantKeyCheck, ClientToServerEvents, DeviceLinkApproveBody, DeviceLinkCreateBody, DeviceLinkCreatedRecord, DeviceLinkPlatform, DeviceLinkRecord, DeviceLinkStatus, FlagRecord, FlagsQuery, GuestRecord, HolderKeyCheck, InvitationRecord, KeyCheckResult, KeyHolder, KeyPin, LogContext, LogData, LogDeliveryOutcome, LogEncrypted, LogIngestBody, LogLevelName, LogPayload, LogRecord, LogTimeseriesQuery, LogsQuery, NoteEncrypted, NoteRecord, NotesQuery, NotificationPreference, NotificationPreferences, PendingAppKey, PendingDeviceLink, PushData, PushThreshold, PushToken, PushType, ResponseMetadata, ServerToClientEvents, SocketAppPayload, SubscriptionLevelName, SubscriptionLimits, SubscriptionPlan, SubscriptionPlanRecord, TeamMemberRecord, TeamRecord, TeamsQuery, TimeClock, TimeFormat, TimeZoneChoice, TimeseriesBucket, TimeseriesResponse, UserRecord };
package/dist/index.js CHANGED
@@ -24,8 +24,10 @@ __export(index_exports, {
24
24
  DEFAULT_ACCENT: () => DEFAULT_ACCENT,
25
25
  DEFAULT_API_ENDPOINT: () => DEFAULT_API_ENDPOINT,
26
26
  DEFAULT_PUSH_THRESHOLD: () => DEFAULT_PUSH_THRESHOLD,
27
+ DEFAULT_TIME_FORMAT: () => DEFAULT_TIME_FORMAT,
27
28
  LOG_KEY_VERSION: () => LOG_KEY_VERSION,
28
29
  LogLevel: () => LogLevel,
30
+ MAX_LOG_PAYLOAD_LENGTH: () => MAX_LOG_PAYLOAD_LENGTH,
29
31
  PUSH_OFF: () => PUSH_OFF,
30
32
  SubscriptionLevel: () => SubscriptionLevel,
31
33
  apiErrorCodeOf: () => apiErrorCodeOf,
@@ -39,6 +41,7 @@ __export(index_exports, {
39
41
  isValidLogLevel: () => isValidLogLevel,
40
42
  isValidSubscriptionLevel: () => isValidSubscriptionLevel,
41
43
  isValidTeamMemberRole: () => isValidTeamMemberRole,
44
+ isValidTimeFormat: () => isValidTimeFormat,
42
45
  joinApiKey: () => joinApiKey,
43
46
  logDelivery: () => logDelivery,
44
47
  logDeliveryOutcome: () => logDeliveryOutcome,
@@ -47,8 +50,10 @@ __export(index_exports, {
47
50
  logLevelToName: () => logLevelToName,
48
51
  logLevels: () => logLevels,
49
52
  logRetryDelayMs: () => logRetryDelayMs,
53
+ monthlyLogsCap: () => monthlyLogsCap,
50
54
  nameColor: () => nameColor,
51
55
  normalizePushThreshold: () => normalizePushThreshold,
56
+ normalizeTimeFormat: () => normalizeTimeFormat,
52
57
  parsePushData: () => parsePushData,
53
58
  personColor: () => personColor,
54
59
  resolveAppColor: () => resolveAppColor,
@@ -123,10 +128,12 @@ var logDelivery = {
123
128
  maxDelayMs: 3e4,
124
129
  pauseMs: 60 * 60 * 1e3
125
130
  };
131
+ var MAX_LOG_PAYLOAD_LENGTH = 16 * 1024;
126
132
 
127
133
  // src/constants/notifications.ts
128
134
  var PUSH_OFF = 5;
129
135
  var DEFAULT_PUSH_THRESHOLD = 2 /* Warn */;
136
+ var DEFAULT_TIME_FORMAT = { clock: "24h", zone: "local" };
130
137
 
131
138
  // src/constants/subscription.ts
132
139
  var SubscriptionLevel = /* @__PURE__ */ ((SubscriptionLevel3) => {
@@ -144,11 +151,17 @@ var subscriptionLevelToName = {
144
151
  };
145
152
  var subscriptionLevels = [0 /* Hobby */, 1 /* Dev */, 2 /* Pro */, 3 /* Enterprise */];
146
153
  var subscriptionLimits = {
147
- [0 /* Hobby */]: { apps: 5, seats: 1, apiKeys: 1, logRetentionDays: 7, monthlyLogs: 1e4, realtimeLogs: false, minPushLevel: Infinity },
148
- [1 /* Dev */]: { apps: 10, seats: 5, apiKeys: 5, logRetentionDays: 30, monthlyLogs: 25e4, realtimeLogs: true, minPushLevel: 3 /* Error */ },
154
+ [0 /* Hobby */]: { apps: 5, seats: 1, apiKeys: 1, logRetentionDays: 7, monthlyLogs: 1e5, realtimeLogs: false, minPushLevel: 4 /* Fatal */ },
155
+ [1 /* Dev */]: { apps: 10, seats: 5, apiKeys: 5, logRetentionDays: 30, monthlyLogs: 5e5, realtimeLogs: true, minPushLevel: 3 /* Error */ },
149
156
  [2 /* Pro */]: { apps: 20, seats: 20, apiKeys: 15, logRetentionDays: 90, monthlyLogs: 2e6, realtimeLogs: true, minPushLevel: 2 /* Warn */ },
150
157
  [3 /* Enterprise */]: { apps: Infinity, seats: Infinity, apiKeys: Infinity, logRetentionDays: Infinity, monthlyLogs: Infinity, realtimeLogs: true, minPushLevel: 0 /* Debug */ }
151
158
  };
159
+ var monthlyLogsCap = {
160
+ /** The lowest level still taken past the cap. */
161
+ minLevelPastCap: 3 /* Error */,
162
+ /** Where every log is refused, as a multiple of the plan's monthlyLogs. */
163
+ hardStopMultiple: 2
164
+ };
152
165
  var subscriptionPlans = {
153
166
  [0 /* Hobby */]: { name: "Hobby", monthlyPrice: 0 },
154
167
  [1 /* Dev */]: { name: "Dev", monthlyPrice: 9 },
@@ -185,6 +198,17 @@ var logRetryDelayMs = (failedAttempts) => Math.min(logDelivery.baseDelayMs * 2 *
185
198
  // src/utils/index.ts
186
199
  var appearanceValues = ["system", "dark", "light"];
187
200
  var isValidAppearance = (value) => appearanceValues.includes(value);
201
+ var timeClocks = ["24h", "12h"];
202
+ var timeZoneChoices = ["local", "utc"];
203
+ var isValidTimeFormat = (value) => {
204
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
205
+ const { clock, zone, ...rest } = value;
206
+ return timeClocks.includes(clock) && timeZoneChoices.includes(zone) && Object.keys(rest).length === 0;
207
+ };
208
+ var normalizeTimeFormat = (value) => {
209
+ const { clock, zone } = value && typeof value === "object" ? value : {};
210
+ return { clock: timeClocks.includes(clock) ? clock : DEFAULT_TIME_FORMAT.clock, zone: timeZoneChoices.includes(zone) ? zone : DEFAULT_TIME_FORMAT.zone };
211
+ };
188
212
  var isValidColor = (value) => /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(value);
189
213
  var hashName = (name) => {
190
214
  let hash = 2166136261;
@@ -259,8 +283,10 @@ var parsePushData = (data) => {
259
283
  DEFAULT_ACCENT,
260
284
  DEFAULT_API_ENDPOINT,
261
285
  DEFAULT_PUSH_THRESHOLD,
286
+ DEFAULT_TIME_FORMAT,
262
287
  LOG_KEY_VERSION,
263
288
  LogLevel,
289
+ MAX_LOG_PAYLOAD_LENGTH,
264
290
  PUSH_OFF,
265
291
  SubscriptionLevel,
266
292
  apiErrorCodeOf,
@@ -274,6 +300,7 @@ var parsePushData = (data) => {
274
300
  isValidLogLevel,
275
301
  isValidSubscriptionLevel,
276
302
  isValidTeamMemberRole,
303
+ isValidTimeFormat,
277
304
  joinApiKey,
278
305
  logDelivery,
279
306
  logDeliveryOutcome,
@@ -282,8 +309,10 @@ var parsePushData = (data) => {
282
309
  logLevelToName,
283
310
  logLevels,
284
311
  logRetryDelayMs,
312
+ monthlyLogsCap,
285
313
  nameColor,
286
314
  normalizePushThreshold,
315
+ normalizeTimeFormat,
287
316
  parsePushData,
288
317
  personColor,
289
318
  resolveAppColor,
package/dist/index.mjs CHANGED
@@ -61,10 +61,12 @@ var logDelivery = {
61
61
  maxDelayMs: 3e4,
62
62
  pauseMs: 60 * 60 * 1e3
63
63
  };
64
+ var MAX_LOG_PAYLOAD_LENGTH = 16 * 1024;
64
65
 
65
66
  // src/constants/notifications.ts
66
67
  var PUSH_OFF = 5;
67
68
  var DEFAULT_PUSH_THRESHOLD = 2 /* Warn */;
69
+ var DEFAULT_TIME_FORMAT = { clock: "24h", zone: "local" };
68
70
 
69
71
  // src/constants/subscription.ts
70
72
  var SubscriptionLevel = /* @__PURE__ */ ((SubscriptionLevel3) => {
@@ -82,11 +84,17 @@ var subscriptionLevelToName = {
82
84
  };
83
85
  var subscriptionLevels = [0 /* Hobby */, 1 /* Dev */, 2 /* Pro */, 3 /* Enterprise */];
84
86
  var subscriptionLimits = {
85
- [0 /* Hobby */]: { apps: 5, seats: 1, apiKeys: 1, logRetentionDays: 7, monthlyLogs: 1e4, realtimeLogs: false, minPushLevel: Infinity },
86
- [1 /* Dev */]: { apps: 10, seats: 5, apiKeys: 5, logRetentionDays: 30, monthlyLogs: 25e4, realtimeLogs: true, minPushLevel: 3 /* Error */ },
87
+ [0 /* Hobby */]: { apps: 5, seats: 1, apiKeys: 1, logRetentionDays: 7, monthlyLogs: 1e5, realtimeLogs: false, minPushLevel: 4 /* Fatal */ },
88
+ [1 /* Dev */]: { apps: 10, seats: 5, apiKeys: 5, logRetentionDays: 30, monthlyLogs: 5e5, realtimeLogs: true, minPushLevel: 3 /* Error */ },
87
89
  [2 /* Pro */]: { apps: 20, seats: 20, apiKeys: 15, logRetentionDays: 90, monthlyLogs: 2e6, realtimeLogs: true, minPushLevel: 2 /* Warn */ },
88
90
  [3 /* Enterprise */]: { apps: Infinity, seats: Infinity, apiKeys: Infinity, logRetentionDays: Infinity, monthlyLogs: Infinity, realtimeLogs: true, minPushLevel: 0 /* Debug */ }
89
91
  };
92
+ var monthlyLogsCap = {
93
+ /** The lowest level still taken past the cap. */
94
+ minLevelPastCap: 3 /* Error */,
95
+ /** Where every log is refused, as a multiple of the plan's monthlyLogs. */
96
+ hardStopMultiple: 2
97
+ };
90
98
  var subscriptionPlans = {
91
99
  [0 /* Hobby */]: { name: "Hobby", monthlyPrice: 0 },
92
100
  [1 /* Dev */]: { name: "Dev", monthlyPrice: 9 },
@@ -123,6 +131,17 @@ var logRetryDelayMs = (failedAttempts) => Math.min(logDelivery.baseDelayMs * 2 *
123
131
  // src/utils/index.ts
124
132
  var appearanceValues = ["system", "dark", "light"];
125
133
  var isValidAppearance = (value) => appearanceValues.includes(value);
134
+ var timeClocks = ["24h", "12h"];
135
+ var timeZoneChoices = ["local", "utc"];
136
+ var isValidTimeFormat = (value) => {
137
+ if (!value || typeof value !== "object" || Array.isArray(value)) return false;
138
+ const { clock, zone, ...rest } = value;
139
+ return timeClocks.includes(clock) && timeZoneChoices.includes(zone) && Object.keys(rest).length === 0;
140
+ };
141
+ var normalizeTimeFormat = (value) => {
142
+ const { clock, zone } = value && typeof value === "object" ? value : {};
143
+ return { clock: timeClocks.includes(clock) ? clock : DEFAULT_TIME_FORMAT.clock, zone: timeZoneChoices.includes(zone) ? zone : DEFAULT_TIME_FORMAT.zone };
144
+ };
126
145
  var isValidColor = (value) => /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(value);
127
146
  var hashName = (name) => {
128
147
  let hash = 2166136261;
@@ -196,8 +215,10 @@ export {
196
215
  DEFAULT_ACCENT,
197
216
  DEFAULT_API_ENDPOINT,
198
217
  DEFAULT_PUSH_THRESHOLD,
218
+ DEFAULT_TIME_FORMAT,
199
219
  LOG_KEY_VERSION,
200
220
  LogLevel,
221
+ MAX_LOG_PAYLOAD_LENGTH,
201
222
  PUSH_OFF,
202
223
  SubscriptionLevel,
203
224
  apiErrorCodeOf,
@@ -211,6 +232,7 @@ export {
211
232
  isValidLogLevel,
212
233
  isValidSubscriptionLevel,
213
234
  isValidTeamMemberRole,
235
+ isValidTimeFormat,
214
236
  joinApiKey,
215
237
  logDelivery,
216
238
  logDeliveryOutcome,
@@ -219,8 +241,10 @@ export {
219
241
  logLevelToName,
220
242
  logLevels,
221
243
  logRetryDelayMs,
244
+ monthlyLogsCap,
222
245
  nameColor,
223
246
  normalizePushThreshold,
247
+ normalizeTimeFormat,
224
248
  parsePushData,
225
249
  personColor,
226
250
  resolveAppColor,