@lowdefy/codemods 5.6.0 → 6.0.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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lowdefy/codemods",
3
- "version": "5.6.0",
3
+ "version": "6.0.0",
4
4
  "description": "Codemod scripts and migration prompts for Lowdefy version upgrades",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
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.2"
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
+ ```