@raystack/chronicle 0.7.4 → 0.9.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.
Files changed (50) hide show
  1. package/dist/cli/index.js +14 -2
  2. package/package.json +3 -4
  3. package/src/components/api/api-code-snippet.module.css +23 -0
  4. package/src/components/api/api-code-snippet.tsx +64 -0
  5. package/src/components/api/api-field-list.module.css +76 -0
  6. package/src/components/api/api-field-list.tsx +91 -0
  7. package/src/components/api/api-overview.module.css +65 -0
  8. package/src/components/api/api-overview.tsx +216 -0
  9. package/src/components/api/api-response-panel.module.css +62 -0
  10. package/src/components/api/api-response-panel.tsx +54 -0
  11. package/src/components/api/index.ts +5 -6
  12. package/src/components/api/json-editor.tsx +8 -8
  13. package/src/components/api/method-badge.tsx +2 -2
  14. package/src/components/api/playground-dialog.module.css +342 -0
  15. package/src/components/api/playground-dialog.tsx +583 -0
  16. package/src/components/ui/search.module.css +27 -5
  17. package/src/components/ui/search.tsx +28 -19
  18. package/src/lib/api-routes.ts +37 -8
  19. package/src/lib/openapi.ts +26 -0
  20. package/src/lib/page-context.tsx +1 -1
  21. package/src/lib/schema.ts +45 -3
  22. package/src/lib/source.ts +79 -13
  23. package/src/lib/use-api-operation.ts +15 -0
  24. package/src/pages/ApiLayout.module.css +1 -0
  25. package/src/pages/ApiPage.tsx +7 -38
  26. package/src/pages/DocsPage.tsx +40 -1
  27. package/src/server/api/apis-proxy.ts +8 -1
  28. package/src/server/api/ready.ts +15 -0
  29. package/src/server/api/search.ts +159 -85
  30. package/src/server/entry-server.tsx +1 -1
  31. package/src/server/routes/[...slug].md.ts +1 -0
  32. package/src/server/routes/apis/[...slug].md.ts +181 -0
  33. package/src/server/vite-config.ts +11 -0
  34. package/src/themes/default/Layout.module.css +53 -0
  35. package/src/themes/default/Layout.tsx +162 -11
  36. package/src/themes/default/Page.module.css +4 -0
  37. package/src/themes/default/Page.tsx +6 -1
  38. package/src/types/config.ts +2 -1
  39. package/src/components/api/code-snippets.module.css +0 -7
  40. package/src/components/api/code-snippets.tsx +0 -76
  41. package/src/components/api/endpoint-page.module.css +0 -58
  42. package/src/components/api/endpoint-page.tsx +0 -283
  43. package/src/components/api/field-row.module.css +0 -126
  44. package/src/components/api/field-row.tsx +0 -204
  45. package/src/components/api/field-section.module.css +0 -24
  46. package/src/components/api/field-section.tsx +0 -100
  47. package/src/components/api/key-value-editor.module.css +0 -13
  48. package/src/components/api/key-value-editor.tsx +0 -62
  49. package/src/components/api/response-panel.module.css +0 -8
  50. package/src/components/api/response-panel.tsx +0 -44
@@ -1,126 +0,0 @@
1
- .row {
2
- display: flex;
3
- flex-direction: column;
4
- gap: var(--rs-space-2);
5
- padding: var(--rs-space-4) 0;
6
- }
7
-
8
- .row + .row {
9
- border-top: 1px solid var(--rs-color-border-base-primary);
10
- }
11
-
12
- .main {
13
- gap: var(--rs-space-2);
14
- }
15
-
16
- .badges {
17
- gap: var(--rs-space-3);
18
- flex-wrap: wrap;
19
- }
20
-
21
- .name {
22
- font-family: monospace;
23
- font-size: 13px;
24
- color: var(--rs-color-foreground-base-primary);
25
- }
26
-
27
- .type {
28
- font-family: monospace;
29
- font-size: 12px;
30
- padding: 1px var(--rs-space-2);
31
- border-radius: 4px;
32
- background: var(--rs-color-background-neutral-secondary);
33
- color: var(--rs-color-foreground-base-secondary);
34
- }
35
-
36
- .location {
37
- font-size: 12px;
38
- padding: 1px var(--rs-space-2);
39
- border-radius: 4px;
40
- background: var(--rs-color-background-neutral-secondary);
41
- color: var(--rs-color-foreground-base-secondary);
42
- }
43
-
44
- .required {
45
- font-size: 11px;
46
- padding: 1px var(--rs-space-2);
47
- border-radius: 4px;
48
- background: var(--rs-color-background-danger-primary);
49
- color: var(--rs-color-foreground-danger-primary);
50
- }
51
-
52
- .description {
53
- color: var(--rs-color-foreground-base-secondary);
54
- font-size: 13px;
55
- }
56
-
57
- .example {
58
- color: var(--rs-color-foreground-base-secondary);
59
- font-size: 12px;
60
- }
61
-
62
- .example code {
63
- font-family: monospace;
64
- background: var(--rs-color-background-neutral-secondary);
65
- padding: 1px var(--rs-space-2);
66
- border-radius: 3px;
67
- }
68
-
69
- .accordion {
70
- border: none;
71
- }
72
-
73
- .accordion :global([class*="accordion-header"]) {
74
- margin: 0;
75
- }
76
-
77
- .accordion button {
78
- min-height: unset;
79
- padding: 0;
80
- border: none;
81
- background: transparent;
82
- box-shadow: none;
83
- }
84
-
85
- .accordion button:hover,
86
- .accordion button:focus-visible {
87
- background: transparent;
88
- }
89
-
90
- .accordion :global([class*="accordion-content-inner"]) {
91
- padding: var(--rs-space-3) 0 0 0;
92
- border: none;
93
- box-shadow: none;
94
- }
95
-
96
- .children {
97
- display: flex;
98
- flex-direction: column;
99
- padding-left: var(--rs-space-5);
100
- width: 100%;
101
- }
102
-
103
- .trigger {
104
- color: var(--rs-color-foreground-base-secondary);
105
- }
106
-
107
- .fieldInfo {
108
- flex: 1;
109
- min-width: 0;
110
- }
111
-
112
- .fieldInput {
113
- flex: 1;
114
- min-width: 0;
115
- }
116
-
117
- .arrayItems {
118
- gap: var(--rs-space-3);
119
- }
120
-
121
- .arrayItem {
122
- align-items: center;
123
- border: 1px solid var(--rs-color-border-base-primary);
124
- border-radius: 8px;
125
- padding: var(--rs-space-3) var(--rs-space-4);
126
- }
@@ -1,204 +0,0 @@
1
- 'use client'
2
-
3
- import { Flex, Text, Accordion, InputField, Switch, Select, IconButton } from '@raystack/apsara'
4
- import { TrashIcon, PlusIcon } from '@heroicons/react/24/outline'
5
- import type { SchemaField } from '@/lib/schema'
6
- import styles from './field-row.module.css'
7
-
8
- interface FieldRowProps {
9
- field: SchemaField
10
- location?: string
11
- editable?: boolean
12
- value?: unknown
13
- onChange?: (name: string, value: unknown) => void
14
- }
15
-
16
- export function FieldRow({ field, location, editable, value, onChange }: FieldRowProps) {
17
- const hasChildren = field.children && field.children.length > 0
18
- const isArray = field.type.endsWith('[]')
19
-
20
- const label = (
21
- <Flex align="center" className={styles.badges}>
22
- <Text size={2} className={styles.name}>{field.name}</Text>
23
- <Text size={1} className={styles.type}>{field.type}</Text>
24
- {location && <Text size={1} className={styles.location}>{location}</Text>}
25
- {field.required && <Text size={1} className={styles.required}>required</Text>}
26
- </Flex>
27
- )
28
-
29
- if (hasChildren && !isArray) {
30
- const objValue = (value ?? {}) as Record<string, unknown>
31
- return (
32
- <div className={styles.row}>
33
- <Accordion className={styles.accordion}>
34
- <Accordion.Item value={field.name}>
35
- <Accordion.Trigger className={styles.trigger}>{label}</Accordion.Trigger>
36
- <Accordion.Content>
37
- <div className={styles.children}>
38
- {field.children!.map((child) => (
39
- <FieldRow
40
- key={child.name}
41
- field={child}
42
- editable={editable}
43
- value={objValue[child.name]}
44
- onChange={editable ? (name, val) => {
45
- onChange?.(field.name, { ...objValue, [name]: val })
46
- } : undefined}
47
- />
48
- ))}
49
- </div>
50
- </Accordion.Content>
51
- </Accordion.Item>
52
- </Accordion>
53
- </div>
54
- )
55
- }
56
-
57
- if (isArray && editable) {
58
- const items = (Array.isArray(value) ? value : []) as unknown[]
59
- const itemChildren = field.children
60
-
61
- return (
62
- <div className={styles.row}>
63
- <Flex direction="column" className={styles.main}>
64
- <Flex align="center" justify="between">
65
- {label}
66
- <IconButton size={1} onClick={() => {
67
- const newItem = itemChildren ? {} : ''
68
- onChange?.(field.name, [...items, newItem])
69
- }}>
70
- <PlusIcon width={14} height={14} />
71
- </IconButton>
72
- </Flex>
73
- {field.description && <Text size={2} className={styles.description}>{field.description}</Text>}
74
- <Flex direction="column" className={styles.arrayItems}>
75
- {items.map((item, i) => (
76
- <Flex key={i} align="start" gap="small" className={styles.arrayItem}>
77
- {itemChildren ? (
78
- <Flex direction="column" className={styles.children}>
79
- {itemChildren.map((child) => (
80
- <FieldRow
81
- key={child.name}
82
- field={child}
83
- editable
84
- value={(item as Record<string, unknown>)?.[child.name]}
85
- onChange={(name, val) => {
86
- const updated = [...items]
87
- updated[i] = { ...(updated[i] as Record<string, unknown>), [name]: val }
88
- onChange?.(field.name, updated)
89
- }}
90
- />
91
- ))}
92
- </Flex>
93
- ) : (
94
- <EditableInput
95
- field={{
96
- ...field,
97
- name: `${field.name}[${i}]`,
98
- type: field.type.replace('[]', ''),
99
- }}
100
- value={item}
101
- onChange={(_, val) => {
102
- const updated = [...items]
103
- updated[i] = val
104
- onChange?.(field.name, updated)
105
- }}
106
- />
107
- )}
108
- <IconButton size={1} onClick={() => {
109
- const updated = items.filter((_, j) => j !== i)
110
- onChange?.(field.name, updated)
111
- }}>
112
- <TrashIcon width={14} height={14} />
113
- </IconButton>
114
- </Flex>
115
- ))}
116
- </Flex>
117
- </Flex>
118
- </div>
119
- )
120
- }
121
-
122
- // Leaf field — inline layout
123
- return (
124
- <div className={styles.row}>
125
- <Flex align="center" gap="medium">
126
- <Flex direction="column" gap="extra-small" className={styles.fieldInfo}>
127
- {label}
128
- {field.description && <Text size={2} className={styles.description}>{field.description}</Text>}
129
- </Flex>
130
- {editable ? (
131
- <div className={styles.fieldInput}>
132
- <EditableInput field={field} value={value} onChange={onChange} />
133
- </div>
134
- ) : (
135
- field.default !== undefined && (
136
- <Text size={1} className={styles.example}>
137
- Default: <code>{JSON.stringify(field.default)}</code>
138
- </Text>
139
- )
140
- )}
141
- </Flex>
142
- </div>
143
- )
144
- }
145
-
146
- function EditableInput({
147
- field,
148
- value,
149
- onChange,
150
- }: {
151
- field: SchemaField
152
- value: unknown
153
- onChange?: (name: string, value: unknown) => void
154
- }) {
155
- if (field.enum) {
156
- const enumMap = new Map(field.enum.map((opt) => [String(opt), opt]))
157
- return (
158
- <Select value={String(value ?? '')} onValueChange={(v) => onChange?.(field.name, enumMap.get(v) ?? v)}>
159
- <Select.Trigger size="small">
160
- <Select.Value placeholder={`Select ${field.name}`} />
161
- </Select.Trigger>
162
- <Select.Content>
163
- {field.enum.map((opt) => (
164
- <Select.Item key={String(opt)} value={String(opt)}>
165
- {String(opt)}
166
- </Select.Item>
167
- ))}
168
- </Select.Content>
169
- </Select>
170
- )
171
- }
172
-
173
- const baseType = field.type.replace('[]', '').replace(/\(.*\)/, '')
174
-
175
- if (baseType === 'boolean') {
176
- return (
177
- <Switch
178
- checked={Boolean(value)}
179
- onCheckedChange={(checked) => onChange?.(field.name, checked)}
180
- />
181
- )
182
- }
183
-
184
- if (baseType === 'integer' || baseType === 'number') {
185
- return (
186
- <InputField
187
- size="small"
188
- type="number"
189
- placeholder={field.description ?? field.name}
190
- value={String(value ?? '')}
191
- onChange={(e) => onChange?.(field.name, Number(e.target.value))}
192
- />
193
- )
194
- }
195
-
196
- return (
197
- <InputField
198
- size="small"
199
- placeholder={field.description ?? field.name}
200
- value={String(value ?? '')}
201
- onChange={(e) => onChange?.(field.name, e.target.value)}
202
- />
203
- )
204
- }
@@ -1,24 +0,0 @@
1
- .header {
2
- padding-bottom: var(--rs-space-3);
3
- }
4
-
5
- .label {
6
- font-family: monospace;
7
- color: var(--rs-color-foreground-base-secondary);
8
- font-size: 13px;
9
- }
10
-
11
- .separator {
12
- height: 1px;
13
- background: var(--rs-color-border-base-primary);
14
- }
15
-
16
- .tabs {
17
- margin-top: var(--rs-space-3);
18
- }
19
-
20
- .noFields {
21
- color: var(--rs-color-foreground-base-secondary);
22
- padding: var(--rs-space-5);
23
- }
24
-
@@ -1,100 +0,0 @@
1
- 'use client'
2
-
3
- import { Flex, Text, Tabs, CodeBlock } from '@raystack/apsara'
4
- import type { SchemaField } from '@/lib/schema'
5
- import { FieldRow } from './field-row'
6
- import { JsonEditor } from './json-editor'
7
- import styles from './field-section.module.css'
8
-
9
- interface FieldSectionProps {
10
- title: string
11
- label?: string
12
- fields: SchemaField[]
13
- locations?: Record<string, string>
14
- jsonExample?: string
15
- editableJson?: boolean
16
- onJsonChange?: (value: string) => void
17
- alwaysShow?: boolean
18
- editable?: boolean
19
- values?: Record<string, unknown>
20
- onValuesChange?: (values: Record<string, unknown>) => void
21
- children?: React.ReactNode
22
- }
23
-
24
- export function FieldSection({
25
- title, label, fields, locations, jsonExample,
26
- editableJson, onJsonChange, alwaysShow,
27
- editable, values, onValuesChange, children,
28
- }: FieldSectionProps) {
29
- if (fields.length === 0 && !children && !alwaysShow) return null
30
-
31
- const fieldsContent = fields.length > 0 ? (
32
- <Flex direction="column">
33
- {fields.map((field) => (
34
- <FieldRow
35
- key={field.name}
36
- field={field}
37
- location={locations?.[field.name]}
38
- editable={editable}
39
- value={values?.[field.name]}
40
- onChange={editable ? (name, val) => {
41
- onValuesChange?.({ ...values, [name]: val })
42
- } : undefined}
43
- />
44
- ))}
45
- </Flex>
46
- ) : !children ? (
47
- <Text size={2} className={styles.noFields}>No fields defined</Text>
48
- ) : null
49
-
50
- if (jsonExample !== undefined || alwaysShow) {
51
- return (
52
- <Flex direction="column">
53
- <Flex align="center" justify="between" className={styles.header}>
54
- <Text size={4} weight="medium">{title}</Text>
55
- {label && <Text size={2} className={styles.label}>{label}</Text>}
56
- </Flex>
57
- <div className={styles.separator} />
58
- <Tabs defaultValue="fields" className={styles.tabs}>
59
- <Tabs.List>
60
- <Tabs.Tab value="fields">Fields</Tabs.Tab>
61
- <Tabs.Tab value="json">JSON</Tabs.Tab>
62
- </Tabs.List>
63
- <Tabs.Content value="fields">
64
- {fieldsContent}
65
- {children}
66
- </Tabs.Content>
67
- <Tabs.Content value="json">
68
- {editableJson ? (
69
- <JsonEditor
70
- value={jsonExample ?? '{}'}
71
- onChange={onJsonChange}
72
- />
73
- ) : (
74
- <CodeBlock>
75
- <CodeBlock.Header>
76
- <CodeBlock.CopyButton />
77
- </CodeBlock.Header>
78
- <CodeBlock.Content>
79
- <CodeBlock.Code language="json">{jsonExample ?? '{}'}</CodeBlock.Code>
80
- </CodeBlock.Content>
81
- </CodeBlock>
82
- )}
83
- </Tabs.Content>
84
- </Tabs>
85
- </Flex>
86
- )
87
- }
88
-
89
- return (
90
- <Flex direction="column">
91
- <Flex align="center" justify="between" className={styles.header}>
92
- <Text size={4} weight="medium">{title}</Text>
93
- {label && <Text size={2} className={styles.label}>{label}</Text>}
94
- </Flex>
95
- <div className={styles.separator} />
96
- {fieldsContent}
97
- {children}
98
- </Flex>
99
- )
100
- }
@@ -1,13 +0,0 @@
1
- .editor {
2
- padding: var(--rs-space-3) 0;
3
- }
4
-
5
- .row {
6
- width: 100%;
7
- }
8
-
9
- .input {
10
- flex: 1;
11
- min-width: 0;
12
- }
13
-
@@ -1,62 +0,0 @@
1
- 'use client'
2
-
3
- import { Flex, InputField, IconButton, Button } from '@raystack/apsara'
4
- import { TrashIcon, PlusIcon } from '@heroicons/react/24/outline'
5
- import styles from './key-value-editor.module.css'
6
-
7
- export interface KeyValueEntry {
8
- key: string
9
- value: string
10
- }
11
-
12
- interface KeyValueEditorProps {
13
- entries: KeyValueEntry[]
14
- onChange: (entries: KeyValueEntry[]) => void
15
- }
16
-
17
- export function KeyValueEditor({ entries, onChange }: KeyValueEditorProps) {
18
- const updateEntry = (index: number, field: 'key' | 'value', val: string) => {
19
- const updated = [...entries]
20
- updated[index] = { ...updated[index], [field]: val }
21
- onChange(updated)
22
- }
23
-
24
- const removeEntry = (index: number) => {
25
- onChange(entries.filter((_, i) => i !== index))
26
- }
27
-
28
- const addEntry = () => {
29
- onChange([...entries, { key: '', value: '' }])
30
- }
31
-
32
- return (
33
- <Flex direction="column" gap="small" className={styles.editor}>
34
- {entries.map((entry, i) => (
35
- <Flex key={i} align="center" gap="small" className={styles.row}>
36
- <div className={styles.input}>
37
- <InputField
38
- size="small"
39
- placeholder="Header name"
40
- value={entry.key}
41
- onChange={(e) => updateEntry(i, 'key', e.target.value)}
42
- />
43
- </div>
44
- <div className={styles.input}>
45
- <InputField
46
- size="small"
47
- placeholder="Value"
48
- value={entry.value}
49
- onChange={(e) => updateEntry(i, 'value', e.target.value)}
50
- />
51
- </div>
52
- <IconButton size={1} aria-label={`Delete ${entry.key || 'entry'}`} onClick={() => removeEntry(i)}>
53
- <TrashIcon width={14} height={14} />
54
- </IconButton>
55
- </Flex>
56
- ))}
57
- <Button variant="ghost" size="small" onClick={addEntry}>
58
- <PlusIcon width={14} height={14} /> Add header
59
- </Button>
60
- </Flex>
61
- )
62
- }
@@ -1,8 +0,0 @@
1
- .panel {
2
- width: 100%;
3
- }
4
-
5
- /* stylelint-disable-next-line selector-pseudo-class-no-unknown */
6
- .panel :global([class*="code-block-module_header"]) {
7
- justify-content: space-between;
8
- }
@@ -1,44 +0,0 @@
1
- 'use client'
2
-
3
- import { CodeBlock } from '@raystack/apsara'
4
- import styles from './response-panel.module.css'
5
-
6
- interface ResponsePanelProps {
7
- responses: {
8
- status: string
9
- description?: string
10
- jsonExample?: string
11
- }[]
12
- }
13
-
14
- export function ResponsePanel({ responses }: ResponsePanelProps) {
15
- const withExamples = responses.filter((r) => r.jsonExample)
16
- if (withExamples.length === 0) return null
17
-
18
- const defaultValue = withExamples[0].status
19
-
20
- return (
21
- <CodeBlock defaultValue={defaultValue} className={styles.panel}>
22
- <CodeBlock.Header>
23
- <CodeBlock.LanguageSelect>
24
- <CodeBlock.LanguageSelectTrigger />
25
- <CodeBlock.LanguageSelectContent>
26
- {withExamples.map((resp) => (
27
- <CodeBlock.LanguageSelectItem key={resp.status} value={resp.status}>
28
- {resp.status} {resp.description ?? resp.status}
29
- </CodeBlock.LanguageSelectItem>
30
- ))}
31
- </CodeBlock.LanguageSelectContent>
32
- </CodeBlock.LanguageSelect>
33
- <CodeBlock.CopyButton />
34
- </CodeBlock.Header>
35
- <CodeBlock.Content>
36
- {withExamples.map((resp) => (
37
- <CodeBlock.Code key={resp.status} value={resp.status} language="json">
38
- {resp.jsonExample!}
39
- </CodeBlock.Code>
40
- ))}
41
- </CodeBlock.Content>
42
- </CodeBlock>
43
- )
44
- }