@read-frog/definitions 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,22 @@
1
+ export const CHROME_EXTENSION_ORIGIN = "chrome-extension://modkelfkcfjpgbfmnbnllalkiogfofhb"
2
+ export const EDGE_EXTENSION_ORIGIN = "extension://cbcbomlgikfbdnoaohcjfledcoklcjbo"
3
+
4
+ export const READFROG_DOMAIN = "readfrog.app"
5
+ export const LOCALHOST_DOMAIN = "localhost"
6
+
7
+ // Browser-accessible TanStack Start dev server.
8
+ export const WEBSITE_DEV_PORT = 8888
9
+ export const WEBSITE_DEV_URL = `http://localhost:${WEBSITE_DEV_PORT}`
10
+
11
+ // Legacy Next.js Caddy proxy kept for compatibility while the old app is still present.
12
+ export const CADDY_DEV_PORT = 4433
13
+ export const WEBSITE_CADDY_DEV_URL = `https://localhost:${CADDY_DEV_PORT}`
14
+
15
+ export const WEBSITE_PROD_URL = "https://www.readfrog.app"
16
+ export const TRUSTED_ORIGINS = [CHROME_EXTENSION_ORIGIN, EDGE_EXTENSION_ORIGIN, WEBSITE_PROD_URL]
17
+ export const API_DEV_PORT = 7777
18
+ export const API_DEV_URL = `http://localhost:${API_DEV_PORT}`
19
+ export const API_PROD_URL = "https://api.readfrog.app"
20
+
21
+ // Domain constants for cookie monitoring and other domain-based operations
22
+ export const AUTH_DOMAINS = [READFROG_DOMAIN, LOCALHOST_DOMAIN] as const
package/src/index.ts ADDED
@@ -0,0 +1,10 @@
1
+ export * from "./constants/app"
2
+ export * from "./constants/auth"
3
+ export * from "./constants/beta-access"
4
+ export * from "./constants/column"
5
+ export * from "./constants/dictionary"
6
+ export * from "./constants/url"
7
+ export * from "./schemas/cell-value"
8
+ export * from "./schemas/version"
9
+ export * from "./types/column"
10
+ export * from "./types/languages"
@@ -0,0 +1,54 @@
1
+ import type { ColumnConfig } from "@/types/column"
2
+
3
+ import { z } from "zod"
4
+
5
+ /**
6
+ * Create a Zod schema for a cell value based on column config.
7
+ * All cell schemas are nullable (empty cells are valid).
8
+ */
9
+ export function createCellSchema(config: ColumnConfig): z.ZodType {
10
+ switch (config.type) {
11
+ case "string":
12
+ return z.string().nullable()
13
+ case "number":
14
+ return z.number({ message: "Must be a valid number" }).nullable()
15
+ case "boolean":
16
+ return z.boolean().nullable()
17
+ case "date":
18
+ return z.iso.date().nullable()
19
+ case "select":
20
+ if (config.options.length === 0) {
21
+ return z.string().nullable()
22
+ }
23
+ return z
24
+ .enum(config.options.map(o => o.id) as [string, ...string[]])
25
+ .nullable()
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Create a Zod schema for an entire row based on column definitions.
31
+ * Each column becomes a field in the object schema.
32
+ */
33
+ export function createRowSchema(
34
+ columns: Array<{ id: string, name: string, config: ColumnConfig }>,
35
+ ): z.ZodObject<Record<string, z.ZodType>> {
36
+ const shape: Record<string, z.ZodType> = {}
37
+
38
+ for (const column of columns) {
39
+ // Use column name as description for AI schema generation
40
+ shape[column.id] = createCellSchema(column.config).describe(column.name)
41
+ }
42
+
43
+ return z.object(shape)
44
+ }
45
+
46
+ /**
47
+ * Convert row schema to JSON Schema for AI structured outputs.
48
+ * Use this when generating prompts for LLMs that support structured outputs.
49
+ */
50
+ export function rowSchemaToJsonSchema(
51
+ columns: Array<{ id: string, name: string, config: ColumnConfig }>,
52
+ ) {
53
+ return z.toJSONSchema(createRowSchema(columns))
54
+ }
@@ -0,0 +1,101 @@
1
+ import { z } from "zod"
2
+
3
+ /**
4
+ * Semantic version regex pattern
5
+ * Matches versions like: 1.0.0, 10.20.30
6
+ * Does NOT match: v1.0.0, 1.0.0-alpha, 1.0, 1.-1.0
7
+ */
8
+ export const SEMANTIC_VERSION_REGEX = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)$/
9
+
10
+ /**
11
+ * Zod schema for semantic version validation
12
+ * Validates semantic version strings according to SemVer conventions
13
+ * Requires exactly 3 parts: major.minor.patch
14
+ *
15
+ * @example
16
+ * semanticVersionSchema.parse('1.0.0') // ✓ valid
17
+ * semanticVersionSchema.parse('10.20.30') // ✓ valid
18
+ * semanticVersionSchema.parse('1.11') // ✗ throws error (must have 3 parts)
19
+ * semanticVersionSchema.parse('v1.0.0') // ✗ throws error
20
+ * semanticVersionSchema.parse('1.0.0-alpha') // ✗ throws error
21
+ */
22
+ export const semanticVersionSchema = z.string().regex(
23
+ SEMANTIC_VERSION_REGEX,
24
+ "Must be a valid semantic version with exactly 3 parts (e.g., 1.0.0, 10.20.30)",
25
+ ).refine(
26
+ (version) => {
27
+ // Additional validation: ensure all parts are non-negative numbers
28
+ const parts = version.split(".")
29
+ return parts.length === 3 && parts.every(part => !Number.isNaN(Number(part)) && Number(part) >= 0)
30
+ },
31
+ { message: "Version must have exactly 3 parts and all parts must be non-negative numbers" },
32
+ )
33
+
34
+ /**
35
+ * Type for semantic version string
36
+ */
37
+ export type SemanticVersion = z.infer<typeof semanticVersionSchema>
38
+
39
+ /**
40
+ * Version type classification
41
+ */
42
+ export type VersionType = "major" | "minor" | "patch"
43
+
44
+ /**
45
+ * Parse a semantic version string into its components
46
+ * Validates the input using semanticVersionSchema before parsing
47
+ *
48
+ * @param version - The version string to parse (must be in format major.minor.patch)
49
+ * @returns An object containing the major, minor, and patch numbers
50
+ * @throws {z.ZodError} If the version string is invalid
51
+ *
52
+ * @example
53
+ * parseSemanticVersion('1.2.3') // { major: 1, minor: 2, patch: 3 }
54
+ * parseSemanticVersion('10.20.30') // { major: 10, minor: 20, patch: 30 }
55
+ * parseSemanticVersion('1.0') // throws error - must have 3 parts
56
+ * parseSemanticVersion('v1.0.0') // throws error - invalid format
57
+ */
58
+ export function parseSemanticVersion(version: string): {
59
+ major: number
60
+ minor: number
61
+ patch: number
62
+ } {
63
+ // Validate the version string first
64
+ const validatedVersion = semanticVersionSchema.parse(version)
65
+
66
+ const parts = validatedVersion.split(".")
67
+ return {
68
+ major: Number.parseInt(parts[0]!, 10),
69
+ minor: Number.parseInt(parts[1]!, 10),
70
+ patch: Number.parseInt(parts[2]!, 10),
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Determine the version type (major, minor, or patch) based on semantic versioning rules
76
+ * Validates the input using semanticVersionSchema before classification
77
+ *
78
+ * @param version - The version string to classify
79
+ * @returns The version type classification
80
+ * @throws {z.ZodError} If the version string is invalid
81
+ *
82
+ * @example
83
+ * getVersionType('1.0.0') // 'major'
84
+ * getVersionType('1.2.0') // 'minor'
85
+ * getVersionType('1.2.3') // 'patch'
86
+ * getVersionType('1.0') // throws error - must have 3 parts
87
+ */
88
+ export function getVersionType(version: string): VersionType {
89
+ // parseSemanticVersion already validates the version
90
+ const { major, minor, patch } = parseSemanticVersion(version)
91
+
92
+ if (major > 0 && minor === 0 && patch === 0) {
93
+ return "major"
94
+ }
95
+ else if (minor > 0 && patch === 0) {
96
+ return "minor"
97
+ }
98
+ else {
99
+ return "patch"
100
+ }
101
+ }
@@ -0,0 +1,60 @@
1
+ import { z } from "zod"
2
+
3
+ // Source of truth - can be iterated at runtime
4
+ export const COLUMN_TYPES = ["string", "number", "boolean", "date", "select"] as const
5
+
6
+ export type ColumnType = (typeof COLUMN_TYPES)[number]
7
+
8
+ export const selectOptionSchema = z.object({
9
+ id: z.string(),
10
+ value: z.string(),
11
+ color: z.string().regex(/^#[0-9a-f]{6}$/i, "Color must be a valid hex color"),
12
+ })
13
+
14
+ export const columnConfigSchema = z.discriminatedUnion("type", [
15
+ z.object({ type: z.literal("string") }),
16
+ z.object({
17
+ type: z.literal("number"),
18
+ decimal: z.number().int().min(0).default(0),
19
+ format: z.enum(["number", "currency", "percent"]).default("number"),
20
+ }),
21
+ z.object({ type: z.literal("boolean") }),
22
+ z.object({
23
+ type: z.literal("date"),
24
+ dateFormat: z.string().optional(),
25
+ }),
26
+ z.object({
27
+ type: z.literal("select"),
28
+ options: z.array(selectOptionSchema).default([]),
29
+ }),
30
+ ])
31
+
32
+ export type ColumnConfig = z.infer<typeof columnConfigSchema>
33
+ export type SelectOption = z.infer<typeof selectOptionSchema>
34
+
35
+ // Column type metadata (label, default config)
36
+ export const COLUMN_TYPE_INFO: Record<
37
+ ColumnType,
38
+ { label: string, defaultConfig: ColumnConfig }
39
+ > = {
40
+ string: { label: "Text", defaultConfig: { type: "string" } },
41
+ number: {
42
+ label: "Number",
43
+ defaultConfig: { type: "number", decimal: 0, format: "number" },
44
+ },
45
+ boolean: { label: "Checkbox", defaultConfig: { type: "boolean" } },
46
+ date: { label: "Date", defaultConfig: { type: "date" } },
47
+ select: { label: "Select", defaultConfig: { type: "select", options: [] } },
48
+ }
49
+
50
+ export function isNumberConfig(
51
+ config: ColumnConfig,
52
+ ): config is Extract<ColumnConfig, { type: "number" }> {
53
+ return config.type === "number"
54
+ }
55
+
56
+ export function isSelectConfig(
57
+ config: ColumnConfig,
58
+ ): config is Extract<ColumnConfig, { type: "select" }> {
59
+ return config.type === "select"
60
+ }