diff --git a/packages/shared/seed/SCRIPT_JSON_DOCSTRINGS.md b/packages/shared/seed/SCRIPT_JSON_DOCSTRINGS.md new file mode 100644 index 000000000..026f411b3 --- /dev/null +++ b/packages/shared/seed/SCRIPT_JSON_DOCSTRINGS.md @@ -0,0 +1,648 @@ +# JSON Script Docstring Specification + +## Overview + +JSON scripts support **JSDoc-style docstrings** embedded directly in JSON format. Docstrings provide: +- **Function documentation** - What the function does, parameters, return values +- **Type information** - Parameter types, return types, generic constraints +- **Usage examples** - Code samples showing how to use functions +- **Metadata** - Version, author, deprecation warnings, etc. + +## Docstring Format + +Docstrings are added to functions, constants, and types using a `docstring` property: + +```json +{ + "name": "calculateArea", + "docstring": { + "summary": "Calculates the area of a rectangle", + "description": "Takes width and height and returns the area. Values are clamped to prevent overflow.", + "params": [ + { + "name": "width", + "type": "number", + "description": "Width of the rectangle in pixels" + }, + { + "name": "height", + "type": "number", + "description": "Height of the rectangle in pixels" + } + ], + "returns": { + "type": "number", + "description": "Area in square pixels, clamped between 0 and 1000" + }, + "examples": [ + { + "title": "Basic usage", + "code": "const area = calculateArea(10, 20); // Returns 200" + }, + { + "title": "Large values are clamped", + "code": "const area = calculateArea(100, 100); // Returns 1000 (clamped)" + } + ], + "throws": [ + { + "type": "TypeError", + "description": "If width or height are not numbers" + } + ], + "see": ["multiply", "clamp"], + "since": "1.0.0", + "deprecated": false + }, + "params": [...], + "body": [...] +} +``` + +## Docstring Properties + +### Required Fields + +#### `summary` (string) +**Short one-line description** of what the function does. +- Keep to 80 characters or less +- No period at the end +- Use present tense ("Calculates..." not "Calculate...") + +```json +{ + "summary": "Validates user input against a set of rules" +} +``` + +### Optional Fields + +#### `description` (string) +**Detailed multi-line description** providing context, behavior, edge cases. + +```json +{ + "description": "Validates form input by checking:\n- Name is not empty\n- Age is between 0 and 120\n- Email matches RFC 5322 format\n\nReturns a ValidationResult with errors keyed by field name." +} +``` + +#### `params` (array) +**Parameter documentation** - one entry per parameter. + +```json +{ + "params": [ + { + "name": "input", + "type": "FormInput", + "description": "Form data to validate", + "optional": false, + "default": null + }, + { + "name": "strict", + "type": "boolean", + "description": "Whether to use strict validation rules", + "optional": true, + "default": false + } + ] +} +``` + +**Fields**: +- `name` (string, required) - Parameter name +- `type` (string, optional) - Type annotation +- `description` (string, required) - What the parameter is for +- `optional` (boolean, optional) - Whether parameter is optional +- `default` (any, optional) - Default value if not provided + +#### `returns` (object) +**Return value documentation**. + +```json +{ + "returns": { + "type": "ValidationResult", + "description": "Object with 'valid' boolean and 'errors' object" + } +} +``` + +**Fields**: +- `type` (string, optional) - Return type +- `description` (string, required) - What is returned + +#### `examples` (array) +**Usage examples** showing how to call the function. + +```json +{ + "examples": [ + { + "title": "Valid input", + "code": "const result = validateInput({name: 'John', age: 30});\n// result.valid === true" + }, + { + "title": "Invalid input", + "code": "const result = validateInput({name: '', age: -5});\n// result.errors === {name: '...', age: '...'}" + } + ] +} +``` + +**Fields**: +- `title` (string, required) - Short description of the example +- `code` (string, required) - Example code snippet + +#### `throws` (array) +**Exceptions that can be thrown**. + +```json +{ + "throws": [ + { + "type": "TypeError", + "description": "If input is not an object" + }, + { + "type": "ValidationError", + "description": "If validation fails in strict mode" + } + ] +} +``` + +**Fields**: +- `type` (string, required) - Exception type +- `description` (string, required) - When it's thrown + +#### `see` (array of strings) +**Related functions or documentation** to reference. + +```json +{ + "see": ["isNotEmpty", "validateRange", "FormInput"] +} +``` + +#### `since` (string) +**Version when function was added**. + +```json +{ + "since": "1.2.0" +} +``` + +#### `deprecated` (boolean | object) +**Whether function is deprecated** and what to use instead. + +```json +{ + "deprecated": { + "version": "2.0.0", + "reason": "Use validateFormData instead", + "alternative": "validateFormData" + } +} +``` + +Or simply: +```json +{ + "deprecated": true +} +``` + +#### `author` (string) +**Function author**. + +```json +{ + "author": "John Doe " +} +``` + +#### `version` (string) +**Current version** of the function. + +```json +{ + "version": "1.3.0" +} +``` + +#### `tags` (array of strings) +**Categorization tags**. + +```json +{ + "tags": ["validation", "forms", "user-input"] +} +``` + +#### `internal` (boolean) +**Whether function is internal-only** (not part of public API). + +```json +{ + "internal": true +} +``` + +## Complete Example + +```json +{ + "id": "validate_input_fn", + "name": "validateInput", + "exported": true, + "docstring": { + "summary": "Validates form input against a set of rules", + "description": "Validates user input by checking:\n- Name is not empty\n- Age is between 0 and 120\n- Email matches RFC 5322 format\n\nReturns a ValidationResult with errors keyed by field name.", + "params": [ + { + "name": "input", + "type": "FormInput", + "description": "Form data containing name, age, and email fields" + }, + { + "name": "strict", + "type": "boolean", + "description": "If true, throws on validation failure instead of returning errors", + "optional": true, + "default": false + } + ], + "returns": { + "type": "ValidationResult", + "description": "Object with 'valid' boolean and 'errors' object containing field-specific error messages" + }, + "examples": [ + { + "title": "Valid input", + "code": "const result = validateInput({\n name: 'John Doe',\n age: 30,\n email: 'john@example.com'\n});\n// result.valid === true\n// result.errors === {}" + }, + { + "title": "Invalid input", + "code": "const result = validateInput({\n name: '',\n age: 200,\n email: 'not-an-email'\n});\n// result.valid === false\n// result.errors === {\n// name: 'Name is required',\n// age: 'Age must be between 0 and 120',\n// email: 'Invalid email format'\n// }" + }, + { + "title": "Strict mode", + "code": "try {\n validateInput({name: ''}, true);\n} catch (e) {\n // Throws ValidationError\n}" + } + ], + "throws": [ + { + "type": "TypeError", + "description": "If input is not an object" + }, + { + "type": "ValidationError", + "description": "If validation fails and strict mode is enabled" + } + ], + "see": ["isNotEmpty", "validateRange", "isValidEmail", "FormInput", "ValidationResult"], + "since": "1.0.0", + "author": "MetaBuilder Team", + "tags": ["validation", "forms", "user-input"], + "version": "1.2.0", + "deprecated": false + }, + "params": [ + { + "name": "input", + "type": "FormInput" + }, + { + "name": "strict", + "type": "boolean", + "optional": true, + "default": false + } + ], + "returnType": "ValidationResult", + "body": [...] +} +``` + +## Constant Docstrings + +Constants can also have docstrings: + +```json +{ + "constants": [ + { + "id": "max_retries", + "name": "MAX_RETRIES", + "value": 3, + "exported": true, + "docstring": { + "summary": "Maximum number of retry attempts for failed requests", + "description": "After this many retries, the request will fail permanently. Set to 0 to disable retries.", + "since": "1.0.0", + "see": ["retryRequest", "RETRY_DELAY"] + } + } + ] +} +``` + +## Type Docstrings + +Types in `types.json` can have docstrings: + +```json +{ + "types": [ + { + "id": "validation_result", + "name": "ValidationResult", + "type": "object", + "exported": true, + "docstring": { + "summary": "Result of input validation operation", + "description": "Contains validation status and error messages for each invalid field.", + "examples": [ + { + "title": "Valid result", + "code": "{ valid: true, errors: {} }" + }, + { + "title": "Invalid result", + "code": "{ valid: false, errors: { name: 'Name is required' } }" + } + ], + "see": ["validateInput", "ValidationErrors"], + "since": "1.0.0" + }, + "properties": { + "valid": { + "type": "boolean", + "required": true, + "description": "Whether input passed validation" + }, + "errors": { + "type": "ValidationErrors", + "description": "Error messages keyed by field name" + } + } + } + ] +} +``` + +## Generating Documentation + +Docstrings can be extracted to generate: + +### 1. Markdown Documentation + +```markdown +# validateInput + +Validates form input against a set of rules + +## Description + +Validates user input by checking: +- Name is not empty +- Age is between 0 and 120 +- Email matches RFC 5322 format + +Returns a ValidationResult with errors keyed by field name. + +## Parameters + +- **input** (`FormInput`) - Form data containing name, age, and email fields +- **strict** (`boolean`, optional, default: `false`) - If true, throws on validation failure instead of returning errors + +## Returns + +`ValidationResult` - Object with 'valid' boolean and 'errors' object containing field-specific error messages + +## Examples + +### Valid input +\`\`\`javascript +const result = validateInput({ + name: 'John Doe', + age: 30, + email: 'john@example.com' +}); +// result.valid === true +// result.errors === {} +\`\`\` + +### Invalid input +\`\`\`javascript +const result = validateInput({ + name: '', + age: 200, + email: 'not-an-email' +}); +// result.valid === false +\`\`\` + +## Throws + +- `TypeError` - If input is not an object +- `ValidationError` - If validation fails and strict mode is enabled + +## See Also + +- isNotEmpty +- validateRange +- isValidEmail +- FormInput +- ValidationResult + +## Version + +1.2.0 (since 1.0.0) +``` + +### 2. TypeScript Definitions + +```typescript +/** + * Validates form input against a set of rules + * + * Validates user input by checking: + * - Name is not empty + * - Age is between 0 and 120 + * - Email matches RFC 5322 format + * + * @param input - Form data containing name, age, and email fields + * @param strict - If true, throws on validation failure instead of returning errors + * @returns Object with 'valid' boolean and 'errors' object containing field-specific error messages + * @throws {TypeError} If input is not an object + * @throws {ValidationError} If validation fails and strict mode is enabled + * @since 1.0.0 + * @version 1.2.0 + * @see isNotEmpty + * @see validateRange + */ +export function validateInput( + input: FormInput, + strict?: boolean +): ValidationResult; +``` + +### 3. Storybook Integration + +Docstrings can automatically generate Storybook documentation: + +```typescript +export const ValidateInputStory: Story = { + name: 'validateInput', + parameters: { + docs: { + description: { + story: 'Validates form input against a set of rules\n\n' + + 'Validates user input by checking:\n' + + '- Name is not empty\n' + + '- Age is between 0 and 120' + } + } + }, + argTypes: { + input: { + description: 'Form data containing name, age, and email fields', + type: { name: 'object', required: true } + }, + strict: { + description: 'If true, throws on validation failure', + type: { name: 'boolean', required: false }, + defaultValue: false + } + } +} +``` + +## Runtime Integration + +### JavaScript/TypeScript + +The script executor can expose docstrings via a metadata API: + +```javascript +import { getDocstring } from './script_executor.js' + +const doc = getDocstring(scriptJson, 'validateInput') +console.log(doc.summary) +console.log(doc.params) +console.log(doc.examples) +``` + +### Lua + +```lua +local executor = require('script_executor') +local doc = executor.get_docstring(script, 'validateInput') +print(doc.summary) +``` + +## Best Practices + +### ✅ DO + +- **Write clear summaries** - One line, present tense, under 80 chars +- **Document all parameters** - Even if types are obvious +- **Provide examples** - Show common use cases and edge cases +- **Link related functions** - Use `see` to cross-reference +- **Keep descriptions current** - Update docs when code changes +- **Use proper formatting** - Newlines, bullets, code blocks + +### ❌ DON'T + +- **Repeat type information** - Types are already in `params`/`returnType` +- **Write obvious docs** - "Gets the name" for `getName()` is useless +- **Use jargon** - Explain acronyms and domain terms +- **Forget examples** - Code samples are worth 1000 words +- **Leave deprecated functions** - Mark with `deprecated` or remove + +## Schema Validation + +Docstrings should validate against JSON Schema: + +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "type": "object", + "properties": { + "summary": { "type": "string", "maxLength": 200 }, + "description": { "type": "string" }, + "params": { + "type": "array", + "items": { + "type": "object", + "required": ["name", "description"], + "properties": { + "name": { "type": "string" }, + "type": { "type": "string" }, + "description": { "type": "string" }, + "optional": { "type": "boolean" }, + "default": {} + } + } + }, + "returns": { + "type": "object", + "required": ["description"], + "properties": { + "type": { "type": "string" }, + "description": { "type": "string" } + } + }, + "examples": { + "type": "array", + "items": { + "type": "object", + "required": ["title", "code"], + "properties": { + "title": { "type": "string" }, + "code": { "type": "string" } + } + } + }, + "throws": { + "type": "array", + "items": { + "type": "object", + "required": ["type", "description"], + "properties": { + "type": { "type": "string" }, + "description": { "type": "string" } + } + } + }, + "see": { "type": "array", "items": { "type": "string" } }, + "since": { "type": "string" }, + "deprecated": { "oneOf": [ + { "type": "boolean" }, + { + "type": "object", + "properties": { + "version": { "type": "string" }, + "reason": { "type": "string" }, + "alternative": { "type": "string" } + } + } + ]}, + "author": { "type": "string" }, + "version": { "type": "string" }, + "tags": { "type": "array", "items": { "type": "string" } }, + "internal": { "type": "boolean" } + }, + "required": ["summary"] +} +``` + +## See Also + +- [JSON Script Specification](SCRIPT_JSON_SPEC_ISSUES.md) +- [Type Definitions Guide](../../json_script_example/TYPES.md) +- [Testing Guide](../../json_script_example/tests/README.md)