@ttt-productions/q-core 0.1.60 → 0.1.61

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.
@@ -11,16 +11,7 @@ export interface MaintenanceRefusalDetails {
11
11
  }
12
12
  export declare function isMaintenanceRefusalDetails(details: unknown): details is MaintenanceRefusalDetails;
13
13
  export declare function isMaintenanceRefusalError(err: unknown): boolean;
14
- export interface AppConfigLeverRepair {
15
- readonly lever: AppConfigLeverKey;
16
- readonly storedValue: unknown;
17
- }
18
- export interface AppConfigMerge {
19
- readonly next: AppConfig;
20
- readonly repairs: readonly AppConfigLeverRepair[];
21
- }
22
- export declare function mergeAppConfigUpdate(existing: Readonly<Record<string, unknown>> | null | undefined, update: Readonly<Partial<AppConfig>>): AppConfigMerge;
14
+ export declare function mergeAppConfigUpdate(existing: Readonly<Record<string, unknown>> | null | undefined, update: Readonly<Partial<AppConfig>>): AppConfig;
23
15
  export declare function readAppConfigLever<L extends AppConfigLeverKey>(snapshot: Readonly<Record<string, unknown>> | null | undefined, lever: L): AppConfigLevers[L];
24
- export declare function isAppConfigLeverRefused(snapshot: Readonly<Record<string, unknown>> | null | undefined, lever: AppConfigLeverKey): boolean;
25
16
  export declare function readAppConfigLevers(snapshot: Readonly<Record<string, unknown>> | null | undefined): AppConfigLevers;
26
17
  //# sourceMappingURL=app-config.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"app-config.d.ts","sourceRoot":"","sources":["../../src/utils/app-config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE1D,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;AAE5D,MAAM,MAAM,iBAAiB,GAAG,MAAM,eAAe,CAAC;AAEtD,eAAO,MAAM,qBAAqB,6GAAkC,CAAC;AAErE,eAAO,MAAM,kBAAkB,EAAE,eAM/B,CAAC;AAEH,eAAO,MAAM,2BAA2B,4GAC8D,CAAC;AAEvG,eAAO,MAAM,wBAAwB,EAAG,aAAsB,CAAC;AAE/D,eAAO,MAAM,0BAA0B,EAAG,aAAsB,CAAC;AAEjE,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,MAAM,EAAE,OAAO,0BAA0B,CAAC;CACpD;AASD,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,OAAO,GACf,OAAO,IAAI,yBAAyB,CAGtC;AAED,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAQ/D;AAMD,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAC;IAClC,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;CAC/B;AAED,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,SAAS,oBAAoB,EAAE,CAAC;CACnD;AAED,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,EAC9D,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,GACnC,cAAc,CAsBhB;AAED,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,iBAAiB,EAC5D,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,EAC9D,KAAK,EAAE,CAAC,GACP,eAAe,CAAC,CAAC,CAAC,CAKpB;AAED,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,EAC9D,KAAK,EAAE,iBAAiB,GACvB,OAAO,CAGT;AAED,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,GAC7D,eAAe,CAMjB"}
1
+ {"version":3,"file":"app-config.d.ts","sourceRoot":"","sources":["../../src/utils/app-config.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAE1D,MAAM,MAAM,eAAe,GAAG,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC;AAE5D,MAAM,MAAM,iBAAiB,GAAG,MAAM,eAAe,CAAC;AAEtD,eAAO,MAAM,qBAAqB,6GAAkC,CAAC;AAErE,eAAO,MAAM,kBAAkB,EAAE,eAM/B,CAAC;AAEH,eAAO,MAAM,2BAA2B,4GAC8D,CAAC;AAEvG,eAAO,MAAM,wBAAwB,EAAG,aAAsB,CAAC;AAE/D,eAAO,MAAM,0BAA0B,EAAG,aAAsB,CAAC;AAEjE,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,MAAM,EAAE,OAAO,0BAA0B,CAAC;CACpD;AASD,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,OAAO,GACf,OAAO,IAAI,yBAAyB,CAGtC;AAED,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,OAAO,GAAG,OAAO,CAQ/D;AAMD,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,EAC9D,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,GACnC,SAAS,CAcX;AAED,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,iBAAiB,EAC5D,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,EAC9D,KAAK,EAAE,CAAC,GACP,eAAe,CAAC,CAAC,CAAC,CAUpB;AAED,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,IAAI,GAAG,SAAS,GAC7D,eAAe,CAMjB"}
@@ -33,17 +33,9 @@ function definedFields(fields) {
33
33
  return Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined));
34
34
  }
35
35
  export function mergeAppConfigUpdate(existing, update) {
36
- const stored = definedFields(existing ?? {});
37
- const repairs = [];
38
- for (const lever of APP_CONFIG_LEVER_KEYS) {
39
- if (!(lever in stored) || AppConfigSchema.shape[lever].safeParse(stored[lever]).success)
40
- continue;
41
- repairs.push({ lever, storedValue: stored[lever] });
42
- delete stored[lever];
43
- }
44
36
  const parsed = AppConfigSchema.safeParse({
45
37
  ...DEFAULT_APP_CONFIG,
46
- ...stored,
38
+ ...definedFields(existing ?? {}),
47
39
  ...definedFields(update),
48
40
  });
49
41
  if (!parsed.success) {
@@ -51,17 +43,17 @@ export function mergeAppConfigUpdate(existing, update) {
51
43
  const field = issue?.path.join('.') || 'document';
52
44
  throw new Error(`_appConfig/app would not match its schema after this update (${field}: ${issue?.message ?? 'invalid'}). Nothing should be written.`);
53
45
  }
54
- return { next: parsed.data, repairs };
46
+ return parsed.data;
55
47
  }
56
48
  export function readAppConfigLever(snapshot, lever) {
57
- const parsed = AppConfigSchema.shape[lever].safeParse(snapshot?.[lever]);
58
- return parsed.success && parsed.data !== undefined
59
- ? parsed.data
60
- : DEFAULT_APP_CONFIG[lever];
61
- }
62
- export function isAppConfigLeverRefused(snapshot, lever) {
63
49
  const stored = snapshot?.[lever];
64
- return stored !== undefined && !AppConfigSchema.shape[lever].safeParse(stored).success;
50
+ if (stored === undefined)
51
+ return DEFAULT_APP_CONFIG[lever];
52
+ const parsed = AppConfigSchema.shape[lever].safeParse(stored);
53
+ if (!parsed.success) {
54
+ throw new Error(`_appConfig/app.${lever} does not match its schema (${parsed.error.issues[0]?.message ?? 'invalid'}).`);
55
+ }
56
+ return parsed.data;
65
57
  }
66
58
  export function readAppConfigLevers(snapshot) {
67
59
  const levers = {};
@@ -1 +1 @@
1
- {"version":3,"file":"app-config.js","sourceRoot":"","sources":["../../src/utils/app-config.ts"],"names":[],"mappings":"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","sourcesContent":["import { AppConfigSchema } from '../doc-schemas/system.js';\nimport type { AppConfig } from '../doc-schemas/system.js';\n\nexport type AppConfigLevers = Readonly<Required<AppConfig>>;\n\nexport type AppConfigLeverKey = keyof AppConfigLevers;\n\nexport const APP_CONFIG_LEVER_KEYS = AppConfigSchema.keyof().options;\n\nexport const DEFAULT_APP_CONFIG: AppConfigLevers = Object.freeze({\n appVersion: '',\n maintenanceMode: false,\n maintenanceMessage: '',\n registrationEnabled: true,\n announcementMessage: '',\n});\n\nexport const DEFAULT_MAINTENANCE_MESSAGE =\n 'Q-Sports is down for maintenance right now. We will be back shortly — thank you for your patience.';\n\nexport const MAINTENANCE_REFUSAL_CODE = 'unavailable' as const;\n\nexport const MAINTENANCE_REFUSAL_REASON = 'maintenance' as const;\n\nexport interface MaintenanceRefusalDetails {\n readonly reason: typeof MAINTENANCE_REFUSAL_REASON;\n}\n\n// The refusal code as the server (`unavailable`) and the Firebase client SDK\n// (`functions/unavailable`) spell it.\nconst MAINTENANCE_REFUSAL_CODES: ReadonlySet<string> = new Set([\n MAINTENANCE_REFUSAL_CODE,\n `functions/${MAINTENANCE_REFUSAL_CODE}`,\n]);\n\nexport function isMaintenanceRefusalDetails(\n details: unknown,\n): details is MaintenanceRefusalDetails {\n if (!details || typeof details !== 'object') return false;\n return (details as Record<string, unknown>).reason === MAINTENANCE_REFUSAL_REASON;\n}\n\nexport function isMaintenanceRefusalError(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false;\n const candidate = err as { code?: unknown; details?: unknown };\n return (\n typeof candidate.code === 'string' &&\n MAINTENANCE_REFUSAL_CODES.has(candidate.code) &&\n isMaintenanceRefusalDetails(candidate.details)\n );\n}\n\nfunction definedFields(fields: Readonly<Record<string, unknown>>): Record<string, unknown> {\n return Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined));\n}\n\nexport interface AppConfigLeverRepair {\n readonly lever: AppConfigLeverKey;\n readonly storedValue: unknown;\n}\n\nexport interface AppConfigMerge {\n readonly next: AppConfig;\n readonly repairs: readonly AppConfigLeverRepair[];\n}\n\nexport function mergeAppConfigUpdate(\n existing: Readonly<Record<string, unknown>> | null | undefined,\n update: Readonly<Partial<AppConfig>>,\n): AppConfigMerge {\n const stored = definedFields(existing ?? {});\n const repairs: AppConfigLeverRepair[] = [];\n for (const lever of APP_CONFIG_LEVER_KEYS) {\n if (!(lever in stored) || AppConfigSchema.shape[lever].safeParse(stored[lever]).success)\n continue;\n repairs.push({ lever, storedValue: stored[lever] });\n delete stored[lever];\n }\n const parsed = AppConfigSchema.safeParse({\n ...DEFAULT_APP_CONFIG,\n ...stored,\n ...definedFields(update),\n });\n if (!parsed.success) {\n const issue = parsed.error.issues[0];\n const field = issue?.path.join('.') || 'document';\n throw new Error(\n `_appConfig/app would not match its schema after this update (${field}: ${issue?.message ?? 'invalid'}). Nothing should be written.`,\n );\n }\n return { next: parsed.data, repairs };\n}\n\nexport function readAppConfigLever<L extends AppConfigLeverKey>(\n snapshot: Readonly<Record<string, unknown>> | null | undefined,\n lever: L,\n): AppConfigLevers[L] {\n const parsed = AppConfigSchema.shape[lever].safeParse(snapshot?.[lever]);\n return parsed.success && parsed.data !== undefined\n ? (parsed.data as AppConfigLevers[L])\n : DEFAULT_APP_CONFIG[lever];\n}\n\nexport function isAppConfigLeverRefused(\n snapshot: Readonly<Record<string, unknown>> | null | undefined,\n lever: AppConfigLeverKey,\n): boolean {\n const stored = snapshot?.[lever];\n return stored !== undefined && !AppConfigSchema.shape[lever].safeParse(stored).success;\n}\n\nexport function readAppConfigLevers(\n snapshot: Readonly<Record<string, unknown>> | null | undefined,\n): AppConfigLevers {\n const levers: Partial<Record<AppConfigLeverKey, unknown>> = {};\n for (const lever of APP_CONFIG_LEVER_KEYS) {\n levers[lever] = readAppConfigLever(snapshot, lever);\n }\n return levers as AppConfigLevers;\n}\n"]}
1
+ {"version":3,"file":"app-config.js","sourceRoot":"","sources":["../../src/utils/app-config.ts"],"names":[],"mappings":"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","sourcesContent":["import { AppConfigSchema } from '../doc-schemas/system.js';\nimport type { AppConfig } from '../doc-schemas/system.js';\n\nexport type AppConfigLevers = Readonly<Required<AppConfig>>;\n\nexport type AppConfigLeverKey = keyof AppConfigLevers;\n\nexport const APP_CONFIG_LEVER_KEYS = AppConfigSchema.keyof().options;\n\nexport const DEFAULT_APP_CONFIG: AppConfigLevers = Object.freeze({\n appVersion: '',\n maintenanceMode: false,\n maintenanceMessage: '',\n registrationEnabled: true,\n announcementMessage: '',\n});\n\nexport const DEFAULT_MAINTENANCE_MESSAGE =\n 'Q-Sports is down for maintenance right now. We will be back shortly — thank you for your patience.';\n\nexport const MAINTENANCE_REFUSAL_CODE = 'unavailable' as const;\n\nexport const MAINTENANCE_REFUSAL_REASON = 'maintenance' as const;\n\nexport interface MaintenanceRefusalDetails {\n readonly reason: typeof MAINTENANCE_REFUSAL_REASON;\n}\n\n// The refusal code as the server (`unavailable`) and the Firebase client SDK\n// (`functions/unavailable`) spell it.\nconst MAINTENANCE_REFUSAL_CODES: ReadonlySet<string> = new Set([\n MAINTENANCE_REFUSAL_CODE,\n `functions/${MAINTENANCE_REFUSAL_CODE}`,\n]);\n\nexport function isMaintenanceRefusalDetails(\n details: unknown,\n): details is MaintenanceRefusalDetails {\n if (!details || typeof details !== 'object') return false;\n return (details as Record<string, unknown>).reason === MAINTENANCE_REFUSAL_REASON;\n}\n\nexport function isMaintenanceRefusalError(err: unknown): boolean {\n if (!err || typeof err !== 'object') return false;\n const candidate = err as { code?: unknown; details?: unknown };\n return (\n typeof candidate.code === 'string' &&\n MAINTENANCE_REFUSAL_CODES.has(candidate.code) &&\n isMaintenanceRefusalDetails(candidate.details)\n );\n}\n\nfunction definedFields(fields: Readonly<Record<string, unknown>>): Record<string, unknown> {\n return Object.fromEntries(Object.entries(fields).filter(([, value]) => value !== undefined));\n}\n\nexport function mergeAppConfigUpdate(\n existing: Readonly<Record<string, unknown>> | null | undefined,\n update: Readonly<Partial<AppConfig>>,\n): AppConfig {\n const parsed = AppConfigSchema.safeParse({\n ...DEFAULT_APP_CONFIG,\n ...definedFields(existing ?? {}),\n ...definedFields(update),\n });\n if (!parsed.success) {\n const issue = parsed.error.issues[0];\n const field = issue?.path.join('.') || 'document';\n throw new Error(\n `_appConfig/app would not match its schema after this update (${field}: ${issue?.message ?? 'invalid'}). Nothing should be written.`,\n );\n }\n return parsed.data;\n}\n\nexport function readAppConfigLever<L extends AppConfigLeverKey>(\n snapshot: Readonly<Record<string, unknown>> | null | undefined,\n lever: L,\n): AppConfigLevers[L] {\n const stored = snapshot?.[lever];\n if (stored === undefined) return DEFAULT_APP_CONFIG[lever];\n const parsed = AppConfigSchema.shape[lever].safeParse(stored);\n if (!parsed.success) {\n throw new Error(\n `_appConfig/app.${lever} does not match its schema (${parsed.error.issues[0]?.message ?? 'invalid'}).`,\n );\n }\n return parsed.data as AppConfigLevers[L];\n}\n\nexport function readAppConfigLevers(\n snapshot: Readonly<Record<string, unknown>> | null | undefined,\n): AppConfigLevers {\n const levers: Partial<Record<AppConfigLeverKey, unknown>> = {};\n for (const lever of APP_CONFIG_LEVER_KEYS) {\n levers[lever] = readAppConfigLever(snapshot, lever);\n }\n return levers as AppConfigLevers;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ttt-productions/q-core",
3
- "version": "0.1.60",
3
+ "version": "0.1.61",
4
4
  "description": "Core types, Firestore path constants, zod schemas, and shared constants for Q-Sports",
5
5
  "repository": {
6
6
  "type": "git",