Repository navigation
Let a destination declare several tables, each found by its key - #1768
michielbdejong wants to merge 4 commits into
Conversation
A plugin can declare `accepts` (extensions, media types, maxBytes, as
text) and a `destination` (a schema and one table) in its version-two
manifest. PluginPage then shows an Import tab instead of Run, Schedule and
Trigger: Set up creates the destination table beneath the plugin and
stores { table, rowClass, properties } as its config; choosing a file
decodes it (UTF-8, else Windows-1252), runs the plugin on the server with
input.upload, and reviews the verdict in RunPluginDialog before anything
is written.
/plugin-run refuses an upload the manifest does not accept or that is
larger than its maxBytes, and takes bodies up to PAYLOAD_MAX so a 5 MB
statement fits after JSON escaping. Both manifest parsers accept the new
fields and share the conformance fixtures.
This replaces the plugin-specific ImportMT940 dialog removed in 4bab16e
with a generic entry point, for atomic-plugins' Bank statements importer
(atomic-plugins#95).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Table's genesis commit must carry its classtype, so Set up now creates the table and view with all their properties instead of setting them after creation. The new UI strings are added to the wuchale catalogs, without which they render empty. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A plugin's manifest `destination` can now declare keyed `tables` beside
(or instead of) its single `table`. Set up creates each one beneath the
plugin with its own row class and default view, and stores
`tables: { <key>: { table, rowClass } }` in the plugin's config next to
the unchanged `{ table, rowClass, properties }`. A drive app shown as a
view of any of those tables gets the same map from `store.getData()`.
This is what the money app needs for statements and closing balances
(atomic-plugins#148, M-7 and M-12).
- provisionDestination moves from FileImport into @tomic/lib
(plugin-destination.ts), with destinationTablesFor for the host's
`data` op. Set up resumes: a release that adds a key creates only that
table.
- The server now checks `destination` with the browser's rules and
messages instead of only requiring a map. Both parsers are held to new
shared fixtures in testdata/plugin-manifest/.
- A single-table manifest keeps its config, its table localId and its
release id; the single-table fixture's id is pinned, and so is the
stored bytes of an app package carrying it.
- Docs: creating-plugins.md describes `accepts`, `destination.table`
and `destination.tables`.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
Merge-risk triage (2026-09-28). This is a read-only review of this PR's own diff. The rule it applies: nothing that works today may break for existing users or clients, and new behaviour is added alongside the old. The stack is being rebased onto Verdict: SAFE-WITH-FIX The client side is additive ( Fix before merging: The new server-side Note: It carries a merge of 🤖 Generated with Claude Code |
|
Replaced by the consolidated #2148 (Q-101), which carries this change onto develop. The branch is kept. Closed by Claude (VPS coordinator) on behalf of Michiel. |
Closes #1741
What
A plugin's manifest
destinationcan now declare keyedtablesbeside (or instead of) its singletable. Set up creates each table beneath the plugin, with its own row class and a default view. Each kind of record gets its own table, so statements never show up as rows of the transactions table.The stored config (under
config.key) is{ table, rowClass, properties, tables: { statements: { table, rowClass }, closingBalances: { table, rowClass } } }. The importer writes a statement withparent: config.tables.statements.table. A drive app shown as a view of any of these tables gets the same map fromawait store.getData(), as{ table, rowClass, tables }.The money app needs this for stored statements and closing balances (ontola/atomic-plugins#148, M-7 and M-12). Once this lands, money can keep
tablefor transactions, addtables, and addtablestoconfig.required. Existing installations then show Set up once more, and it creates only the new tables.Rules (the same in TS and Rust, same messages)
table,tablesor both.tablesmust not be empty.Object.prototypemembers (constructor, ...) are refused.rowClass, and it must be a class declared inschema. Columns must be declared properties.Unchanged for single-table manifests
{ table, rowClass, properties }, same table localId (atomic:destination:table), so existing installs resume the same table.destinationis still carried verbatim.destination_fixtures_keep_their_release_idspins the single-table fixture's id, which matchesrelease-ids.jsononclaude/atomic-plugins-pin.app-package.test.ts.Base
The destination Set up step (#1653, branch
claude/plugin-accepts-file) is ondevelop(via #1699) and on the atomic-plugins pin branches, but not onclaude/installation-connect. So this branch isclaude/installation-connectplus a merge ofclaude/plugin-accepts-file(2 commits). The conflicts inplugin-manifest.ts,index.jsonanden.powere resolved by keeping both sides. That merge is the first commit. The change itself is the second.Changes
browser/lib/src/plugin-manifest.ts:DeclaredDestinationTable,destination.tablesand their validation.browser/lib/src/plugin-destination.ts(new):provisionDestinationmoved here fromFileImport.tsxand extended to covertables. Also addsdestinationTablesFor.server/src/plugins/manifest.rs:validate_destinationmirrors the TS checks. Before this, the server only requireddestinationto be a map.hostStore.ts: thedataop addstableswhen the table belongs to a multi-table destination.FileImport.tsx: when there is more than one table, Set up lists them.docs/src/plugins/creating-plugins.md: documentsacceptsand destinations with one table or several.browser/lib/src/test-schema-store.ts: the fake store fromplugin-app.test.ts, now shared.Tests
testdata/plugin-manifest/(2 accepted, 11 rejected), run by bothplugin-manifest.test.tsandshared_manifest_conformance.plugin-destination.test.ts:destinationTablesForfrom any of the tables, with or without a config key, and nothing for a single-table destinationhostStore.test.ts:datareturnstablesfor a multi-table destination and the old shape otherwise.app-package.test.ts: package content is pinned. A multi-table release is carried, and an invalid one is refused.destination_fixtures_keep_their_release_ids(Rust).browser/libvitest (998 passed), data-browser AppPage/PluginRuns/AppSetup vitest (45 passed),pnpm typecheck,cargo test -p atomic-server --lib(288 passed), clippy clean onmanifest.rs.handleRequest(…'data'…)on the transactions table and on the statements table both returned the keyedtables.Translations
The catalogs were settled with one vite run after
rm -rf src/locales/.wuchale. The diff has one new message ("Set up creates {0} tables for this importer. Nothing is imported yet.") and a changed placeholder comment on the single-table message.Screenshots
Set up for a three-table importer, before and after, on desktop (1280) and mobile (390). They were taken with Playwright against the running app and shared in the session:
multiclass-desktop-before.png,multiclass-desktop-after.png,multiclass-mobile-before.png,multiclass-mobile-after.png.🤖 Generated with Claude Code