Conversation
4672e37 to
6da22a2
Compare
|
So far Claude has done browser tests, and I have also done manual tests for a full end-to-end flow: Create Schema -> Create Subject -> View Subject -> Edit Schema -> Edit Subject -> View Subject. This does not consider the recent comments in #686 (comment). |
Strongly aligned with #686's design direction. Structural choices match; a few things worth calling out. Aligned
Points worth flagging
Open items worth checking in the diff
Observations worth preserving
Overall: tracks the agreed direction and makes two sensible judgment calls on top. The narrow-vs-broad API surface is the one decision I'd want confirmed explicitly before it solidifies. |
6da22a2 to
bd8f5d3
Compare
Re-export every TS module (via wildcard) and every Vue component under
resources/ext.neowiki/src/ from the public-api barrel. Extensions using
require('ext.neowiki') can now reach anything NeoWiki itself exposes —
domain types and services, application-layer lookups and repositories,
persistence serializers/deserializers, stores, composables, and the
full component library.
0.x / alpha stability contract: the surface is provisional and any
symbol may be renamed or removed without migration guidance. A
curation pass to narrow the public API is planned before production
stabilisation. Consumers should pin specific NeoWiki commits during
the alpha phase.
Bundle size impact: 218 KB → 229 KB raw (+11 KB / +5%), 59 KB → 62 KB
gzipped (+3 KB). Acceptable for the flexibility it provides during
alpha build-out.
Stacks on top of #754.
Re-export every TS module (via wildcard) and every Vue component under
resources/ext.neowiki/src/ from the public-api barrel. Extensions using
require('ext.neowiki') can now reach anything NeoWiki itself exposes —
domain types and services, application-layer lookups and repositories,
persistence serializers/deserializers, stores, composables, and the
full component library.
0.x / alpha stability contract: the surface is provisional and any
symbol may be renamed or removed without migration guidance. A
curation pass to narrow the public API is planned before production
stabilisation. Consumers should pin specific NeoWiki commits during
the alpha phase.
Bundle size impact: 218 KB → 229 KB raw (+11 KB / +5%), 59 KB → 62 KB
gzipped (+3 KB). Acceptable for the flexibility it provides during
alpha build-out.
Stacks on top of #754.
Seems better to have a fully working initial example in one PR.
That adds everything (for now).
Likely TODO in a follow-up. Maybe as part of the same non-DateTime example.
This should be split off into a new issue. While more likely to happen with extensions, it can still theoretically happen in core if we removed a type. I need to do some final reviews on this PR, and see if I can come up with an alternative to DateTime so we do not have to jump around between core and RedHerb. Otherwise, if we keep the revertable commit, we can just undo this a little bit later once we have a different example to keep in RedHerb. |
Re-export every TS module (via wildcard) and every Vue component under
resources/ext.neowiki/src/ from the public-api barrel. Extensions using
require('ext.neowiki') can now reach anything NeoWiki itself exposes —
domain types and services, application-layer lookups and repositories,
persistence serializers/deserializers, stores, composables, and the
full component library.
0.x / alpha stability contract: the surface is provisional and any
symbol may be renamed or removed without migration guidance. A
curation pass to narrow the public API is planned before production
stabilisation. Consumers should pin specific NeoWiki commits during
the alpha phase.
Bundle size impact: 218 KB -> 239 KB raw (+21 KB / +10%), 59 KB -> 64 KB
gzipped (+5 KB / +8%). Acceptable for the flexibility it provides
during alpha build-out.
Stacks on top of #754.
bd8f5d3 to
bb0782c
Compare
Re-export every TS module (via wildcard) and every Vue component under
resources/ext.neowiki/src/ from the public-api barrel. Extensions using
require('ext.neowiki') can now reach anything NeoWiki itself exposes -
domain types and services, application-layer lookups and repositories,
persistence serializers/deserializers, stores, composables, and the
full component library.
0.x / alpha stability contract: the surface is provisional and any
symbol may be renamed or removed without migration guidance. A
curation pass to narrow the public API is planned before production
stabilisation. Consumers should pin specific NeoWiki commits during
the alpha phase.
Bundle size impact: 240 KB -> 251 KB raw (+11 KB / +4%), 63 KB -> 66 KB
gzipped (+3 KB / +5%). Acceptable for the flexibility it provides
during alpha build-out.
Stacks on top of #754.
Re-export every TS module (via wildcard) and every Vue component under
resources/ext.neowiki/src/ from the public-api barrel. Extensions using
require('ext.neowiki') can now reach anything NeoWiki itself exposes -
domain types and services, application-layer lookups and repositories,
persistence serializers/deserializers, stores, composables, and the
full component library.
0.x / alpha stability contract: the surface is provisional and any
symbol may be renamed or removed without migration guidance. A
curation pass to narrow the public API is planned before production
stabilisation. Consumers should pin specific NeoWiki commits during
the alpha phase.
Bundle size impact: 240 KB -> 251 KB raw (+11 KB / +4%), 63 KB -> 66 KB
gzipped (+3 KB / +5%). Acceptable for the flexibility it provides
during alpha build-out.
Stacks on top of #754.
dc3dce6 to
e9fc307
Compare
Re-export every TS module (via wildcard) and every Vue component under
resources/ext.neowiki/src/ from the public-api barrel. Extensions using
require('ext.neowiki') can now reach anything NeoWiki itself exposes -
domain types and services, application-layer lookups and repositories,
persistence serializers/deserializers, stores, composables, and the
full component library.
0.x / alpha stability contract: the surface is provisional and any
symbol may be renamed or removed without migration guidance. A
curation pass to narrow the public API is planned before production
stabilisation. Consumers should pin specific NeoWiki commits during
the alpha phase.
Bundle size impact: 240 KB -> 251 KB raw (+11 KB / +4%), 63 KB -> 66 KB
gzipped (+3 KB / +5%). Acceptable for the flexibility it provides
during alpha build-out.
Stacks on top of #754.
e9fc307 to
136ee5e
Compare
Re-export every TS module (via wildcard) and every Vue component under
resources/ext.neowiki/src/ from the public-api barrel. Extensions using
require('ext.neowiki') can now reach anything NeoWiki itself exposes -
domain types and services, application-layer lookups and repositories,
persistence serializers/deserializers, stores, composables, and the
full component library.
0.x / alpha stability contract: the surface is provisional and any
symbol may be renamed or removed without migration guidance. A
curation pass to narrow the public API is planned before production
stabilisation. Consumers should pin specific NeoWiki commits during
the alpha phase.
Bundle size impact: 240 KB -> 251 KB raw (+11 KB / +4%), 63 KB -> 66 KB
gzipped (+3 KB / +5%). Acceptable for the flexibility it provides
during alpha build-out.
Stacks on top of #754.
Re-export every TS module (via wildcard) and every Vue component under
resources/ext.neowiki/src/ from the public-api barrel. Extensions using
require('ext.neowiki') can now reach anything NeoWiki itself exposes -
domain types and services, application-layer lookups and repositories,
persistence serializers/deserializers, stores, composables, and the
full component library.
0.x / alpha stability contract: the surface is provisional and any
symbol may be renamed or removed without migration guidance. A
curation pass to narrow the public API is planned before production
stabilisation. Consumers should pin specific NeoWiki commits during
the alpha phase.
Bundle size impact: 240 KB -> 251 KB raw (+11 KB / +4%), 63 KB -> 66 KB
gzipped (+3 KB / +5%). Acceptable for the flexibility it provides
during alpha build-out.
Stacks on top of #754.
136ee5e to
933e95e
Compare
Split Vite entry: src/public-api.ts is now the barrel that re-exports
runtime symbols MW-native extensions can require('ext.neowiki'). It
imports src/neowiki.ts for its mount side-effects.
Memoize NeoWikiExtension.getTypeSpecificComponentRegistry() and
PropertyTypeRegistry on Neo so every caller sees the same instance —
prerequisite for the extension-registration hook being added next,
which needs to mutate the registry that Vue later receives.
For #686
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Extensions can now register custom property types at runtime:
1. PHP: implement NeoWikiGetFrontendModules to append their RL module
name to NeoWiki's module list for any page that loads ext.neowiki.
2. JS: subscribe to mw.hook('neowiki.registration') and call
registrar.registerPropertyType({...}) with a plain-object
PropertyTypeRegistration.
NeoWiki wraps incoming registrations via PropertyTypeAdapter into
BasePropertyType instances; FrontendRegistrar inserts them into the
memoized property-type and component registries.
Mount is deferred via queueMicrotask so extension init.js files get a
chance to subscribe before the hook fires. No consumer of the hook
exists yet — DateTime still lives in core and will move in a
follow-up commit.
For #686
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Re-export every TS module (via wildcard) and every Vue component under
resources/ext.neowiki/src/ from the public-api barrel. Extensions using
require('ext.neowiki') can now reach anything NeoWiki itself exposes -
domain types and services, application-layer lookups and repositories,
persistence serializers/deserializers, stores, composables, and the
full component library.
0.x / alpha stability contract: the surface is provisional and any
symbol may be renamed or removed without migration guidance. A
curation pass to narrow the public API is planned before production
stabilisation. Consumers should pin specific NeoWiki commits during
the alpha phase.
Bundle size impact: 240 KB -> 251 KB raw (+11 KB / +4%), 63 KB -> 66 KB
gzipped (+3 KB / +5%). Acceptable for the flexibility it provides
during alpha build-out.
Stacks on top of #754.
933e95e to
efc2dfc
Compare
|
I decided to split out the DateTime implementation into #777. It seems like we would just be moving that up and down for the sake of an example. I'm keeping that PR, but will follow-up with a Color example instead, which can stay in RedHerb and not require us to mess around with a property type that is a requirement. After the Color PR we should move all the DateTime code in RedHerb into core instead. |
First extension-provided property-type frontend built on the neowiki.registration hook surface. For #686. Stacks on #754. This is an alternative path to PR #777, not a stack on it. PR #777 is a reference-only example that moves the existing core DateTime frontend into RedHerb to demonstrate the migration path; the intent for DateTime itself is to remain in NeoWiki core as a built-in. This PR introduces Color as a permanent example of an extension-defined property type, i.e. the shape any third-party extension would take, without disturbing core's built-in types. What it adds: * ColorDisplay.vue renders a swatch + hex via a format-only check (intentionally tolerant of input-time constraints like allowedColors so previously-valid values keep rendering); falls back through I18nSlot for unparseable values. * ColorInput.vue is a cdx-text-input plus a live preview swatch and the type's start icon. Mirrors the useStringValueInput convention: invalid mid-typing stays visible to the user but does not propagate through update:modelValue or getCurrentValue(). * ColorAttributesEditor.vue wraps an optional allowedColors palette in NeoNestedField, with a drag-reorderable list via useSortable and a dirty marker driven by useChangeDetection. * RedHerbFrontendHook.php implements NeoWikiGetFrontendModules so the new ext.redherb ResourceLoader module loads alongside ext.neowiki. * ColorProperty gains an optional allowedColors attribute (validated as 6-digit hex strings), with round-trip coverage in tests/phpunit/RedHerb/ColorPropertyTest.php. Integration points exercised: * mw.hook('neowiki.registration') as a second consumer alongside core property types. * NeoWikiGetFrontendModules PHP hook. * NeoWikiServices.getPropertyTypeRegistry and NeoWikiServices.getComponentRegistry. * Composables: useValueValidation, useChangeDetection, useSortable. * Vue components from the public-API barrel: NeoNestedField, I18nSlot. NeoWikiHooksTest::testAddsCoreModuleWhenNoExtensionsHandleHook now clearHook()'s NeoWikiGetFrontendModules so it tests what its name claims regardless of which extensions the test environment loads. Known gaps: * Persistence-side schema validation rejects extension-defined type names; saves through the schema editor return hookaborted until the JSON Schema in src/Persistence/MediaWiki/schemaContentSchema.json is loosened or built from the registry. See #779. * Pinia store sharing not directly probed in this PR; deferred to a Vitest integration test in NeoWiki core. * SchemaDeserializer / RestSchemaRepository / RestLayoutRepository do not have a natural fit in the Color flow. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Each initialize*Page() in neowiki.ts previously called createPinia() and got its own root, so two Vue apps mounted on the same page could not observe each other's store state. Centralize ownership of the Pinia in NeoWikiExtension, expose getPinia(), and route every init through it. Tested via a behavioral spec that mutates a store via one consumer and reads it via another using the same Pinia, which is the actual contract the shared instance is meant to guarantee. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a SidebarBeforeOutput hook handler contributing a "RedHerb" sidebar section with a "Find a subject" link to a new Special:RedHerbSubjectFinder. The special page mounts a RedHerb-owned Vue panel via Vue.createMwApp that lets the user pick a subject by schema and label, then renders the selected subject using NeoWiki's Infobox component. Exercises several integration points through the public-API barrel introduced by #754: RestSubjectLabelSearch (REST repository) via the embedded SubjectLookup component, the SubjectLabelSearch service via NeoWiki's service injection, the StoreStateLoader for bulk-loading subjects and schemas, the Infobox view component, and the shared Pinia owned by NeoWikiExtension. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
testAddsCoreModuleWhenNoExtensionsHandleHook asserts that only ext.neowiki is queued when no extension contributes via the NeoWikiGetFrontendModules hook. Real loaded extensions (such as RedHerb in CI) register handlers that contribute their own modules, so the assertion fails. Clear the hook at the start of the test so its premise holds regardless of which extensions are loaded. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
First extension-provided property-type frontend built on the neowiki.registration hook surface. For #686. Stacks on #754. This is an alternative path to PR #777, not a stack on it. PR #777 is a reference-only example that moves the existing core DateTime frontend into RedHerb to demonstrate the migration path; the intent for DateTime itself is to remain in NeoWiki core as a built-in. This PR introduces Color as a permanent example of an extension-defined property type, i.e. the shape any third-party extension would take, without disturbing core's built-in types. What it adds: * ColorDisplay.vue renders a swatch + hex via a format-only check (intentionally tolerant of input-time constraints like allowedColors so previously-valid values keep rendering); falls back through I18nSlot for unparseable values. * ColorInput.vue is a cdx-text-input plus a live preview swatch and the type's start icon. Mirrors the useStringValueInput convention: invalid mid-typing stays visible to the user but does not propagate through update:modelValue or getCurrentValue(). * ColorAttributesEditor.vue wraps an optional allowedColors palette in NeoNestedField, with a drag-reorderable list via useSortable and a dirty marker driven by useChangeDetection. * RedHerbFrontendHook.php implements NeoWikiGetFrontendModules so the new ext.redherb ResourceLoader module loads alongside ext.neowiki. * ColorProperty gains an optional allowedColors attribute (validated as 6-digit hex strings), with round-trip coverage in tests/phpunit/RedHerb/ColorPropertyTest.php. Integration points exercised: * mw.hook('neowiki.registration') as a second consumer alongside core property types. * NeoWikiGetFrontendModules PHP hook. * NeoWikiServices.getPropertyTypeRegistry and NeoWikiServices.getComponentRegistry. * Composables: useValueValidation, useChangeDetection, useSortable. * Vue components from the public-API barrel: NeoNestedField, I18nSlot. NeoWikiHooksTest::testAddsCoreModuleWhenNoExtensionsHandleHook now clearHook()'s NeoWikiGetFrontendModules so it tests what its name claims regardless of which extensions the test environment loads. Known gaps: * Persistence-side schema validation rejects extension-defined type names; saves through the schema editor return hookaborted until the JSON Schema in src/Persistence/MediaWiki/schemaContentSchema.json is loosened or built from the registry. See #779. * Pinia store sharing not directly probed in this PR; deferred to a Vitest integration test in NeoWiki core. * SchemaDeserializer / RestSchemaRepository / RestLayoutRepository do not have a natural fit in the Color flow. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a SidebarBeforeOutput hook handler contributing a "RedHerb" sidebar section with a "Find a subject" link to a new Special:RedHerbSubjectFinder. The special page mounts a RedHerb-owned Vue panel via Vue.createMwApp that lets the user pick a subject by schema and label, then renders the selected subject using NeoWiki's Infobox component. Exercises several integration points through the public-API barrel introduced by #754: RestSubjectLabelSearch (REST repository) via the embedded SubjectLookup component, the SubjectLabelSearch service via NeoWiki's service injection, the StoreStateLoader for bulk-loading subjects and schemas, the Infobox view component, and the shared Pinia owned by NeoWikiExtension. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Fixes #686
Implements Option 1 from the design discussion on #686 —
mw.hook('neowiki.registration')with plain-object registrations, MW-native ResourceLoader modules, and plain.vueSFCs for extensions. No bundler or TypeScript required on the extension side.Summary
Lets external MediaWiki extensions register custom frontend property types at runtime, without touching NeoWiki source or rebuilding its bundle. This PR adds the mechanism only; no consumer is in-tree. Companion PRs exercise it:
datetime-example— reference PR demonstrating migration of a core property type (DateTime) into an extension. Not intended to merge; exists as a live worked example.What this PR ships
mw.hook('neowiki.registration')— subscribers receive aFrontendRegistrarand callregistrar.registerPropertyType({...})with a plain-objectPropertyTypeRegistration. An internalPropertyTypeAdapterwraps each registration as aBasePropertyType; the registrar inserts it into NeoWiki's component and property-type registries.NeoWikiGetFrontendModules( array &$modules, OutputPage $out, Skin $skin )— extensions append their ResourceLoader module names; NeoWiki adds them alongsideext.neowikion any page that loads it. Consumed inonBeforePageDisplayplus the Special:Schemas and Special:Layouts entry points.resources/ext.neowiki/src/public-api.ts— re-exports every TS module and Vue component underresources/ext.neowiki/src/(83 TS files, 52 Vue files, ~146 runtime names).0.x / alphastability contract: anything may change without migration guidance, narrowed before production stabilisation.NeoWikiExtension.getTypeSpecificComponentRegistry()andNeo.getPropertyTypeRegistry()return the same instance per call, so the registry the hook populates is the same oneNeoWikiServicesprovides to Vue.initialize*function inneowiki.tswraps its body inqueueMicrotaskand fires the registration hook before deserialization and mount, giving extensioninit.jsfiles a chance to subscribe first.Jeroen's three usage modes (from this comment)
The mechanism itself supports all three. The consumer PRs exercise them end-to-end:
mw.hook('neowiki.registration')+FrontendRegistrardatetime-example(#777); upcoming Color PRdatetime-exampleusesNeoNestedField; Color PR plansNeoMultiTextInput,EditSummarydatetime-examplecallsNeoWikiServices.getPropertyTypeRegistry().getType(...).validate(...); Color PR plans stores + composables + a repositoryCommits
resources/ext.neowiki/tests/integration/HookRegistration.spec.ts) proves end-to-end via a fake registration.src/.Test plan
make tscigreen — 771 Vitest tests across 78 files, 266 bundler modules transformedmake phpunitfull suite — pre-existing unrelated failure inNeo4jConstraintUpdaterTest::testDefaultConstraintsAreCreated(version driftUNIQUENESSvsNODE_PROPERTY_UNIQUENESS) reproduces on mastermake csphpcs + phpstan cleanHookRegistration.spec.tsexercises.fire()before subscribe +.add()after.fire()(mw.hook replay semantics) — both code paths greenManual browser check
Because this PR has no in-tree consumer, end-to-end UI verification lives in the consumer PRs. From the repo root,
make import-demo-data(if needed) anddocker compose restart mediawiki. Log in asAdminName/AdminPassword.Mechanism-only checks
curl 'http://localhost:8484/load.php?modules=ext.neowiki&only=scripts&lang=en&debug=true'returns 200 and the response includes the 146 named exports onmodule.exports(inspect the tail of the bundle).Full end-to-end via a consumer
For end-to-end verification of the registration flow itself, either check out
datetime-example(PR #777) or the upcoming Color PR and follow its test plan. The integration test in this PR covers the same flow at the unit level.Follow-ups
export *errors at build when two modules grow an identically-named export. Fine for now; worth a script/check if the source tree grows a lot.datetime-example: disabling the extension leaves stored data using that type unrenderable and the NeoWiki UI container empty. Architecturally expected for extension-owned types, but worth a "unknown property type" placeholder component rather than aborting the whole view.