@lowdefy/codemods 5.6.0 → 6.1.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.
package/package.json
CHANGED
package/registry.json
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"versions": [
|
|
3
|
+
{
|
|
4
|
+
"version": "6.0.0",
|
|
5
|
+
"from": ">=5.0.0 <6.0.0",
|
|
6
|
+
"description": "Vite + Hono server migration (plugin ESM exports, Auth.js imports, provider-neutral file blocks)",
|
|
7
|
+
"codemods": [
|
|
8
|
+
{
|
|
9
|
+
"id": "migrate-plugin-packages",
|
|
10
|
+
"description": "Convert custom plugin packages for the unbundled Node ESM server: exports subpaths must resolve to files (fixes ERR_UNSUPPORTED_DIR_IMPORT), next-auth imports move to @auth/core, next/* imports and NEXT_PUBLIC_* reads are removed.",
|
|
11
|
+
"path": "v6-0-0/01-migrate-plugin-packages.md"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"id": "s3-blocks-to-file-blocks",
|
|
15
|
+
"description": "Optional: rename the deprecated S3UploadButton/S3UploadPhoto/S3UploadDragger/S3Download blocks to the provider-neutral Upload/UploadPhoto/UploadDragger/Download blocks, and s3PostPolicyRequestId/s3GetPolicyRequestId to uploadPolicyRequestId/downloadPolicyRequestId (also on TiptapInput, TiptapMentionInput, and AgentChat attachments). The aliases keep working; this silences deprecation warnings.",
|
|
16
|
+
"path": "v6-0-0/02-s3-blocks-to-file-blocks.md"
|
|
17
|
+
}
|
|
18
|
+
]
|
|
19
|
+
},
|
|
3
20
|
{
|
|
4
21
|
"version": "5.5.0",
|
|
5
22
|
"from": ">=5.1.0 <5.5.0",
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# Migration: Plugin Packages for the Hono + Vite Server (ESM Exports, Auth.js Imports)
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
In Lowdefy v6 the server no longer runs on Next.js. The production server is a Hono app running as **plain, unbundled Node.js ESM**, and the client is bundled by Vite. This changes how plugin packages are loaded:
|
|
6
|
+
|
|
7
|
+
- **Server-side plugin files** (`connections`, `agents`, `operators/server`, `auth/*`) are imported by Node.js directly from the generated `build/plugins/*.js` files. Node's ESM resolver is **stricter than a bundler**: it does not try `.js` completion and does not resolve `index.js` inside directories.
|
|
8
|
+
- **Client-side plugin files** (`blocks`, `actions`, `operators/client`, `metas`, `icons`, plus any CSS the blocks import) are bundled by Vite, which resolves package `exports` the standard way.
|
|
9
|
+
|
|
10
|
+
A plugin that worked in v5 can crash the v6 server at startup with:
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
Error [ERR_UNSUPPORTED_DIR_IMPORT]: Directory import '.../node_modules/<plugin>/dist/connections'
|
|
14
|
+
is not supported resolving ES modules imported from .../build/plugins/connections.js
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
This happens when the plugin's `exports` map resolves a subpath to a **directory** instead of a file. A wildcard like `"./*": "./dist/*"` substitutes literally — the specifier `<plugin>/connections` maps to `dist/connections`, and if that is a directory (with the real barrel at `dist/connections.js` or `dist/connections/index.js`), Node refuses it. Bundlers silently completed this to a file, which is why v5 never surfaced the problem.
|
|
18
|
+
|
|
19
|
+
Additionally, **auth plugins can no longer import from `next-auth`**. The v6 auth engine is Auth.js (`@auth/core`). The `next-auth` package is gone from the server, and although a plugin that ships its own `next-auth` dependency may still load, it should migrate to `@auth/core` imports, which are clean ESM (no `.default` CJS workaround).
|
|
20
|
+
|
|
21
|
+
This migration targets **plugin package source and `package.json`** (local plugins and published plugin packages), not YAML configs.
|
|
22
|
+
|
|
23
|
+
## Scope
|
|
24
|
+
|
|
25
|
+
`plugins` — scan `package.json` and source files of custom plugin packages (workspace plugins, `git:`/`link:` plugins, and your published plugin packages).
|
|
26
|
+
|
|
27
|
+
## What to Do
|
|
28
|
+
|
|
29
|
+
### 1. Make every Lowdefy entry-point subpath in `exports` resolve to a file
|
|
30
|
+
|
|
31
|
+
List the subpaths Lowdefy generates imports for, based on what the plugin provides:
|
|
32
|
+
|
|
33
|
+
| Plugin provides | Imported subpath |
|
|
34
|
+
| --- | --- |
|
|
35
|
+
| Connections/requests | `<plugin>/connections` |
|
|
36
|
+
| Blocks | `<plugin>/blocks`, `<plugin>/metas` |
|
|
37
|
+
| Actions | `<plugin>/actions` |
|
|
38
|
+
| Operators | `<plugin>/operators/client`, `<plugin>/operators/server` |
|
|
39
|
+
| Auth providers/adapters/callbacks/events | `<plugin>/auth/providers`, `<plugin>/auth/adapters`, `<plugin>/auth/callbacks`, `<plugin>/auth/events` |
|
|
40
|
+
| Agents | `<plugin>/agents` |
|
|
41
|
+
| All plugins | `<plugin>/types` (and optionally `<plugin>/schemas`) |
|
|
42
|
+
|
|
43
|
+
For each of these that the plugin uses, add an **explicit file entry** to `exports`. Explicit entries take precedence over wildcards, so the wildcard can stay for other paths:
|
|
44
|
+
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"exports": {
|
|
48
|
+
"./connections": "./dist/connections.js",
|
|
49
|
+
"./types": "./dist/types.js",
|
|
50
|
+
"./*": "./dist/*"
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Do **not** rely on `"./*": "./dist/*"` alone for any of the entry-point subpaths above — it maps the specifier to a directory whenever a folder with the same name exists next to the barrel file.
|
|
56
|
+
|
|
57
|
+
If the barrel file does not exist (e.g. only `dist/connections/MyConnection.js` exists), create a barrel `src/connections.js` that re-exports each type and rebuild, then point the export at it.
|
|
58
|
+
|
|
59
|
+
### 2. Replace `next-auth` imports in auth plugins with `@auth/core`
|
|
60
|
+
|
|
61
|
+
For auth provider plugins:
|
|
62
|
+
|
|
63
|
+
```javascript
|
|
64
|
+
// Before (v4/v5)
|
|
65
|
+
import _googleProvider from 'next-auth/providers/google';
|
|
66
|
+
const GoogleProvider = _googleProvider.default; // CJS workaround
|
|
67
|
+
|
|
68
|
+
// After (v6)
|
|
69
|
+
import GoogleProvider from '@auth/core/providers/google';
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
All provider module ids are unchanged (`@auth/core/providers/<id>` matches the old `next-auth/providers/<id>`), and the `.default` CJS workaround is no longer needed — `@auth/core` is native ESM.
|
|
73
|
+
|
|
74
|
+
For custom OAuth/OIDC provider objects, the v4 shape still works; for OpenID Connect providers prefer the v5 `type: 'oidc'` (discovery and ID token handling are built in, so `idToken: true` is redundant).
|
|
75
|
+
|
|
76
|
+
For adapter plugins wrapping the MongoDB adapter:
|
|
77
|
+
|
|
78
|
+
```javascript
|
|
79
|
+
// Before
|
|
80
|
+
import { MongoDBAdapter } from '@next-auth/mongodb-adapter';
|
|
81
|
+
|
|
82
|
+
// After — same call shape
|
|
83
|
+
import { MongoDBAdapter } from '@auth/mongodb-adapter';
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Update `package.json`: remove `next` and `next-auth` from dependencies/peerDependencies; add `@auth/core` (and `@auth/mongodb-adapter` if used).
|
|
87
|
+
|
|
88
|
+
Plugins that keep their own `next-auth@4` dependency for provider factories (e.g. a custom email provider wrapping `next-auth/providers/email`) continue to work — the v4 provider object shape is compatible with the Auth.js engine — but migrating removes a duplicate auth engine from the install.
|
|
89
|
+
|
|
90
|
+
### 3. Remove any `next/*` imports and `NEXT_PUBLIC_*` env reads
|
|
91
|
+
|
|
92
|
+
Client plugin code is bundled by Vite. `next/head`, `next/link`, `next/router`, `next/dynamic` no longer exist — blocks receive `Components.Head`, `Components.Link` and `router` through the framework adapter props as before, so most blocks need no change. `process.env.NEXT_PUBLIC_*` variables are not defined under Vite; only `process.env.NODE_ENV` is replaced.
|
|
93
|
+
|
|
94
|
+
### 4. Prefer ESM dists
|
|
95
|
+
|
|
96
|
+
Server-side plugin files load as Node ESM. CJS dists still load through Node's interop, but explicit file exports are required either way. If the plugin's build outputs CJS, consider adding `"type": "module"` and building ESM output — this matches all `@lowdefy/*` plugin packages.
|
|
97
|
+
|
|
98
|
+
## Files to Check
|
|
99
|
+
|
|
100
|
+
- `package.json` — `exports` map, `type`, dependencies (`next`, `next-auth`, `@next-auth/mongodb-adapter`)
|
|
101
|
+
- `src/auth/**` — `next-auth` imports
|
|
102
|
+
- `src/**` — `next/*` imports, `NEXT_PUBLIC_*` reads
|
|
103
|
+
- Build output (`dist/`) — confirm the barrel files the exports point at actually exist
|
|
104
|
+
|
|
105
|
+
## Examples
|
|
106
|
+
|
|
107
|
+
### Before — plugin `package.json` (crashes the v6 server)
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"name": "@my-org/plugin-local",
|
|
112
|
+
"type": "module",
|
|
113
|
+
"exports": {
|
|
114
|
+
"./*": "./dist/*"
|
|
115
|
+
},
|
|
116
|
+
"peerDependencies": {
|
|
117
|
+
"next-auth": ">=4.24"
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### After — plugin `package.json`
|
|
123
|
+
|
|
124
|
+
```json
|
|
125
|
+
{
|
|
126
|
+
"name": "@my-org/plugin-local",
|
|
127
|
+
"type": "module",
|
|
128
|
+
"exports": {
|
|
129
|
+
"./connections": "./dist/connections.js",
|
|
130
|
+
"./types": "./dist/types.js",
|
|
131
|
+
"./*": "./dist/*"
|
|
132
|
+
},
|
|
133
|
+
"dependencies": {
|
|
134
|
+
"@auth/core": "0.41.3"
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Edge Cases
|
|
140
|
+
|
|
141
|
+
- **`<plugin>/schemas` and `<plugin>/metas`**: the build imports these inside `try/catch` — a missing `schemas` export degrades gracefully (no schema validation for that plugin), but a wrong one that resolves to a directory still warns. Point them at files or omit them.
|
|
142
|
+
- **Deep wildcard exports** (`"./connections/*": "./dist/connections/*"`) for per-file imports can coexist with the explicit barrel entry.
|
|
143
|
+
- **Published plugins**: a new version must be published; `lowdefy build` installs from the registry. For local testing, use a `link:`/`file:` plugin reference.
|
|
144
|
+
|
|
145
|
+
## Verification
|
|
146
|
+
|
|
147
|
+
1. Resolve every generated plugin import the way Node will. From the built server directory (`<config-directory>/.lowdefy/server`):
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
node --input-type=module -e "
|
|
151
|
+
import fs from 'node:fs';
|
|
152
|
+
import { fileURLToPath } from 'node:url';
|
|
153
|
+
const files = ['build/plugins/connections.js','build/plugins/agents.js','build/plugins/operators/server.js','build/plugins/auth/adapters.js','build/plugins/auth/callbacks.js','build/plugins/auth/events.js','build/plugins/auth/providers.js'];
|
|
154
|
+
let bad = 0;
|
|
155
|
+
for (const f of files) {
|
|
156
|
+
if (!fs.existsSync(f)) continue;
|
|
157
|
+
for (const m of fs.readFileSync(f, 'utf8').matchAll(/from '([^']+)'/g)) {
|
|
158
|
+
try {
|
|
159
|
+
const p = fileURLToPath(import.meta.resolve(m[1], new URL('build/plugins/x.js', import.meta.url).href));
|
|
160
|
+
const stat = fs.statSync(p, { throwIfNoEntry: false });
|
|
161
|
+
if (!stat || stat.isDirectory()) { console.log('BAD:', m[1], '→', p); bad++; }
|
|
162
|
+
} catch (e) { console.log('BAD:', m[1], e.code); bad++; }
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
console.log(bad === 0 ? 'all server plugin imports resolve to files' : bad + ' failing imports');
|
|
166
|
+
"
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
Note: a plain `import.meta.resolve` check is not enough — it does not touch the filesystem, so the `stat` check is what catches directory resolutions.
|
|
170
|
+
|
|
171
|
+
2. `lowdefy build && lowdefy start` — the server must boot without `ERR_UNSUPPORTED_DIR_IMPORT` or `ERR_MODULE_NOT_FOUND`.
|
|
172
|
+
|
|
173
|
+
3. For auth plugins: complete a sign-in flow (the providers list at `/api/auth/providers` must include the plugin's provider ids).
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# Migration: S3 Blocks → Provider-Neutral File Blocks
|
|
2
|
+
|
|
3
|
+
## Context
|
|
4
|
+
|
|
5
|
+
The S3-specific file blocks are deprecated in favour of provider-neutral blocks in
|
|
6
|
+
`@lowdefy/blocks-files` that work with any storage connection (AWS S3 and S3-compatible
|
|
7
|
+
services, Google Cloud Storage, Azure Blob Storage):
|
|
8
|
+
|
|
9
|
+
- `S3UploadButton` → `Upload`
|
|
10
|
+
- `S3UploadPhoto` → `UploadPhoto`
|
|
11
|
+
- `S3UploadDragger` → `UploadDragger`
|
|
12
|
+
- `S3Download` → `Download`
|
|
13
|
+
|
|
14
|
+
The request-id properties are renamed with them:
|
|
15
|
+
|
|
16
|
+
- `s3PostPolicyRequestId` → `uploadPolicyRequestId` (upload blocks, TiptapInput,
|
|
17
|
+
TiptapMentionInput, AgentChat `sender.attachments`)
|
|
18
|
+
- `s3GetPolicyRequestId` → `downloadPolicyRequestId` (Download block)
|
|
19
|
+
|
|
20
|
+
**This migration is optional.** The S3\* block names and legacy properties keep working as
|
|
21
|
+
deprecated aliases — they log a console deprecation warning and will be removed in a future
|
|
22
|
+
major. Migrating now silences the warnings and drops the misleading S3 naming.
|
|
23
|
+
|
|
24
|
+
The requests behind the blocks (`AwsS3PresignedPostPolicy`, `AwsS3PresignedGetObject`) are
|
|
25
|
+
NOT renamed — only the block types and their request-id properties change.
|
|
26
|
+
|
|
27
|
+
## What to Do
|
|
28
|
+
|
|
29
|
+
| Old | New |
|
|
30
|
+
| ------------------------------------------------ | -------------------------------------------- |
|
|
31
|
+
| `type: S3UploadButton` | `type: Upload` |
|
|
32
|
+
| `type: S3UploadPhoto` | `type: UploadPhoto` |
|
|
33
|
+
| `type: S3UploadDragger` | `type: UploadDragger` |
|
|
34
|
+
| `type: S3Download` | `type: Download` |
|
|
35
|
+
| `properties.s3PostPolicyRequestId` | `properties.uploadPolicyRequestId` |
|
|
36
|
+
| `properties.s3GetPolicyRequestId` | `properties.downloadPolicyRequestId` |
|
|
37
|
+
| `sender.attachments.s3PostPolicyRequestId` (AgentChat) | `sender.attachments.uploadPolicyRequestId` |
|
|
38
|
+
|
|
39
|
+
Rename the block `type:` and its request-id property together in one pass — the generic
|
|
40
|
+
blocks do not read the legacy property names (only the aliases map them).
|
|
41
|
+
|
|
42
|
+
`TiptapInput` and `TiptapMentionInput` keep their block type; only rename their
|
|
43
|
+
`s3PostPolicyRequestId` property.
|
|
44
|
+
|
|
45
|
+
## Files to Check
|
|
46
|
+
|
|
47
|
+
Glob: `**/*.{yaml,yml}`
|
|
48
|
+
Grep: `S3UploadButton|S3UploadPhoto|S3UploadDragger|S3Download|s3PostPolicyRequestId|s3GetPolicyRequestId`
|
|
49
|
+
|
|
50
|
+
## Examples
|
|
51
|
+
|
|
52
|
+
### Before — upload button
|
|
53
|
+
|
|
54
|
+
```yaml
|
|
55
|
+
- id: file_upload
|
|
56
|
+
type: S3UploadButton
|
|
57
|
+
properties:
|
|
58
|
+
s3PostPolicyRequestId: upload_policy
|
|
59
|
+
accept: '.pdf'
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### After
|
|
63
|
+
|
|
64
|
+
```yaml
|
|
65
|
+
- id: file_upload
|
|
66
|
+
type: Upload
|
|
67
|
+
properties:
|
|
68
|
+
uploadPolicyRequestId: upload_policy
|
|
69
|
+
accept: '.pdf'
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Before — download list
|
|
73
|
+
|
|
74
|
+
```yaml
|
|
75
|
+
- id: file_list
|
|
76
|
+
type: S3Download
|
|
77
|
+
properties:
|
|
78
|
+
s3GetPolicyRequestId: download_policy
|
|
79
|
+
fileList:
|
|
80
|
+
_state: uploaded.fileList
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### After
|
|
84
|
+
|
|
85
|
+
```yaml
|
|
86
|
+
- id: file_list
|
|
87
|
+
type: Download
|
|
88
|
+
properties:
|
|
89
|
+
downloadPolicyRequestId: download_policy
|
|
90
|
+
fileList:
|
|
91
|
+
_state: uploaded.fileList
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Before — Tiptap editor image uploads
|
|
95
|
+
|
|
96
|
+
```yaml
|
|
97
|
+
- id: editor
|
|
98
|
+
type: TiptapInput
|
|
99
|
+
properties:
|
|
100
|
+
s3PostPolicyRequestId: image_upload_policy
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### After
|
|
104
|
+
|
|
105
|
+
```yaml
|
|
106
|
+
- id: editor
|
|
107
|
+
type: TiptapInput
|
|
108
|
+
properties:
|
|
109
|
+
uploadPolicyRequestId: image_upload_policy
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Before — AgentChat attachments
|
|
113
|
+
|
|
114
|
+
```yaml
|
|
115
|
+
sender:
|
|
116
|
+
attachments:
|
|
117
|
+
enabled: true
|
|
118
|
+
s3PostPolicyRequestId: get_upload_policy
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
### After
|
|
122
|
+
|
|
123
|
+
```yaml
|
|
124
|
+
sender:
|
|
125
|
+
attachments:
|
|
126
|
+
enabled: true
|
|
127
|
+
uploadPolicyRequestId: get_upload_policy
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
## Edge Cases
|
|
131
|
+
|
|
132
|
+
- **Custom CSS selectors:** the generic blocks emit `lf-upload`, `lf-upload-photo`, and
|
|
133
|
+
`lf-upload-dragger` element classes instead of `lf-s3-upload-button`, `lf-s3-upload-photo`,
|
|
134
|
+
and `lf-s3-upload-dragger` (the aliases keep the legacy classes; the renamed blocks do not).
|
|
135
|
+
Grep CSS/`style:` config for `lf-s3-` and update selectors — including inner classes like
|
|
136
|
+
`.lf-s3-upload-photo-icon` → `.lf-upload-photo-icon` and the CSS variable
|
|
137
|
+
`--lf-s3-dragger-height` → `--lf-dragger-height`.
|
|
138
|
+
- If a request-id property value is an operator expression, rename the key and keep the value.
|
|
139
|
+
- Do not rename request types (`AwsS3PresignedPostPolicy`, `AwsS3PresignedGetObject`) or
|
|
140
|
+
connection types (`AwsS3Bucket`) — they are unchanged.
|
|
141
|
+
- References in markdown/help text (e.g. docs strings inside the app) can be renamed too, but
|
|
142
|
+
flag them for human review rather than assuming.
|
|
143
|
+
|
|
144
|
+
## Verification
|
|
145
|
+
|
|
146
|
+
No old block types or legacy property names should remain:
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
grep -rnE 'type: S3(UploadButton|UploadPhoto|UploadDragger|Download)|s3PostPolicyRequestId|s3GetPolicyRequestId' --include='*.yaml' --include='*.yml' .
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
Also confirm no stale CSS selectors:
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
grep -rn 'lf-s3-' --include='*.yaml' --include='*.yml' --include='*.css' .
|
|
156
|
+
```
|