@myop/cli 0.1.40 → 0.1.41

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,357 @@
1
+ # Component Public API
2
+
3
+ Myop components communicate with host applications through exactly 2 global functions defined on `window`. Both must be defined synchronously during top-level script execution.
4
+
5
+ ## Contents
6
+ - [myop_init_interface](#myop_init_interface)
7
+ - [myop_cta_handler](#myop_cta_handler)
8
+ - [Preview Script](#preview-script)
9
+ - [Complete Example](#complete-example)
10
+ - [Host-Side Integration](#host-side-integration)
11
+
12
+ ## myop_init_interface
13
+
14
+ **Direction:** Host Application -> Component
15
+
16
+ **Purpose:** Initialize or update the component with data. Also used to read current state.
17
+
18
+ ### Dual Signature
19
+
20
+ ```javascript
21
+ // Setter: Host sends data to component
22
+ window.myop_init_interface(data); // returns void
23
+
24
+ // Getter: Host reads current component state
25
+ const state = window.myop_init_interface(); // returns MyopInitData
26
+ ```
27
+
28
+ ### Implementation Pattern
29
+
30
+ ```javascript
31
+ (function() {
32
+ var currentState = {};
33
+
34
+ window.myop_init_interface = function(data) {
35
+ // Getter mode: return current state when called without arguments
36
+ if (!data) return currentState;
37
+
38
+ // Setter mode: store state and render synchronously
39
+ currentState = data;
40
+ render(data);
41
+ };
42
+
43
+ function render(data) {
44
+ var root = document.getElementById('app-root');
45
+ // Build HTML string and set innerHTML in one operation
46
+ root.innerHTML = '<h1>' + data.title + '</h1>';
47
+ }
48
+ })();
49
+ ```
50
+
51
+ ### Rules
52
+
53
+ 1. **MUST be defined at top-level** - The host calls this function immediately after loading the component. If it's not yet defined, the component will not receive data.
54
+
55
+ 2. **MUST render synchronously** - When called with data, the component must update the DOM before the function returns. No `setTimeout`, no `requestAnimationFrame`, no `await`.
56
+
57
+ 3. **MUST handle getter mode** - When called without arguments (`myop_init_interface()`), return the current state object. The host uses this to read component state.
58
+
59
+ 4. **MUST be idempotent** - Can be called multiple times with updated data. Each call should fully re-render.
60
+
61
+ ### WRONG Examples
62
+
63
+ ```javascript
64
+ // WRONG: Defined inside DOMContentLoaded (too late)
65
+ document.addEventListener('DOMContentLoaded', () => {
66
+ window.myop_init_interface = function(data) { render(data); };
67
+ });
68
+
69
+ // WRONG: Async rendering (host expects synchronous update)
70
+ window.myop_init_interface = async function(data) {
71
+ const extra = await fetch('/api/data'); // NO!
72
+ render(data, extra);
73
+ };
74
+
75
+ // WRONG: Deferred rendering
76
+ window.myop_init_interface = function(data) {
77
+ setTimeout(() => render(data), 0); // NO!
78
+ };
79
+
80
+ // WRONG: Missing getter mode
81
+ window.myop_init_interface = function(data) {
82
+ render(data); // What happens when called with no args?
83
+ };
84
+ ```
85
+
86
+ ## myop_cta_handler
87
+
88
+ **Direction:** Component -> Host Application
89
+
90
+ **Purpose:** Send user actions (clicks, selections, form submissions) from the component to the host application. Also used for standard actions like requesting a size change.
91
+
92
+ ### Signature
93
+
94
+ ```javascript
95
+ window.myop_cta_handler(action_id, payload);
96
+ ```
97
+
98
+ | Parameter | Type | Description |
99
+ |-----------|------|-------------|
100
+ | `action_id` | `string` | Action identifier (kebab-case, e.g. `'item-clicked'`) |
101
+ | `payload` | `object` | Action-specific data |
102
+
103
+ ### Implementation Pattern
104
+
105
+ ```javascript
106
+ (function() {
107
+ // Define a no-op default (host will override this)
108
+ window.myop_cta_handler = function(action, payload) {
109
+ // Will be replaced by host application
110
+ };
111
+
112
+ // Call it when user interacts
113
+ document.getElementById('app-root').addEventListener('click', function(e) {
114
+ var button = e.target.closest('button');
115
+ if (button) {
116
+ window.myop_cta_handler('button-clicked', {
117
+ buttonId: button.dataset.id,
118
+ label: button.textContent
119
+ });
120
+ }
121
+ });
122
+ })();
123
+ ```
124
+
125
+ ### Standard Actions
126
+
127
+ These action IDs have special meaning and are handled by the Myop SDK:
128
+
129
+ | Action ID | Payload | Purpose |
130
+ |-----------|---------|---------|
131
+ | `'size-requested'` | `{ width?, height?, minWidth?, maxWidth?, minHeight?, maxHeight?, required? }` | Request the host to resize the component container |
132
+
133
+ ```javascript
134
+ // Request more width
135
+ window.myop_cta_handler('size-requested', {
136
+ width: 400,
137
+ minWidth: 300,
138
+ maxWidth: 600,
139
+ required: true // true = content is clipped without this size
140
+ // false = preference only, host may ignore
141
+ });
142
+ ```
143
+
144
+ ### Custom Actions
145
+
146
+ Define any custom actions your component needs. Use kebab-case for action IDs:
147
+
148
+ ```javascript
149
+ // User selects an item
150
+ window.myop_cta_handler('item-selected', { itemId: 'abc-123' });
151
+
152
+ // Form submitted
153
+ window.myop_cta_handler('form-submitted', {
154
+ name: 'John',
155
+ email: 'john@example.com'
156
+ });
157
+
158
+ // Status changed
159
+ window.myop_cta_handler('status-changed', { from: 'draft', to: 'published' });
160
+
161
+ // No-payload action
162
+ window.myop_cta_handler('refresh-requested', {});
163
+ ```
164
+
165
+ ### Rules
166
+
167
+ 1. **Define at top-level** - Even though the host overrides it, defining a no-op default prevents errors if the component fires an action before the host sets up its handler.
168
+
169
+ 2. **Use kebab-case** for action IDs: `'item-clicked'` not `'itemClicked'` or `'ITEM_CLICKED'`.
170
+
171
+ 3. **Payload must be serializable** - Plain objects only. No DOM elements, functions, or class instances.
172
+
173
+ 4. **Document all actions** in the `MyopCtaPayloads` type definition so the host developer knows what to expect.
174
+
175
+ ## Preview Script
176
+
177
+ The `<script id="myop_preview">` block provides mock data for development. In production, the Myop SDK replaces this with a real data call.
178
+
179
+ ```html
180
+ <!-- Runs during development, removed in production -->
181
+ <script id="myop_preview">
182
+ window.myop_init_interface({
183
+ title: 'Preview Mode',
184
+ items: [
185
+ { id: '1', label: 'Sample item 1' },
186
+ { id: '2', label: 'Sample item 2' },
187
+ { id: '3', label: 'Sample item 3' }
188
+ ]
189
+ });
190
+ </script>
191
+ ```
192
+
193
+ ### Testing Delayed Load
194
+
195
+ ```html
196
+ <script id="myop_preview">
197
+ // Simulate delayed data arrival
198
+ setTimeout(function() {
199
+ window.myop_init_interface({
200
+ title: 'Loaded after delay',
201
+ items: []
202
+ });
203
+ }, 1000);
204
+ </script>
205
+ ```
206
+
207
+ ## Complete Example
208
+
209
+ ```html
210
+ <!DOCTYPE html>
211
+ <html lang="en">
212
+ <head>
213
+ <meta charset="UTF-8">
214
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
215
+ <meta name="myop:size" content='{"width":350,"height":"100%","minWidth":250}'>
216
+ <title>Task List</title>
217
+ <script type="myop/types">
218
+ interface MyopInitData {
219
+ tasks: Array<{
220
+ id: string;
221
+ title: string;
222
+ completed: boolean;
223
+ }>;
224
+ }
225
+
226
+ interface MyopCtaPayloads {
227
+ 'task-toggled': { taskId: string; completed: boolean };
228
+ 'task-deleted': { taskId: string };
229
+ 'size-requested': {
230
+ width?: number | null;
231
+ height?: number | null;
232
+ minWidth?: number | null;
233
+ maxWidth?: number | null;
234
+ minHeight?: number | null;
235
+ maxHeight?: number | null;
236
+ required?: boolean;
237
+ };
238
+ }
239
+
240
+ declare function myop_init_interface(): MyopInitData;
241
+ declare function myop_init_interface(data: MyopInitData): void;
242
+ declare function myop_cta_handler<K extends keyof MyopCtaPayloads>(
243
+ action: K, payload: MyopCtaPayloads[K]
244
+ ): void;
245
+ </script>
246
+ <style>
247
+ * { box-sizing: border-box; margin: 0; padding: 0; }
248
+ html, body { width: 100%; height: 100%; overflow: hidden;
249
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
250
+ color: #333;
251
+ }
252
+ #app-root { width: 100%; height: 100%; display: flex; flex-direction: column; }
253
+ .header { padding: 12px 16px; border-bottom: 1px solid #eee; font-weight: 600; }
254
+ .task-list { flex: 1; overflow-y: auto; min-height: 0; }
255
+ .task { display: flex; align-items: center; padding: 10px 16px;
256
+ border-bottom: 1px solid #f0f0f0; gap: 10px; }
257
+ .task.done .task-title { text-decoration: line-through; color: #999; }
258
+ .task-title { flex: 1; }
259
+ .delete-btn { cursor: pointer; color: #999; border: none;
260
+ background: none; font-size: 16px; }
261
+ .delete-btn:hover { color: #e74c3c; }
262
+ </style>
263
+ </head>
264
+ <body>
265
+ <div id="app-root">
266
+ <div class="header">Tasks</div>
267
+ <div class="task-list"></div>
268
+ </div>
269
+
270
+ <script>
271
+ (function() {
272
+ var state = { tasks: [] };
273
+ var taskList = document.querySelector('.task-list');
274
+
275
+ function render(data) {
276
+ state = data;
277
+ taskList.innerHTML = data.tasks.map(function(task) {
278
+ return '<div class="task' + (task.completed ? ' done' : '') + '" data-id="' + task.id + '">' +
279
+ '<input type="checkbox"' + (task.completed ? ' checked' : '') + '>' +
280
+ '<span class="task-title">' + task.title + '</span>' +
281
+ '<button class="delete-btn" data-action="delete">&times;</button>' +
282
+ '</div>';
283
+ }).join('');
284
+ }
285
+
286
+ window.myop_init_interface = function(data) {
287
+ if (!data) return state;
288
+ render(data);
289
+ };
290
+
291
+ window.myop_cta_handler = function(action, payload) {};
292
+
293
+ // Event delegation for all task interactions
294
+ taskList.addEventListener('click', function(e) {
295
+ var taskEl = e.target.closest('.task');
296
+ if (!taskEl) return;
297
+ var taskId = taskEl.dataset.id;
298
+
299
+ if (e.target.type === 'checkbox') {
300
+ window.myop_cta_handler('task-toggled', {
301
+ taskId: taskId,
302
+ completed: e.target.checked
303
+ });
304
+ } else if (e.target.dataset.action === 'delete') {
305
+ window.myop_cta_handler('task-deleted', { taskId: taskId });
306
+ }
307
+ });
308
+ })();
309
+ </script>
310
+
311
+ <script id="myop_preview">
312
+ window.myop_init_interface({
313
+ tasks: [
314
+ { id: '1', title: 'Review pull request', completed: false },
315
+ { id: '2', title: 'Update documentation', completed: true },
316
+ { id: '3', title: 'Deploy to staging', completed: false }
317
+ ]
318
+ });
319
+ </script>
320
+ </body>
321
+ </html>
322
+ ```
323
+
324
+ ## Host-Side Integration
325
+
326
+ For context on how the host application uses these functions (useful when debugging):
327
+
328
+ ```javascript
329
+ // React host example using @myop/sdk
330
+ import { getHostModule } from '@myop/sdk';
331
+
332
+ const { hostSDK } = await getHostModule();
333
+ const component = await hostSDK.loadComponent(componentConfig, containerElement);
334
+
335
+ // Send data to component
336
+ component.props.myop_init_interface({
337
+ tasks: fetchedTasks
338
+ });
339
+
340
+ // Listen for component actions
341
+ component.props.myop_cta_handler = (action, payload) => {
342
+ switch (action) {
343
+ case 'task-toggled':
344
+ updateTask(payload.taskId, { completed: payload.completed });
345
+ break;
346
+ case 'task-deleted':
347
+ deleteTask(payload.taskId);
348
+ break;
349
+ case 'size-requested':
350
+ resizeContainer(payload);
351
+ break;
352
+ }
353
+ };
354
+
355
+ // Read current component state
356
+ const currentState = component.props.myop_init_interface();
357
+ ```
@@ -0,0 +1,218 @@
1
+ # Development Workflow & CLI Commands
2
+
3
+ The Myop CLI provides commands for creating, developing, and deploying components.
4
+
5
+ ## Contents
6
+ - [CLI Installation](#cli-installation)
7
+ - [Commands Reference](#commands-reference)
8
+ - [Create Command](#create-command)
9
+ - [Dev Command](#dev-command)
10
+ - [Sync Command](#sync-command)
11
+ - [Authentication](#authentication)
12
+ - [Configuration](#configuration)
13
+
14
+ ## CLI Installation
15
+
16
+ ```bash
17
+ npm install -g @myop/cli
18
+ ```
19
+
20
+ After installation, the `myop` command is available globally.
21
+
22
+ ## Commands Reference
23
+
24
+ | Command | Description |
25
+ |---------|-------------|
26
+ | `myop create` | Create a new component (interactive, starts dev server after) |
27
+ | `myop dev` | Start dev server with file watching and HMR |
28
+ | `myop push` | Upload component to Myop platform |
29
+ | `myop sync` | Build and upload component to Myop platform |
30
+ | `myop login` | Authenticate with Myop (opens browser for OAuth) |
31
+ | `myop logout` | Clear stored credentials |
32
+ | `myop whoami` | Show current authenticated user |
33
+ | `myop --ci` | Output status as JSON (for CI/CD) |
34
+ | `myop -m` / `myop --monorepo` | Monorepo mode: scan and dev multiple components |
35
+
36
+ ## Create Command
37
+
38
+ Creates a new Myop component in the current directory.
39
+
40
+ ```bash
41
+ myop create
42
+ ```
43
+
44
+ **What it does:**
45
+ 1. Prompts for component name (defaults to directory name)
46
+ 2. Checks that `index.html` and `myop.config.json` don't already exist
47
+ 3. Creates `myop.config.json` with component metadata
48
+ 4. Creates `index.html` with a complete component template
49
+ 5. Automatically starts the dev server
50
+
51
+ **Files created (single-file mode):**
52
+ ```
53
+ ./index.html # Complete Myop component
54
+ ./myop.config.json # Component metadata
55
+ ```
56
+
57
+ **Files created (full project scaffold, when no package.json exists):**
58
+ ```
59
+ ./index.html
60
+ ./myop.config.json
61
+ ./package.json
62
+ ./build.js # esbuild bundler
63
+ ./src/index.js
64
+ ./src/modules/app.js
65
+ ./src/modules/myop.js
66
+ ./src/styles/index.css
67
+ ./src/styles/main.css
68
+ ./.gitignore
69
+ ```
70
+
71
+ **Important:** If `index.html` or `myop.config.json` already exists, the command exits with an error. Use `myop dev` for existing components.
72
+
73
+ ## Dev Command
74
+
75
+ Starts a development server with file watching.
76
+
77
+ ```bash
78
+ myop dev
79
+ ```
80
+
81
+ **Features:**
82
+ - Main server on **port 9292**
83
+ - Management dashboard on **port 9293**
84
+ - Watches `.js` and `.css` files for changes
85
+ - Auto-rebuilds when files change (if `build.js` or `package.json` build script exists)
86
+ - Hot Module Replacement for instant preview updates
87
+ - Single HTML file mode (no build step needed for `index.html`-only components)
88
+
89
+ **URLs during development:**
90
+ | URL | Purpose |
91
+ |-----|---------|
92
+ | `http://localhost:9292` | Dev server dashboard |
93
+ | `http://localhost:9292/view/<componentId>/` | Preview your component |
94
+
95
+ **Monorepo mode:**
96
+ ```bash
97
+ myop dev -m
98
+ ```
99
+ Scans for all `myop.config.json` files in nested directories and lets you select which components to run simultaneously.
100
+
101
+ ### Build Triggering
102
+
103
+ The dev server detects changes and triggers builds automatically:
104
+ - If `build.js` exists: runs `node build.js`
105
+ - If `package.json` has a `build` script: runs `npm run build`
106
+ - If only `index.html` exists (single-file mode): serves directly, no build needed
107
+
108
+ ## Push Command
109
+
110
+ Uploads the component directly to the Myop platform (no build step).
111
+
112
+ ```bash
113
+ myop push
114
+ ```
115
+
116
+ **What it does:**
117
+ 1. Reads `myop.config.json`
118
+ 2. Finds the HTML file to upload (`index.html` in root for single-file mode, or `dist/index.html`)
119
+ 3. Authenticates (prompts for login if needed)
120
+ 4. Uploads HTML to Myop via presigned URL
121
+ 5. Confirms upload
122
+ 6. Updates `myop.config.json` with `componentId` (first push only)
123
+
124
+ **After first push:**
125
+ - `myop.config.json` gets a real `componentId` (UUID)
126
+ - `organization` field is added
127
+ - Component appears on the dashboard
128
+
129
+ **Dashboard URL after push:**
130
+ ```
131
+ https://dashboard.myop.dev/dashboard/2.0/component/<componentId>
132
+ ```
133
+
134
+ ## Sync Command
135
+
136
+ Builds and uploads the component to the Myop platform (for multi-file projects with a build step).
137
+
138
+ ```bash
139
+ myop sync
140
+ ```
141
+
142
+ **What it does:**
143
+ 1. Reads `myop.config.json`
144
+ 2. Runs `npm run build`
145
+ 3. Verifies `dist/index.html` exists
146
+ 4. Authenticates (prompts for login if needed)
147
+ 5. Uploads `dist/index.html` to Myop via presigned URL
148
+ 6. Confirms upload
149
+ 7. Updates `myop.config.json` with `componentId` (first sync only)
150
+
151
+ ## Authentication
152
+
153
+ ### Login
154
+ ```bash
155
+ myop login
156
+ ```
157
+ Opens a browser window for OAuth authentication. Tokens are stored locally at `~/.myop/credentials.json`.
158
+
159
+ ### Logout
160
+ ```bash
161
+ myop logout
162
+ ```
163
+ Clears stored credentials.
164
+
165
+ ### Check Status
166
+ ```bash
167
+ myop whoami
168
+ ```
169
+ Displays the email of the currently authenticated user, or indicates if not logged in.
170
+
171
+ ### Token Storage
172
+ - Location: `~/.myop/credentials.json`
173
+ - Permissions: `0o600` (owner read/write only)
174
+ - Tokens refresh automatically when expired
175
+
176
+ ## Configuration
177
+
178
+ ### myop.config.json
179
+
180
+ | Field | Type | Description |
181
+ |-------|------|-------------|
182
+ | `name` | `string` | Component display name |
183
+ | `componentId` | `string` | `"DEV"` initially, UUID after first sync |
184
+ | `type` | `string` | Always `"html"` |
185
+ | `author` | `string` | `"@myop-cli"` by default |
186
+ | `HMR` | `boolean` | Hot Module Replacement enabled |
187
+ | `organization` | `string` | Organization ID (added after sync) |
188
+
189
+ ### Environment Variables
190
+
191
+ | Variable | Description |
192
+ |----------|-------------|
193
+ | `MYOP_MCP_URL` | MCP server URL (default: `https://mcp.myop.dev`) |
194
+
195
+ ### CI/CD Mode
196
+
197
+ ```bash
198
+ myop --ci
199
+ ```
200
+
201
+ Outputs JSON with version, config status, and auth status. No interactive prompts.
202
+
203
+ ```json
204
+ {
205
+ "version": "0.1.40",
206
+ "config": {
207
+ "found": true,
208
+ "path": "./myop.config.json",
209
+ "name": "My Component",
210
+ "componentId": "abc-123",
211
+ "organization": "org-456"
212
+ },
213
+ "auth": {
214
+ "loggedIn": true,
215
+ "email": "user@example.com"
216
+ }
217
+ }
218
+ ```