@rglabs/butterfly 2.0.1

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 (117) hide show
  1. package/CLAUDE.md +201 -0
  2. package/README.md +371 -0
  3. package/dist/commands/add.d.ts +23 -0
  4. package/dist/commands/add.js +303 -0
  5. package/dist/commands/code.d.ts +11 -0
  6. package/dist/commands/code.js +72 -0
  7. package/dist/commands/create-object.d.ts +6 -0
  8. package/dist/commands/create-object.js +293 -0
  9. package/dist/commands/create-report.d.ts +6 -0
  10. package/dist/commands/create-report.js +154 -0
  11. package/dist/commands/diff.d.ts +4 -0
  12. package/dist/commands/diff.js +238 -0
  13. package/dist/commands/download.d.ts +4 -0
  14. package/dist/commands/download.js +374 -0
  15. package/dist/commands/layout.d.ts +12 -0
  16. package/dist/commands/layout.js +83 -0
  17. package/dist/commands/record.d.ts +21 -0
  18. package/dist/commands/record.js +483 -0
  19. package/dist/commands/run-poc.d.ts +3 -0
  20. package/dist/commands/run-poc.js +18 -0
  21. package/dist/commands/setup.d.ts +3 -0
  22. package/dist/commands/setup.js +66 -0
  23. package/dist/commands/start-poc.d.ts +3 -0
  24. package/dist/commands/start-poc.js +55 -0
  25. package/dist/commands/sync-docs.d.ts +3 -0
  26. package/dist/commands/sync-docs.js +27 -0
  27. package/dist/commands/translate.d.ts +13 -0
  28. package/dist/commands/translate.js +401 -0
  29. package/dist/commands/upload.d.ts +3 -0
  30. package/dist/commands/upload.js +150 -0
  31. package/dist/commands/workflow-info.d.ts +13 -0
  32. package/dist/commands/workflow-info.js +161 -0
  33. package/dist/components/ConflictResolver.d.ts +12 -0
  34. package/dist/components/ConflictResolver.js +77 -0
  35. package/dist/components/DiffView.d.ts +11 -0
  36. package/dist/components/DiffView.js +101 -0
  37. package/dist/components/DownloadProgress.d.ts +11 -0
  38. package/dist/components/DownloadProgress.js +29 -0
  39. package/dist/components/RecordPreview.d.ts +11 -0
  40. package/dist/components/RecordPreview.js +91 -0
  41. package/dist/components/SetupForm.d.ts +8 -0
  42. package/dist/components/SetupForm.js +56 -0
  43. package/dist/components/UploadProgress.d.ts +13 -0
  44. package/dist/components/UploadProgress.js +42 -0
  45. package/dist/diff/adapters/index.d.ts +8 -0
  46. package/dist/diff/adapters/index.js +18 -0
  47. package/dist/diff/adapters/objectsAdapter.d.ts +13 -0
  48. package/dist/diff/adapters/objectsAdapter.js +177 -0
  49. package/dist/diff/adapters/reportsAdapter.d.ts +14 -0
  50. package/dist/diff/adapters/reportsAdapter.js +212 -0
  51. package/dist/diff/adapters/types.d.ts +19 -0
  52. package/dist/diff/adapters/types.js +2 -0
  53. package/dist/diff/engine.d.ts +19 -0
  54. package/dist/diff/engine.js +57 -0
  55. package/dist/diff/types.d.ts +34 -0
  56. package/dist/diff/types.js +110 -0
  57. package/dist/index.d.ts +3 -0
  58. package/dist/index.js +117 -0
  59. package/dist/types/index.d.ts +18 -0
  60. package/dist/types/index.js +2 -0
  61. package/dist/utils/api.d.ts +85 -0
  62. package/dist/utils/api.js +1031 -0
  63. package/dist/utils/auth.d.ts +4 -0
  64. package/dist/utils/auth.js +22 -0
  65. package/dist/utils/bfySplitter.d.ts +12 -0
  66. package/dist/utils/bfySplitter.js +151 -0
  67. package/dist/utils/docs.d.ts +16 -0
  68. package/dist/utils/docs.js +186 -0
  69. package/dist/utils/errorLogger.d.ts +6 -0
  70. package/dist/utils/errorLogger.js +29 -0
  71. package/dist/utils/files.d.ts +14 -0
  72. package/dist/utils/files.js +772 -0
  73. package/dist/utils/lockManager.d.ts +15 -0
  74. package/dist/utils/lockManager.js +126 -0
  75. package/dist/utils/resourceHandlers.d.ts +50 -0
  76. package/dist/utils/resourceHandlers.js +684 -0
  77. package/dist/utils/resourceMapping.d.ts +32 -0
  78. package/dist/utils/resourceMapping.js +210 -0
  79. package/dist/utils/singleResourceDownload.d.ts +14 -0
  80. package/dist/utils/singleResourceDownload.js +261 -0
  81. package/dist/utils/summaryGenerator.d.ts +2 -0
  82. package/dist/utils/summaryGenerator.js +183 -0
  83. package/dist/utils/uploadHandler.d.ts +31 -0
  84. package/dist/utils/uploadHandler.js +263 -0
  85. package/docs/AI_API.md +93 -0
  86. package/docs/CLAUDE.md +216 -0
  87. package/docs/PROJECT_SPECIFIC.md +1 -0
  88. package/docs/RECORD_COMMAND.md +262 -0
  89. package/docs/WORKFLOW_API.md +480 -0
  90. package/docs/bfy-splitting.md +126 -0
  91. package/docs/cli-commands.md +333 -0
  92. package/docs/examples/README.md +95 -0
  93. package/docs/examples/order-system.md +147 -0
  94. package/docs/examples/product-catalog.md +195 -0
  95. package/docs/examples/reports.md +187 -0
  96. package/docs/excel-export.md +216 -0
  97. package/docs/field-types/README.md +29 -0
  98. package/docs/field-types/calculated.md +147 -0
  99. package/docs/field-types/code-mappings.md +84 -0
  100. package/docs/field-types/custom.md +340 -0
  101. package/docs/object-specs/README.md +136 -0
  102. package/docs/object-specs/code-parameters.md +151 -0
  103. package/docs/object-specs/creating.md +203 -0
  104. package/docs/object-specs/js-code-examples.md +208 -0
  105. package/docs/object-specs/js-field-updates.md +168 -0
  106. package/docs/objects/README.md +89 -0
  107. package/docs/objects/creating.md +127 -0
  108. package/docs/page-layout.md +361 -0
  109. package/docs/permissions.md +260 -0
  110. package/docs/reports.md +197 -0
  111. package/docs/state-machines.md +544 -0
  112. package/docs/tasks/create-object.md +81 -0
  113. package/docs/translations.md +346 -0
  114. package/docs/twig-helpers.md +283 -0
  115. package/docs/webservices.md +159 -0
  116. package/docs/workspaces.md +176 -0
  117. package/package.json +59 -0
@@ -0,0 +1,159 @@
1
+ # Webservices Guide
2
+
3
+ Webservices are API endpoints created using the `webservices` table. They provide a dedicated way to build REST APIs and webhooks.
4
+
5
+ > **Note:** Use the `webservices` table for creating API endpoints. Do NOT confuse this with `cms_reports` - they are separate tables for different purposes.
6
+
7
+ ## Table Fields
8
+
9
+ | Field | Type | Description |
10
+ |-------|------|-------------|
11
+ | `custom_seo` | string | URL path for the webservice endpoint |
12
+ | `method` | from_list | HTTP method: `GET`, `POST`, `ALL`, etc. |
13
+ | `page_code` | code | Twig code that handles the request |
14
+
15
+ ## URL Format
16
+
17
+ ```
18
+ {BASE_URL}/{custom_seo}
19
+ ```
20
+
21
+ | `custom_seo` value | Resulting URL |
22
+ |--------------------|---------------|
23
+ | `api/users/list` | `https://example.com/api/users/list` |
24
+ | `webhook/process` | `https://example.com/webhook/process` |
25
+ | `hello-world` | `https://example.com/hello-world` |
26
+
27
+ > **Important:** The `custom_seo` field maps **directly** to the URL path. No prefixes are added automatically.
28
+
29
+ > **AI INSTRUCTION:** Do NOT invent or add any URL prefixes (such as `/ws/`, `/api/`, `/v1/`, etc.) to the `custom_seo` value unless the user explicitly requests it. Use exactly what the user specifies, or choose a simple descriptive path like `webhook/github` or `users/list`.
30
+
31
+ ## Creating a Webservice
32
+
33
+ ### Basic Example
34
+
35
+ ```bash
36
+ butterfly-cli record add webservices --data '{
37
+ "custom_seo": "api/hello",
38
+ "method": "GET",
39
+ "page_code": "{{ {\"message\": \"Hello World\"}|json_encode|raw }}"
40
+ }'
41
+ ```
42
+
43
+ ### JSON API Response
44
+
45
+ ```bash
46
+ butterfly-cli record add webservices --data '{
47
+ "custom_seo": "api/users/list",
48
+ "method": "GET",
49
+ "page_code": "{% set users = db(\"default\", false).table(\"users\").select(\"id\", \"name\", \"email\").get() %}{{ users|json_encode|raw }}"
50
+ }'
51
+ ```
52
+
53
+ ### POST Endpoint with Parameters
54
+
55
+ ```bash
56
+ butterfly-cli record add webservices --data '{
57
+ "custom_seo": "api/contact/submit",
58
+ "method": "POST",
59
+ "page_code": "{% set response = crud(\"default\", false).table(\"contacts\").insert({\"name\": getParameter(\"name\"), \"email\": getParameter(\"email\"), \"message\": getParameter(\"message\")}) %}{{ response|json_encode|raw }}"
60
+ }'
61
+ ```
62
+
63
+ ## Page Code Examples
64
+
65
+ ### Read Data (No Auth)
66
+
67
+ For webservices that don't require authentication, use `db('default', false)`:
68
+
69
+ ```twig
70
+ {% set records = db('default', false)
71
+ .table('products')
72
+ .where('is_active', 1)
73
+ .get()
74
+ %}
75
+ {{ records|json_encode|raw }}
76
+ ```
77
+
78
+ ### Write Data (No Auth)
79
+
80
+ For CRUD operations without authentication, use `crud('default', false)`:
81
+
82
+ ```twig
83
+ {% set response = crud('default', false)
84
+ .table('submissions')
85
+ .insert({
86
+ "name": getParameter('name'),
87
+ "email": getParameter('email')
88
+ })
89
+ %}
90
+ {{ response|json_encode|raw }}
91
+ ```
92
+
93
+ ### Get Request Parameters
94
+
95
+ ```twig
96
+ {# GET or POST parameters #}
97
+ {% set userId = getParameter('user_id') %}
98
+ {% set action = getParameter('action') %}
99
+
100
+ {# With default value #}
101
+ {% set page = getParameter('page') ?: 1 %}
102
+ ```
103
+
104
+ ### Error Handling
105
+
106
+ ```twig
107
+ {% set id = getParameter('id') %}
108
+
109
+ {% if not id %}
110
+ {{ {"error": "ID is required"}|json_encode|raw }}
111
+ {% else %}
112
+ {% set record = db('default', false).table('items').where('id', id).first() %}
113
+ {% if record %}
114
+ {{ record|json_encode|raw }}
115
+ {% else %}
116
+ {{ {"error": "Record not found"}|json_encode|raw }}
117
+ {% endif %}
118
+ {% endif %}
119
+ ```
120
+
121
+ ### Set Response Headers
122
+
123
+ ```twig
124
+ {% do header('Content-Type: application/json') %}
125
+ {% do header('Access-Control-Allow-Origin: *') %}
126
+ {{ data|json_encode|raw }}
127
+ ```
128
+
129
+ ## File Structure
130
+
131
+ Downloaded webservices are stored in:
132
+
133
+ ```
134
+ butterfly-resources/
135
+ └── webservices/
136
+ └── [custom_seo]/
137
+ ├── webservice.json # Webservice definition
138
+ └── page_code.bfy # Twig code (if exists)
139
+ ```
140
+
141
+ ## Webhook Example
142
+
143
+ Create a webhook endpoint for external services:
144
+
145
+ ```bash
146
+ butterfly-cli record add webservices --data '{
147
+ "custom_seo": "webhook/github",
148
+ "method": "POST",
149
+ "page_code": "{% set payload = _POST %}{% set response = crud(\"default\", false).table(\"github_events\").insert({\"event_type\": payload.action, \"payload\": payload|json_encode, \"created_at\": \"now\"|date(\"Y-m-d H:i:s\")}) %}{{ {\"success\": response.success}|json_encode|raw }}"
150
+ }'
151
+ ```
152
+
153
+ ## Best Practices
154
+
155
+ 1. **Always use `db('default', false)` and `crud('default', false)`** for public APIs to disable permission checks
156
+ 2. **Validate input parameters** before processing
157
+ 3. **Return proper JSON responses** with appropriate status information
158
+ 4. **Set Content-Type header** for JSON responses
159
+ 5. **Handle errors gracefully** and return meaningful error messages
@@ -0,0 +1,176 @@
1
+ # Workspaces (bfy_workspaces)
2
+
3
+ Workspaces provide multi-tenant data isolation in Butterfly. Objects can be scoped to workspaces at two levels.
4
+
5
+ ## Workspace Scope Modes
6
+
7
+ | `is_workspace_specific` | Scope | `bfy_workspace_id` Location | Use Case |
8
+ |-------------------------|-------|----------------------------|----------|
9
+ | `0` (default) | Object-level | `objects` table | Object visible only in one workspace |
10
+ | `1` | Record-level | Data table itself | Records filterable by workspace |
11
+
12
+ ## Object-Level Scope (`is_workspace_specific: 0`)
13
+
14
+ The **entire object** belongs to a single workspace. The `bfy_workspace_id` is stored on the `objects` table.
15
+
16
+ - Object appears in admin menu only for users in that workspace
17
+ - All records of this object are implicitly in that workspace
18
+ - Data table does NOT have `bfy_workspace_id` column
19
+
20
+ ### Assign Object to Workspace
21
+
22
+ ```bash
23
+ butterfly-cli record edit objects --id <object_id> --data '{"bfy_workspace_id": <workspace_id>}'
24
+ ```
25
+
26
+ ### Query Objects by Workspace
27
+
28
+ ```twig
29
+ {% set objects = db().table('objects').where('bfy_workspace_id', <workspace_id>).get() %}
30
+ ```
31
+
32
+ ## Record-Level Scope (`is_workspace_specific: 1`)
33
+
34
+ **Each record** can belong to a different workspace. The `bfy_workspace_id` column exists on the data table itself.
35
+
36
+ - Object visible in all workspaces (or controlled separately)
37
+ - Records filtered by `bfy_workspace_id` automatically in admin
38
+ - Useful for shared objects with workspace-specific data
39
+
40
+ ### Requirements
41
+
42
+ The data table must have a `bfy_workspace_id` column:
43
+
44
+ | Column | Type | Description |
45
+ |--------|------|-------------|
46
+ | `bfy_workspace_id` | INT | Foreign key to `bfy_workspaces.id` |
47
+
48
+ ### Enable Record-Level Tracking
49
+
50
+ ```bash
51
+ butterfly-cli record edit objects --id <object_id> --data '{"is_workspace_specific": 1}'
52
+ ```
53
+
54
+ ### Add Record with Workspace
55
+
56
+ ```bash
57
+ butterfly-cli record add <table_name> --data '{"bfy_workspace_id": <workspace_id>, "field": "value"}'
58
+ ```
59
+
60
+ ### Update Record Workspace
61
+
62
+ ```bash
63
+ butterfly-cli record edit <table_name> --id <record_id> --data '{"bfy_workspace_id": <workspace_id>}'
64
+ ```
65
+
66
+ ### Query Records by Workspace
67
+
68
+ ```twig
69
+ {% set records = db().table('<table_name>').where('bfy_workspace_id', <workspace_id>).get() %}
70
+ ```
71
+
72
+ ## Detect Workspace Mode
73
+
74
+ ```bash
75
+ butterfly-cli record get objects --column table_name --value <table_name>
76
+ ```
77
+
78
+ Check `is_workspace_specific` in response:
79
+ - `0` or `null` = Object-level scope
80
+ - `1` = Record-level scope
81
+
82
+ ## Decision Guide
83
+
84
+ | Scenario | Mode | `is_workspace_specific` |
85
+ |----------|------|------------------------|
86
+ | Object only exists in one workspace | Object-level | `0` |
87
+ | Object shared, records separated by workspace | Record-level | `1` |
88
+ | Core system object (all workspaces) | None | `bfy_workspace_id: null` |
89
+
90
+ ## Twig Helpers
91
+
92
+ ```twig
93
+ {# Get current user's workspace #}
94
+ {% set workspace_id = currentUser('bfy_workspace_id') %}
95
+
96
+ {# Filter records by current workspace #}
97
+ {% set records = db().table('orders').where('bfy_workspace_id', currentUser('bfy_workspace_id')).get() %}
98
+ ```
99
+
100
+ ## Migrating Objects to a New Workspace
101
+
102
+ When moving an object to a different workspace, several related resources must be updated.
103
+
104
+ ### Migration Checklist
105
+
106
+ | Resource | Table | Action |
107
+ |----------|-------|--------|
108
+ | Object itself | `objects` | Update `bfy_workspace_id` |
109
+ | Admin menus | `cms_admin_menus` | Update `bfy_workspace_id` |
110
+ | State machines | `bfy_state_machines` | Update `bfy_workspace_id` |
111
+
112
+ ### Step 1: Update the Object
113
+
114
+ ```bash
115
+ butterfly-cli record edit objects --id <object_id> --data '{"bfy_workspace_id": <new_workspace_id>}'
116
+ ```
117
+
118
+ ### Step 2: Update Admin Menus
119
+
120
+ Objects have associated menu entries that must also be migrated:
121
+
122
+ ```bash
123
+ # Get the object to find menu IDs
124
+ butterfly-cli record get objects --id <object_id>
125
+
126
+ # Update menu entries (main_menu_id, sub_menu_id, menu_id from object)
127
+ butterfly-cli record edit cms_admin_menus --id <main_menu_id> --data '{"bfy_workspace_id": <new_workspace_id>}'
128
+ butterfly-cli record edit cms_admin_menus --id <sub_menu_id> --data '{"bfy_workspace_id": <new_workspace_id>}'
129
+ butterfly-cli record edit cms_admin_menus --id <menu_id> --data '{"bfy_workspace_id": <new_workspace_id>}'
130
+ ```
131
+
132
+ ### Step 3: Find and Update Related State Machines
133
+
134
+ Objects with state management have `state` field type specs linked to state machines:
135
+
136
+ ```bash
137
+ # Find state field specs for the object
138
+ butterfly-cli record get object_specs --column objects_id --value <object_id>
139
+ ```
140
+
141
+ Look for specs with `type: "state"`. The `val_1` parameter contains the state machine ID.
142
+
143
+ ```bash
144
+ # Update the state machine workspace
145
+ butterfly-cli record edit bfy_state_machines --id <state_machine_id> --data '{"bfy_workspace_id": <new_workspace_id>}'
146
+ ```
147
+
148
+ ### Step 4: Check for Non-Trivial Dependencies
149
+
150
+ Before completing migration, verify these potential dependencies:
151
+
152
+ | Dependency | How to Check | Action Required |
153
+ |------------|--------------|-----------------|
154
+ | Reports using this object | Check `cms_reports` with queries referencing the table | May need workspace update |
155
+ | Workflows referencing the object | Check `bfy_workflows` | May need workspace update |
156
+ | Other objects with relations | Check `object_specs` with `from_list` pointing to this table | User decision required |
157
+ | Cronjobs processing this object | Check `bfy_cronjobs` | May need workspace update |
158
+
159
+ > **AI INSTRUCTION:** When migrating objects, always ask the user about non-trivial dependencies like reports, workflows, and related objects before proceeding. These may need to stay in the original workspace or be migrated together.
160
+
161
+ ### Migration Query Helper
162
+
163
+ To find all state machines linked to an object:
164
+
165
+ ```twig
166
+ {% set state_specs = db()
167
+ .table('object_specs')
168
+ .where('objects_id', <object_id>)
169
+ .where('type', 'state')
170
+ .get()
171
+ %}
172
+
173
+ {% for spec in state_specs %}
174
+ State Machine ID: {{ spec.val_1 }}
175
+ {% endfor %}
176
+ ```
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "@rglabs/butterfly",
3
+ "version": "2.0.1",
4
+ "description": "CLI tool to download resources from the Butterfly platform",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "butterfly-cli": "./dist/index.js",
9
+ "butterfly": "./dist/index.js"
10
+ },
11
+ "scripts": {
12
+ "dev": "tsx src/index.ts",
13
+ "build": "tsc",
14
+ "start": "node dist/index.js",
15
+ "lint": "eslint src/**/*.ts",
16
+ "typecheck": "tsc --noEmit",
17
+ "prepublishOnly": "npm run build",
18
+ "postinstall": "node -e \"console.log('Butterfly CLI installed successfully!')\""
19
+ },
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "keywords": [
24
+ "butterfly",
25
+ "cli",
26
+ "npx"
27
+ ],
28
+ "author": "",
29
+ "license": "ISC",
30
+ "engines": {
31
+ "node": ">=16.0.0"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^24.1.0",
35
+ "@types/react": "^19.1.8",
36
+ "@typescript-eslint/eslint-plugin": "^8.56.0",
37
+ "@typescript-eslint/parser": "^8.56.0",
38
+ "eslint": "^10.0.1",
39
+ "tsx": "^4.20.3",
40
+ "typescript": "^5.8.3"
41
+ },
42
+ "dependencies": {
43
+ "@types/chokidar": "^2.1.7",
44
+ "axios": "^1.10.0",
45
+ "axios-cookiejar-support": "^6.0.4",
46
+ "chokidar": "^4.0.3",
47
+ "commander": "^14.0.0",
48
+ "dotenv": "^17.2.0",
49
+ "ink": "^6.0.1",
50
+ "ink-spinner": "^5.0.0",
51
+ "ink-text-input": "^6.0.0",
52
+ "react": "^19.1.0",
53
+ "tough-cookie": "^5.1.2",
54
+ "yaml": "^2.8.1"
55
+ },
56
+ "overrides": {
57
+ "minimatch": "^10.2.1"
58
+ }
59
+ }