@ablogcms/plugins 3.2.39 → 3.2.40

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/HOOKS.md CHANGED
@@ -217,6 +217,10 @@ a-blog cms 管理画面(React SPA)が提供する フロントエンド側
217
217
  - プラグイン JS は **`<script type="module">`(または `defer`)で読み込むこと**。HTML 仕様上 DOM 解析完了後・
218
218
  `DOMContentLoaded` 前に出現順で同期実行されるため、「プラグインの登録 → core の消費(`notify`/`gate` /
219
219
  レジストリ読み出し)」の順序が構造的に保証される。
220
+ - 例外として、`ctx.unitEditor.registerUnitDefinition` は、エディターの描画後(`ACMS.adminReady(() => { ... })` の中など)
221
+ に登録しても、描画済みのユニットエディター・ユニット設定・インプレースエディターへ反映される(レジストリの変更を
222
+ `Registry.subscribe` で購読し、`unitDefs` を差し替えて再描画するため)。他のレジストリ(ブロック・Tiptap 拡張・
223
+ dataview 系)は従来どおり、読み込み時の同期登録を前提とする。
220
224
  - **`InjectTemplate` の登録順 = `<script>` の出力順 = ハンドラ登録順**。複数プラグインの実行順を
221
225
  `InjectTemplate::add()` の呼び出し順(= ServiceProvider の初期化順)で制御できる(kintone がプラグイン間
222
226
  順序を保証していない弱点を仕様で塞ぐ)。
@@ -322,7 +326,7 @@ ctx.ui.fill('unit-editor.unit-toolbar', (el) => {
322
326
 
323
327
  | レジストリ | API | 実体 | コアの組み込み定義とのマージ |
324
328
  |---|---|---|---|
325
- | ユニットタイプ | `ctx.unitEditor.registerUnitDefinition(name, def: UnitDefInterface)` | `createRegistry<UnitDefInterface>('unit-definition')` | `ablogcms/js/src/features/unit-editor/unit-definitions.ts` が `mergeRegistryEntries(coreUnitDefs, getUnitDefinitionRegistry())` でマージ(`{ 'rich-editor': RichEditorUnitDef, custom: CustomUnitDef, ...registry.getAll() }`。`rich-editor`/`custom` は本体固有の実装に依存するため `@ablogcms/unit-editor` のコアには置かず本体側から注入する。キー衝突時はレジストリ側が勝つ) |
329
+ | ユニットタイプ | `ctx.unitEditor.registerUnitDefinition(name, def: UnitDefInterface)` | `createRegistry<UnitDefInterface>('unit-definition')` | `ablogcms/js/src/features/unit-editor/unit-definitions.ts` が `mergeRegistryEntries(coreUnitDefs, getUnitDefinitionRegistry())` でマージ。描画後の登録は `useUnitDefinitions`(`Registry.subscribe` を購読)が `unitDefs` として差し替える(`{ 'rich-editor': RichEditorUnitDef, custom: CustomUnitDef, ...registry.getAll() }`。`rich-editor`/`custom` は本体固有の実装に依存するため `@ablogcms/unit-editor` のコアには置かず本体側から注入する。キー衝突時はレジストリ側が勝つ) |
326
330
  | dataview カラム型 | `ctx.dataview.registerColumnType(name, type: ColumnTypeDef)` | `createRegistry<ColumnTypeDef>('column-type')` | `features/entry/utils/index.ts` / `features/module/utils/index.ts` が `{ ...ENTRY_COLUMN_TYPES, ...getColumnTypeRegistry().getAll() }` の形でマージ |
327
331
  | dataview 行アクション | `ctx.dataview.registerAction(view, name, def: Action<RowData>)`(`view` は `'entry' \| 'module'`。将来 `'user'`/`'category'` 等へ拡張可能な文字列 union) | `createViewedRegistry<Action<RowData>>('dataview-action')`(`view` ごとに独立した名前空間を持つ。同じ `name` でも `view` が違えば衝突しない) | `use-entry-admin-actions.tsx` / `use-module-admin-actions.tsx` が `[...coreActions, ...Object.values(getDataviewActionRegistry().getAll(view))]` をコア定義 → レジストリの順で連結し、その結果をレガシー `getActions`(`ACMS.Config.entryAdminConfig.getActions` 等)へそのまま渡す(コア → レジストリ → レガシー getter の3段マージ。既存 getter 利用者は要素数が増えるだけで挙動は変わらない) |
328
332
  | dataview 一括アクション | `ctx.dataview.registerBulkAction(view, name, def: BulkAction<RowData>)` | `createViewedRegistry<BulkAction<RowData>>('dataview-bulk-action')` | `use-entry-admin-bulk-actions.tsx` / `use-module-admin-bulk-actions.tsx` が上記と同じ3段マージ(`getBulkActions` へ連結後の配列を渡す) |
@@ -645,7 +649,7 @@ await ACMS.events.notify('some.thing.success', { foo: 1 });
645
649
  setup(ctx) {
646
650
  const { withFillProps } = ACMS.plugins;
647
651
  ctx.ui.fill('unit-editor.unit-toolbar', withFillProps(({ editor, unit }) => (
648
- window.React.createElement('button', { type: 'button' }, unit.type)
652
+ ACMS.Library.React.createElement('button', { type: 'button' }, unit.type)
649
653
  )));
650
654
  },
651
655
  });
@@ -713,8 +717,28 @@ externals: {
713
717
  (`async` は `defer` より優先され、実行順が保証されない)。その場合 `ACMS.plugins` や `ACMS.Library.React` は
714
718
  まだ無い。トップレベルでこれらを参照する前に `ACMS.adminReady(() => { ... })` で包むこと。`adminReady` は
715
719
  `admin.js` の初期化完了後に呼ばれ、すでに完了していれば即時に呼ばれる。ただしコールバック内の登録は
716
- 「トップレベル同期登録」の前提から外れるため、core が先に参照するレジストリ(`registerUnitDefinition` 等)の
717
- 取りこぼしがないかを確認する。
720
+ 「トップレベル同期登録」の前提から外れるため、core が先に参照するレジストリの取りこぼしがないかを確認する。
721
+ `registerUnitDefinition` だけは、描画後の登録も描画済みのエディターへ反映される([登録タイミング制約](#登録タイミング制約)
722
+ 参照)。ブロック定義・Tiptap 拡張・dataview 系は反映されないため、`adminReady` の中では登録しない。
723
+
724
+ ### ダイアログ(`ACMS.Library.dialog`)
725
+
726
+ 管理画面の確認・通知ダイアログを、ブラウザ標準の `window.confirm` の代わりに使える。`ACMS.Library.dialog` は
727
+ `admin.js` の初期化中に差し込まれるため、`ACMS.adminReady(() => { ... })` の中、またはイベントハンドラなど
728
+ 初期化後に実行される処理から呼ぶこと(トップレベルでは未定義のことがある)。
729
+
730
+ | メソッド | 戻り値 | 説明 |
731
+ |---|---|---|
732
+ | `dialog.confirm(message, options?)` | `Promise<boolean>` | 確認ダイアログ。OK で `true`、キャンセル・閉じるで `false` |
733
+ | `dialog.alert(message, options?)` | `Promise<void>` | 通知ダイアログ。閉じたときに解決される |
734
+ | `dialog.prompt(message, options?)` | `Promise<string \| null>` | 入力ダイアログ。入力値、キャンセルで `null` |
735
+
736
+ `options` では `title` と、`confirmButton` / `cancelButton`(ボタンの props)を上書きできる。
737
+
738
+ ```js
739
+ const ok = await ACMS.Library.dialog.confirm('この操作を実行します。よろしいですか?');
740
+ if (!ok) return;
741
+ ```
718
742
 
719
743
  ### 出力形式は IIFE にする
720
744
 
@@ -12,21 +12,39 @@ import { createI18nResourceRegistry } from "@ablogcms/i18n/core";
12
12
  // src/core/create-registry.ts
13
13
  function createRegistry(label) {
14
14
  const items = /* @__PURE__ */ new Map();
15
+ const listeners = /* @__PURE__ */ new Set();
16
+ const notify = () => {
17
+ [...listeners].forEach((listener) => listener());
18
+ };
15
19
  const add = (name, item) => {
16
20
  if (items.has(name)) {
17
21
  throw new Error(`[@ablogcms/plugins] ${label} "${name}" \u306F\u65E2\u306B\u767B\u9332\u3055\u308C\u3066\u3044\u307E\u3059\u3002`);
18
22
  }
19
23
  items.set(name, item);
24
+ notify();
20
25
  return () => {
21
26
  if (items.get(name) === item) {
22
27
  items.delete(name);
28
+ notify();
23
29
  }
24
30
  };
25
31
  };
26
- const remove = (name) => items.delete(name);
32
+ const remove = (name) => {
33
+ const removed = items.delete(name);
34
+ if (removed) {
35
+ notify();
36
+ }
37
+ return removed;
38
+ };
27
39
  const get = (name) => items.get(name);
28
40
  const getAll = () => Object.fromEntries(items);
29
- return { add, remove, get, getAll };
41
+ const subscribe = (listener) => {
42
+ listeners.add(listener);
43
+ return () => {
44
+ listeners.delete(listener);
45
+ };
46
+ };
47
+ return { add, remove, get, getAll, subscribe };
30
48
  }
31
49
 
32
50
  // src/core/create-viewed-registry.ts
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/create-plugin-registry.ts","../../src/core/create-registry.ts","../../src/core/create-viewed-registry.ts","../../src/core/resolve-fill-entry.ts","../../src/core/merge-registry-entries.ts","../../src/core/global-singletons.ts"],"sourcesContent":["import type { AcmsEventType, AcmsEvents, AcmsGateContext, AcmsLooseEvent, Unsubscribe } from '@ablogcms/events';\nimport { createEventBus } from '@ablogcms/events/core';\nimport { createSlotFillStore, type SlotFillStore, type FillRenderer } from '@ablogcms/components/slot-fill';\nimport type { Action, BulkAction, ColumnTypeDef, Menu, RowData } from '@ablogcms/components/dataview/types';\nimport type { UnitDefInterface } from '@ablogcms/unit-editor/core/types/unit';\nimport { createKeyboardShortcutRegistry } from '@ablogcms/keyboard-shortcut';\nimport type { KeyboardShortcutRegistry } from '@ablogcms/keyboard-shortcut';\nimport { createI18nResourceRegistry, type I18nResourceRegistry } from '@ablogcms/i18n/core';\nimport { createRegistry } from './create-registry';\nimport { createViewedRegistry, type ViewedRegistry } from './create-viewed-registry';\nimport { resolveFillEntry } from './resolve-fill-entry';\nimport { withFillProps } from './with-fill-props';\nimport type { BlockDefinitionFactory, BlockExtensionFactory } from './global-singletons';\nimport type {\n AcmsPlugins,\n FillEntry,\n PluginContext,\n PluginDefinition,\n RegisteredEntry,\n RegisteredPlugin,\n Registry,\n} from './types';\n\nexport interface CreatePluginRegistryOptions {\n /** 委譲先のイベントバス。省略時は自己完結したインスタンスを新規作成する(テスト用の既定値) */\n eventsBus?: AcmsEvents;\n /** `ctx.ui.fill` の書き込み先。省略時は自己完結したインスタンスを新規作成する(テスト用の既定値) */\n fillStore?: SlotFillStore;\n /** `ctx.unitEditor.registerUnitDefinition` の登録先。省略時は自己完結したインスタンスを新規作成する */\n unitDefinitionRegistry?: Registry<UnitDefInterface>;\n /** `ctx.dataview.registerColumnType` の登録先。省略時は自己完結したインスタンスを新規作成する */\n columnTypeRegistry?: Registry<ColumnTypeDef>;\n /** `ctx.blockEditor.registerBlockDefinition` の登録先。省略時は自己完結したインスタンスを新規作成する */\n blockDefinitionRegistry?: Registry<BlockDefinitionFactory>;\n /** `ctx.blockEditor.registerExtension` の登録先。省略時は自己完結したインスタンスを新規作成する */\n blockExtensionRegistry?: Registry<BlockExtensionFactory>;\n /** `ctx.keyboardShortcuts` の登録先。省略時は自己完結したインスタンスを新規作成する */\n keyboardShortcutRegistry?: KeyboardShortcutRegistry;\n\n /** `ctx.i18n` の登録先。省略時は自己完結したインスタンスを新規作成する */\n i18nResourceRegistry?: I18nResourceRegistry;\n /** `ctx.dataview.registerAction` の登録先。省略時は自己完結したインスタンスを新規作成する */\n dataviewActionRegistry?: ViewedRegistry<Action<RowData>>;\n /** `ctx.dataview.registerBulkAction` の登録先。省略時は自己完結したインスタンスを新規作成する */\n dataviewBulkActionRegistry?: ViewedRegistry<BulkAction<RowData>>;\n /** `ctx.dataview.registerMenu` の登録先。省略時は自己完結したインスタンスを新規作成する */\n dataviewMenuRegistry?: ViewedRegistry<Menu<RowData>>;\n}\n\ninterface InternalPlugin {\n name: string;\n version?: string;\n registrations: RegisteredEntry[];\n subscriptions: Unsubscribe[];\n}\n\n// AcmsEvents#on は型引数 K ごとにハンドラの戻り値を絞り込むため、type がユニオン\n// (AcmsEventType | AcmsEventType[])のまま渡すとハンドラの戻り値を厳密に検証できない。\n// create-event-bus.ts と同じく、内部では緩い型で扱い、公開 API の境界(PluginContext)でのみ\n// 厳密な型へキャストする。\ntype LooseHandler = (event: AcmsLooseEvent, gateCtx?: AcmsGateContext) => unknown;\ntype LooseOn = (type: AcmsEventType | AcmsEventType[], handler: LooseHandler) => Unsubscribe;\n\n// プラグイン起因の例外をどのプラグインが投げたか特定できるよう帰属を明示する。\n// イベントバス自身も汎用メッセージで同じ例外をログするため、この行は「どのプラグインか」を示す追加の帰属ログになる。\nconst logHandlerError = (pluginName: string, eventLabel: string, err: unknown): void => {\n console.error(`[@ablogcms/plugins] プラグイン \"${pluginName}\" のハンドラ(\"${eventLabel}\")が例外を投げました。`, err);\n};\n\nconst toEventLabel = (type: AcmsEventType | AcmsEventType[]): string => (Array.isArray(type) ? type.join(',') : type);\n\nconst toKeyboardShortcutRegistrationId = (pluginName: string, id: string): string => `plugin:${pluginName}:${id}`;\n\n/**\n * `window.ACMS.Config` からプラグインの設定値を読む。プラグイン名は `-`/`_` 区切りのことが多いため、\n * `jsModule()` 側の慣例(キャメルケースキー)に合わせて変換する。未初期化・未設定時は空オブジェクトを返し、\n * `setup(ctx)` 内で `ctx.config` を参照しただけで例外にならないようにする。\n */\nconst readConfig = (pluginName: string): Record<string, unknown> => {\n if (typeof window === 'undefined') {\n return {};\n }\n const camelName = pluginName.replace(/[-_](\\w)/g, (_match, chr: string) => chr.toUpperCase());\n const config = (window as unknown as { ACMS?: { Config?: Record<string, unknown> } }).ACMS?.Config;\n const value = config?.[camelName];\n return typeof value === 'object' && value !== null ? (value as Record<string, unknown>) : {};\n};\n\n/**\n * `ACMS.plugins` の純粋実装(グローバル非依存)。VS Code の `activate(context)` +\n * `context.subscriptions` を範に、`setup(ctx)` 内でのイベント購読・レジストリ登録・UI 差し込みを\n * すべて内部の subscriptions 配列に集約し、`unregister(name)` で一括自動回収する。\n *\n * 依存(イベントバス・Fill ブリッジ・各レジストリ)はすべて注入可能で、省略時は自己完結した\n * インスタンスを新規作成する(`@ablogcms/events` の `createEventBus()` と同じ設計。グローバル\n * シングルトンへの結線は本番の bootstrap(admin.js)が `global-singletons.ts` 経由で明示的に行う)。\n */\nexport function createPluginRegistry(options: CreatePluginRegistryOptions = {}): AcmsPlugins {\n const eventsBus = options.eventsBus ?? createEventBus();\n const fillStore = options.fillStore ?? createSlotFillStore();\n const unitDefinitionRegistry = options.unitDefinitionRegistry ?? createRegistry<UnitDefInterface>('unit-definition');\n const columnTypeRegistry = options.columnTypeRegistry ?? createRegistry<ColumnTypeDef>('column-type');\n const blockDefinitionRegistry =\n options.blockDefinitionRegistry ?? createRegistry<BlockDefinitionFactory>('block-definition');\n const blockExtensionRegistry = options.blockExtensionRegistry ?? createRegistry<BlockExtensionFactory>('extension');\n const keyboardShortcutRegistry = options.keyboardShortcutRegistry ?? createKeyboardShortcutRegistry();\n const i18nResourceRegistry = options.i18nResourceRegistry ?? createI18nResourceRegistry();\n const dataviewActionRegistry =\n options.dataviewActionRegistry ?? createViewedRegistry<Action<RowData>>('dataview-action');\n const dataviewBulkActionRegistry =\n options.dataviewBulkActionRegistry ?? createViewedRegistry<BulkAction<RowData>>('dataview-bulk-action');\n const dataviewMenuRegistry = options.dataviewMenuRegistry ?? createViewedRegistry<Menu<RowData>>('dataview-menu');\n\n const plugins = new Map<string, InternalPlugin>();\n let fillIdCounter = 0;\n\n const createContext = (plugin: InternalPlugin): PluginContext => {\n /** そのプラグインの i18n 後始末を subscriptions へ積んだか(積むのは初回の addResources だけ) */\n let hasI18nCleanup = false;\n const onEvent = (type: AcmsEventType | AcmsEventType[], handler: LooseHandler): Unsubscribe => {\n const label = toEventLabel(type);\n // ゲートイベントでは cancel(reason) を持つ gateCtx が第2引数で渡る。通知イベントでは undefined のまま\n const wrapped = async (event: AcmsLooseEvent, gateCtx?: AcmsGateContext) => {\n try {\n return await handler(event, gateCtx);\n } catch (err) {\n logHandlerError(plugin.name, label, err);\n throw err;\n }\n };\n const unsubscribe = (eventsBus.on as LooseOn)(type, wrapped);\n plugin.subscriptions.push(unsubscribe);\n plugin.registrations.push({ kind: 'event', key: label });\n return unsubscribe;\n };\n\n return {\n name: plugin.name,\n config: readConfig(plugin.name),\n events: {\n on: onEvent as PluginContext['events']['on'],\n },\n unitEditor: {\n registerUnitDefinition: (name, def) => {\n const unsubscribe = unitDefinitionRegistry.add(name, def);\n plugin.subscriptions.push(unsubscribe);\n plugin.registrations.push({ kind: 'unit-definition', key: name });\n return unsubscribe;\n },\n },\n blockEditor: {\n registerBlockDefinition: (name, factory) => {\n const unsubscribe = blockDefinitionRegistry.add(name, factory);\n plugin.subscriptions.push(unsubscribe);\n plugin.registrations.push({ kind: 'block-definition', key: name });\n return unsubscribe;\n },\n registerExtension: (name, factory) => {\n const unsubscribe = blockExtensionRegistry.add(name, factory);\n plugin.subscriptions.push(unsubscribe);\n plugin.registrations.push({ kind: 'extension', key: name });\n return unsubscribe;\n },\n },\n keyboardShortcuts: {\n registerCategory: (id, category) => {\n const unsubscribe = keyboardShortcutRegistry.registerCategory(\n toKeyboardShortcutRegistrationId(plugin.name, id),\n category\n );\n plugin.subscriptions.push(unsubscribe);\n plugin.registrations.push({ kind: 'keyboard-shortcut-category', key: id });\n return unsubscribe;\n },\n registerShortcut: (id, shortcut) => {\n const unsubscribe = keyboardShortcutRegistry.registerShortcut(\n toKeyboardShortcutRegistrationId(plugin.name, id),\n {\n ...shortcut,\n categoryId: toKeyboardShortcutRegistrationId(plugin.name, shortcut.categoryId),\n }\n );\n plugin.subscriptions.push(unsubscribe);\n plugin.registrations.push({ kind: 'keyboard-shortcut', key: id });\n return unsubscribe;\n },\n },\n dataview: {\n registerColumnType: (name, type) => {\n const unsubscribe = columnTypeRegistry.add(name, type);\n plugin.subscriptions.push(unsubscribe);\n plugin.registrations.push({ kind: 'column-type', key: name });\n return unsubscribe;\n },\n registerAction: (view, name, def) => {\n const unsubscribe = dataviewActionRegistry.add(view, name, def);\n plugin.subscriptions.push(unsubscribe);\n plugin.registrations.push({ kind: 'dataview-action', key: `${view}:${name}` });\n return unsubscribe;\n },\n registerBulkAction: (view, name, def) => {\n const unsubscribe = dataviewBulkActionRegistry.add(view, name, def);\n plugin.subscriptions.push(unsubscribe);\n plugin.registrations.push({ kind: 'dataview-bulk-action', key: `${view}:${name}` });\n return unsubscribe;\n },\n registerMenu: (view, name, def) => {\n const unsubscribe = dataviewMenuRegistry.add(view, name, def);\n plugin.subscriptions.push(unsubscribe);\n plugin.registrations.push({ kind: 'dataview-menu', key: `${view}:${name}` });\n return unsubscribe;\n },\n },\n i18n: {\n // 名前空間をプラグイン名に固定する。プラグイン側で自由に決められると、コアの\n // `translation` や他プラグインとキーが衝突しうるため、名乗りは registry 側で与える。\n addResources: (lng, resources) => {\n i18nResourceRegistry.add(plugin.name, lng, resources);\n plugin.registrations.push({ kind: 'i18n', key: lng });\n // 破棄は名前空間(=プラグイン名)単位なので、何度 addResources しても後始末は 1 つでよい。\n // 呼び出し回数ぶん積むと unregister 時に remove が空振りで複数回走り、購読側の\n // forget(i18next の removeResourceBundle)も無駄に繰り返されるため、初回だけ積む。\n if (!hasI18nCleanup) {\n hasI18nCleanup = true;\n plugin.subscriptions.push(() => i18nResourceRegistry.remove(plugin.name));\n }\n },\n },\n ui: {\n fill: <T>(slotName: string, entry: FillEntry<T>) => {\n fillIdCounter += 1;\n const id = `plugin:${plugin.name}:${slotName}:${fillIdCounter}`;\n // fillStore(Slot 名ごとに異なる T を型消去した FillRenderer = FillRenderer<unknown> で\n // 保持する)へ渡す境界。Fill コンポーネント(@ablogcms/components/slot-fill)の\n // eraseFillRendererType と同じ理由・同じ境界でここでも明示キャストする。\n const render = resolveFillEntry<T>(entry) as FillRenderer;\n fillStore.upsertFill(slotName, id, render);\n const unsubscribe = () => fillStore.removeFill(slotName, id);\n plugin.subscriptions.push(unsubscribe);\n plugin.registrations.push({ kind: 'fill', key: slotName });\n return unsubscribe;\n },\n },\n };\n };\n\n const register = (name: string, definition: PluginDefinition): void => {\n if (plugins.has(name)) {\n throw new Error(`[@ablogcms/plugins] プラグイン名 \"${name}\" は既に登録されています。`);\n }\n\n const plugin: InternalPlugin = { name, version: definition.version, registrations: [], subscriptions: [] };\n // setup() 実行前に登録しておく。setup() の途中で ctx 経由の登録(例: 名前衝突での throw)が\n // 失敗した場合に catch 節でロールバックできるようにするため。\n plugins.set(name, plugin);\n\n try {\n definition.setup(createContext(plugin));\n } catch (err) {\n // 途中まで行われた登録(イベント購読・レジストリ登録・Fill)をすべて解除し、ゾンビ状態の\n // 半登録プラグインを残さない。呼び出し元は同じ名前で再度 register し直せる。\n plugin.subscriptions.forEach((unsubscribe) => unsubscribe());\n plugins.delete(name);\n throw err;\n }\n };\n\n const unregister = (name: string): boolean => {\n const plugin = plugins.get(name);\n if (!plugin) {\n return false;\n }\n plugin.subscriptions.forEach((unsubscribe) => unsubscribe());\n plugins.delete(name);\n return true;\n };\n\n const toRegisteredPlugin = (plugin: InternalPlugin): RegisteredPlugin => ({\n name: plugin.name,\n version: plugin.version,\n registrations: plugin.registrations,\n });\n\n const get = (name: string): RegisteredPlugin | undefined => {\n const plugin = plugins.get(name);\n return plugin ? toRegisteredPlugin(plugin) : undefined;\n };\n\n const getAll = (): RegisteredPlugin[] => Array.from(plugins.values()).map(toRegisteredPlugin);\n\n return { register, unregister, get, getAll, withFillProps };\n}\n","import type { Registry, Unsubscribe } from './types';\n\n/**\n * 名前衝突を検知する汎用レジストリの純粋実装。`ctx.unitEditor.registerUnitDefinition` /\n * `ctx.dataview.registerColumnType` の土台(Directus の `defineInterface` 等と同型)。\n *\n * `add()` が返す `Unsubscribe` は、登録時点の値を閉じ込めて比較してから削除する。\n * 「解除 → 同名で再登録」の後に古い `Unsubscribe` が誤って新しい登録を消してしまう事故を防ぐ\n * (`name` は一意だが、`Unsubscribe` のライフサイクルは呼び出し側が保持し続けるため)。\n */\nexport function createRegistry<T>(label: string): Registry<T> {\n const items = new Map<string, T>();\n\n const add = (name: string, item: T): Unsubscribe => {\n if (items.has(name)) {\n throw new Error(`[@ablogcms/plugins] ${label} \"${name}\" は既に登録されています。`);\n }\n items.set(name, item);\n return () => {\n if (items.get(name) === item) {\n items.delete(name);\n }\n };\n };\n\n const remove = (name: string): boolean => items.delete(name);\n\n const get = (name: string): T | undefined => items.get(name);\n\n const getAll = (): Record<string, T> => Object.fromEntries(items);\n\n return { add, remove, get, getAll };\n}\n","import type { Registry, Unsubscribe } from './types';\nimport { createRegistry } from './create-registry';\n\n/**\n * `view`(`'entry' | 'module'` 等、将来拡張可能な文字列)ごとに独立した名前空間を持つレジストリ。\n * `ctx.dataview.registerAction(view, name, def)` の土台(design.md Q4「view キー方式」)。\n *\n * 内部では `view` ごとに素の {@link createRegistry} インスタンスを遅延生成して保持する\n * (文字列連結キー方式にしない理由: view 名に区切り文字が含まれた場合の衝突を避けるため)。\n */\nexport interface ViewedRegistry<T> {\n /** `view` 内で `name` が既に登録済みの場合は throw する */\n add(view: string, name: string, item: T): Unsubscribe;\n remove(view: string, name: string): boolean;\n get(view: string, name: string): T | undefined;\n /** 指定した `view` に登録された項目のみを返す。未登録の `view` は空オブジェクト */\n getAll(view: string): Record<string, T>;\n}\n\nexport function createViewedRegistry<T>(label: string): ViewedRegistry<T> {\n const registries = new Map<string, Registry<T>>();\n\n const registryFor = (view: string): Registry<T> => {\n let registry = registries.get(view);\n if (!registry) {\n registry = createRegistry<T>(`${label}(${view})`);\n registries.set(view, registry);\n }\n return registry;\n };\n\n return {\n add: (view, name, item) => registryFor(view).add(name, item),\n remove: (view, name) => registryFor(view).remove(name),\n get: (view, name) => registryFor(view).get(name),\n getAll: (view) => registries.get(view)?.getAll() ?? {},\n };\n}\n","import { createElement, type ComponentType, type FunctionComponent, type ReactNode } from 'react';\nimport { DomFillMount } from '@ablogcms/components/slot-fill';\nimport type { FillRenderer } from '@ablogcms/components/slot-fill';\nimport { isFillComponentEntry } from './with-fill-props';\nimport type { DomFill, FillEntry } from './types';\n\n/**\n * `ctx.ui.fill(slotName, entry)` の `entry` を、Fill ストア(`@ablogcms/components/slot-fill`)に\n * そのまま渡せる `FillRenderer`(`ReactNode`、または `Slot` の `fillProps` を受け取る render-prop\n * 関数)に変換する。\n *\n * 判別は 3 種類:\n * 1. `withFillProps(Component)` が返すマーカーオブジェクト(`FillComponentEntry`)→ `fillProps` を\n * 受け取る render-prop 関数を返す(下記参照)。\n * 2. 仮引数 1 個以上の関数 → `DomFill` として `DomFillMount` でラップする。\n * 3. それ以外(仮引数 0 個の関数)→ props なしの React コンポーネントとして描画する。\n *\n * 2/3 の判別は Why not: 実行時には両者とも「関数」という点で構造的に区別できず\n * (`React.ComponentType` も `DomFill` も型注釈はコンパイル時に消える)、関数名(`.name`)も\n * 拠り所にならない(`vi.fn()` 等のラッパーを経由すると `.name` が `\"Mock\"` 等に上書きされ、\n * テストのモックが大文字始まりになって誤判定するのを実測で確認した)。代わりに\n * `Function.prototype.length`(宣言された仮引数の個数)で判別する(`vi.fn()` 等でラップされても\n * 元関数の `.length` は保たれる)。この 0/1 個の判別は既存の後方互換のため変更しない。\n *\n * 1 のケースをこの判別の外に出している Why not: props を要求するコンポーネント\n * (`({ editor, unit }) => ...`)は仮引数が 1 個になり、`DomFill`(`(el) => ...`)と実行時に\n * 区別できない。`withFillProps` はオブジェクトでラップするため、`typeof entry === 'function'` の\n * 分岐に到達する前に見分けがつき、既存の 0/1 個判別と衝突しない。\n */\nexport function resolveFillEntry(entry: ComponentType | DomFill): ReactNode;\nexport function resolveFillEntry<T>(entry: FillEntry<T>): FillRenderer<T>;\nexport function resolveFillEntry<T>(entry: FillEntry<T>): FillRenderer<T> {\n if (isFillComponentEntry<T>(entry)) {\n // createElement<P>(FunctionComponent<P>, props) はここで P=T を推論できない(T は\n // resolveFillEntry 自身のジェネリックで、createElement 側から見ると「{} と無関係かもしれない\n // 任意の型」にしか見えないため、TS2769 になる。呼び出し境界でのみ型消去する\n // (fill.tsx の eraseFillRendererType と同じ「型消去の境界であることを明示するキャスト」)。\n const Component = entry.Component as FunctionComponent<Record<string, unknown>>;\n // Slot 側(renderFill)は render が関数なら fillProps を渡して呼ぶ実装を既に持つため、\n // ここでは呼び出しを fillProps が判明する Slot の描画タイミングまで遅延させるだけでよい。\n // eslint-disable-next-line react/display-name -- コンポーネント定義ではなく FillRenderer を返す関数(createElement 呼び出しを含むアロー関数がヒューリスティックに誤検知される)\n return (fillProps: T) => createElement(Component, fillProps as Record<string, unknown>);\n }\n if (typeof entry === 'function' && entry.length > 0) {\n return createElement(DomFillMount, { mount: entry as DomFill });\n }\n return createElement(entry as ComponentType);\n}\n","import type { Registry } from './types';\n\n/**\n * コア側の組み込み定義(`core`)と、プラグインが `Registry.add`(`ctx.unitEditor.registerUnitDefinition` /\n * `ctx.dataview.registerColumnType` 経由)で登録した内容をマージする。\n *\n * react-19-migration フェーズ3-1a/3-1b で設計した `unitDefs` / `columnTypes` prop の値計算を\n * レジストリ経由に変える配線(design.md「レジストリ(機能登録)」節)の共通実装。\n * キー衝突時はレジストリ(プラグイン)側を優先する(design.md の例\n * `{ ...coreUnitDefs, ...unitDefinitionRegistry.getAll() }` と同じマージ順)。\n */\nexport function mergeRegistryEntries<T>(core: Record<string, T>, registry: Registry<T>): Record<string, T> {\n return { ...core, ...registry.getAll() };\n}\n","/**\n * ページ全体で 1 つだけ存在するべきシングルトン群。\n *\n * 管理画面は dispatcher ごとに独立した React ルートを `createRoot` する構成\n * (`@ablogcms/react-utils` の `render()`)のため、単一の `<SlotFillProvider>` でページ全体を\n * 包むことができない。`ctx.ui.fill` が書き込む Fill ブリッジをここでシングルトン化しておき、\n * 各ツリーの `<SlotFillProvider store={getGlobalFillBridge()}>` がこれを購読することで、\n * どの独立ツリーの `<Slot>` にも同じ登録内容が届く(design.md「グローバル Fill ブリッジ」節)。\n *\n * `unitDefs` / `columnTypes` prop の値計算(react-19-migration フェーズ3-1a/3-1b)も同じ理由で\n * dispatch 側の複数箇所から同一レジストリを参照する必要があるため、ここでシングルトン化する\n * (実際の prop 配線はフェーズD)。\n *\n * `createPluginRegistry()` 自体は各テストが独立したインスタンスで動かせるよう、これらに依存しない\n * 素の工場関数のまま保つ(`@ablogcms/events` の `createEventBus()` と同じ設計)。\n * 本番の結線(admin.js)でのみ、ここのシングルトンを明示的に注入する。\n */\nimport { createSlotFillStore, type SlotFillStore } from '@ablogcms/components/slot-fill';\nimport type { Action, BulkAction, ColumnTypeDef, Menu, RowData } from '@ablogcms/components/dataview/types';\nimport type { UnitDefInterface } from '@ablogcms/unit-editor/core/types/unit';\nimport type { BlockDefinition } from '@ablogcms/block-editor/types';\nimport type { AnyExtension } from '@ablogcms/block-editor/tiptap/core';\nimport type { Tiptap } from '@ablogcms/block-editor/tiptap';\nimport { createRegistry } from './create-registry';\nimport { createViewedRegistry, type ViewedRegistry } from './create-viewed-registry';\nimport type { Registry } from './types';\n\n/**\n * `ctx.blockEditor.registerBlockDefinition` / `registerExtension` が登録するファクトリの型。\n *\n * Promise を返してもよい(解決側 `features/block-editor/block-definitions.ts` が await する)。\n * 公式 Tiptap 拡張(`@tiptap/extension-*`)はモジュール評価時に Tiptap のクラスを使うため、\n * プラグインはファクトリの中で `await import(...)` して評価自体をチャンクロード後まで遅らせる。\n */\nexport type BlockDefinitionFactory = (tiptap: Tiptap) => BlockDefinition | Promise<BlockDefinition>;\nexport type BlockExtensionFactory = (\n tiptap: Tiptap\n) => AnyExtension | AnyExtension[] | Promise<AnyExtension | AnyExtension[]>;\n\nlet fillBridge: SlotFillStore | undefined;\n\nexport function getGlobalFillBridge(): SlotFillStore {\n if (!fillBridge) {\n fillBridge = createSlotFillStore();\n }\n return fillBridge;\n}\n\nlet unitDefinitionRegistry: Registry<UnitDefInterface> | undefined;\n\nexport function getUnitDefinitionRegistry(): Registry<UnitDefInterface> {\n if (!unitDefinitionRegistry) {\n unitDefinitionRegistry = createRegistry<UnitDefInterface>('unit-definition');\n }\n return unitDefinitionRegistry;\n}\n\nlet columnTypeRegistry: Registry<ColumnTypeDef> | undefined;\n\nexport function getColumnTypeRegistry(): Registry<ColumnTypeDef> {\n if (!columnTypeRegistry) {\n columnTypeRegistry = createRegistry<ColumnTypeDef>('column-type');\n }\n return columnTypeRegistry;\n}\n\nlet blockDefinitionRegistry: Registry<BlockDefinitionFactory> | undefined;\n\nexport function getBlockDefinitionRegistry(): Registry<BlockDefinitionFactory> {\n if (!blockDefinitionRegistry) {\n blockDefinitionRegistry = createRegistry<BlockDefinitionFactory>('block-definition');\n }\n return blockDefinitionRegistry;\n}\n\nlet blockExtensionRegistry: Registry<BlockExtensionFactory> | undefined;\n\nexport function getBlockExtensionRegistry(): Registry<BlockExtensionFactory> {\n if (!blockExtensionRegistry) {\n blockExtensionRegistry = createRegistry<BlockExtensionFactory>('extension');\n }\n return blockExtensionRegistry;\n}\n\nlet dataviewActionRegistry: ViewedRegistry<Action<RowData>> | undefined;\n\nexport function getDataviewActionRegistry(): ViewedRegistry<Action<RowData>> {\n if (!dataviewActionRegistry) {\n dataviewActionRegistry = createViewedRegistry<Action<RowData>>('dataview-action');\n }\n return dataviewActionRegistry;\n}\n\nlet dataviewBulkActionRegistry: ViewedRegistry<BulkAction<RowData>> | undefined;\n\nexport function getDataviewBulkActionRegistry(): ViewedRegistry<BulkAction<RowData>> {\n if (!dataviewBulkActionRegistry) {\n dataviewBulkActionRegistry = createViewedRegistry<BulkAction<RowData>>('dataview-bulk-action');\n }\n return dataviewBulkActionRegistry;\n}\n\nlet dataviewMenuRegistry: ViewedRegistry<Menu<RowData>> | undefined;\n\nexport function getDataviewMenuRegistry(): ViewedRegistry<Menu<RowData>> {\n if (!dataviewMenuRegistry) {\n dataviewMenuRegistry = createViewedRegistry<Menu<RowData>>('dataview-menu');\n }\n return dataviewMenuRegistry;\n}\n"],"mappings":"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","names":["unitDefinitionRegistry","columnTypeRegistry","blockDefinitionRegistry","blockExtensionRegistry","dataviewActionRegistry","dataviewBulkActionRegistry","dataviewMenuRegistry","createSlotFillStore","createSlotFillStore"]}
1
+ {"version":3,"sources":["../../src/core/create-plugin-registry.ts","../../src/core/create-registry.ts","../../src/core/create-viewed-registry.ts","../../src/core/resolve-fill-entry.ts","../../src/core/merge-registry-entries.ts","../../src/core/global-singletons.ts"],"sourcesContent":["import type { AcmsEventType, AcmsEvents, AcmsGateContext, AcmsLooseEvent, Unsubscribe } from '@ablogcms/events';\nimport { createEventBus } from '@ablogcms/events/core';\nimport { createSlotFillStore, type SlotFillStore, type FillRenderer } from '@ablogcms/components/slot-fill';\nimport type { Action, BulkAction, ColumnTypeDef, Menu, RowData } from '@ablogcms/components/dataview/types';\nimport type { UnitDefInterface } from '@ablogcms/unit-editor/core/types/unit';\nimport { createKeyboardShortcutRegistry } from '@ablogcms/keyboard-shortcut';\nimport type { KeyboardShortcutRegistry } from '@ablogcms/keyboard-shortcut';\nimport { createI18nResourceRegistry, type I18nResourceRegistry } from '@ablogcms/i18n/core';\nimport { createRegistry } from './create-registry';\nimport { createViewedRegistry, type ViewedRegistry } from './create-viewed-registry';\nimport { resolveFillEntry } from './resolve-fill-entry';\nimport { withFillProps } from './with-fill-props';\nimport type { BlockDefinitionFactory, BlockExtensionFactory } from './global-singletons';\nimport type {\n AcmsPlugins,\n FillEntry,\n PluginContext,\n PluginDefinition,\n RegisteredEntry,\n RegisteredPlugin,\n Registry,\n} from './types';\n\nexport interface CreatePluginRegistryOptions {\n /** 委譲先のイベントバス。省略時は自己完結したインスタンスを新規作成する(テスト用の既定値) */\n eventsBus?: AcmsEvents;\n /** `ctx.ui.fill` の書き込み先。省略時は自己完結したインスタンスを新規作成する(テスト用の既定値) */\n fillStore?: SlotFillStore;\n /** `ctx.unitEditor.registerUnitDefinition` の登録先。省略時は自己完結したインスタンスを新規作成する */\n unitDefinitionRegistry?: Registry<UnitDefInterface>;\n /** `ctx.dataview.registerColumnType` の登録先。省略時は自己完結したインスタンスを新規作成する */\n columnTypeRegistry?: Registry<ColumnTypeDef>;\n /** `ctx.blockEditor.registerBlockDefinition` の登録先。省略時は自己完結したインスタンスを新規作成する */\n blockDefinitionRegistry?: Registry<BlockDefinitionFactory>;\n /** `ctx.blockEditor.registerExtension` の登録先。省略時は自己完結したインスタンスを新規作成する */\n blockExtensionRegistry?: Registry<BlockExtensionFactory>;\n /** `ctx.keyboardShortcuts` の登録先。省略時は自己完結したインスタンスを新規作成する */\n keyboardShortcutRegistry?: KeyboardShortcutRegistry;\n\n /** `ctx.i18n` の登録先。省略時は自己完結したインスタンスを新規作成する */\n i18nResourceRegistry?: I18nResourceRegistry;\n /** `ctx.dataview.registerAction` の登録先。省略時は自己完結したインスタンスを新規作成する */\n dataviewActionRegistry?: ViewedRegistry<Action<RowData>>;\n /** `ctx.dataview.registerBulkAction` の登録先。省略時は自己完結したインスタンスを新規作成する */\n dataviewBulkActionRegistry?: ViewedRegistry<BulkAction<RowData>>;\n /** `ctx.dataview.registerMenu` の登録先。省略時は自己完結したインスタンスを新規作成する */\n dataviewMenuRegistry?: ViewedRegistry<Menu<RowData>>;\n}\n\ninterface InternalPlugin {\n name: string;\n version?: string;\n registrations: RegisteredEntry[];\n subscriptions: Unsubscribe[];\n}\n\n// AcmsEvents#on は型引数 K ごとにハンドラの戻り値を絞り込むため、type がユニオン\n// (AcmsEventType | AcmsEventType[])のまま渡すとハンドラの戻り値を厳密に検証できない。\n// create-event-bus.ts と同じく、内部では緩い型で扱い、公開 API の境界(PluginContext)でのみ\n// 厳密な型へキャストする。\ntype LooseHandler = (event: AcmsLooseEvent, gateCtx?: AcmsGateContext) => unknown;\ntype LooseOn = (type: AcmsEventType | AcmsEventType[], handler: LooseHandler) => Unsubscribe;\n\n// プラグイン起因の例外をどのプラグインが投げたか特定できるよう帰属を明示する。\n// イベントバス自身も汎用メッセージで同じ例外をログするため、この行は「どのプラグインか」を示す追加の帰属ログになる。\nconst logHandlerError = (pluginName: string, eventLabel: string, err: unknown): void => {\n console.error(`[@ablogcms/plugins] プラグイン \"${pluginName}\" のハンドラ(\"${eventLabel}\")が例外を投げました。`, err);\n};\n\nconst toEventLabel = (type: AcmsEventType | AcmsEventType[]): string => (Array.isArray(type) ? type.join(',') : type);\n\nconst toKeyboardShortcutRegistrationId = (pluginName: string, id: string): string => `plugin:${pluginName}:${id}`;\n\n/**\n * `window.ACMS.Config` からプラグインの設定値を読む。プラグイン名は `-`/`_` 区切りのことが多いため、\n * `jsModule()` 側の慣例(キャメルケースキー)に合わせて変換する。未初期化・未設定時は空オブジェクトを返し、\n * `setup(ctx)` 内で `ctx.config` を参照しただけで例外にならないようにする。\n */\nconst readConfig = (pluginName: string): Record<string, unknown> => {\n if (typeof window === 'undefined') {\n return {};\n }\n const camelName = pluginName.replace(/[-_](\\w)/g, (_match, chr: string) => chr.toUpperCase());\n const config = (window as unknown as { ACMS?: { Config?: Record<string, unknown> } }).ACMS?.Config;\n const value = config?.[camelName];\n return typeof value === 'object' && value !== null ? (value as Record<string, unknown>) : {};\n};\n\n/**\n * `ACMS.plugins` の純粋実装(グローバル非依存)。VS Code の `activate(context)` +\n * `context.subscriptions` を範に、`setup(ctx)` 内でのイベント購読・レジストリ登録・UI 差し込みを\n * すべて内部の subscriptions 配列に集約し、`unregister(name)` で一括自動回収する。\n *\n * 依存(イベントバス・Fill ブリッジ・各レジストリ)はすべて注入可能で、省略時は自己完結した\n * インスタンスを新規作成する(`@ablogcms/events` の `createEventBus()` と同じ設計。グローバル\n * シングルトンへの結線は本番の bootstrap(admin.js)が `global-singletons.ts` 経由で明示的に行う)。\n */\nexport function createPluginRegistry(options: CreatePluginRegistryOptions = {}): AcmsPlugins {\n const eventsBus = options.eventsBus ?? createEventBus();\n const fillStore = options.fillStore ?? createSlotFillStore();\n const unitDefinitionRegistry = options.unitDefinitionRegistry ?? createRegistry<UnitDefInterface>('unit-definition');\n const columnTypeRegistry = options.columnTypeRegistry ?? createRegistry<ColumnTypeDef>('column-type');\n const blockDefinitionRegistry =\n options.blockDefinitionRegistry ?? createRegistry<BlockDefinitionFactory>('block-definition');\n const blockExtensionRegistry = options.blockExtensionRegistry ?? createRegistry<BlockExtensionFactory>('extension');\n const keyboardShortcutRegistry = options.keyboardShortcutRegistry ?? createKeyboardShortcutRegistry();\n const i18nResourceRegistry = options.i18nResourceRegistry ?? createI18nResourceRegistry();\n const dataviewActionRegistry =\n options.dataviewActionRegistry ?? createViewedRegistry<Action<RowData>>('dataview-action');\n const dataviewBulkActionRegistry =\n options.dataviewBulkActionRegistry ?? createViewedRegistry<BulkAction<RowData>>('dataview-bulk-action');\n const dataviewMenuRegistry = options.dataviewMenuRegistry ?? createViewedRegistry<Menu<RowData>>('dataview-menu');\n\n const plugins = new Map<string, InternalPlugin>();\n let fillIdCounter = 0;\n\n const createContext = (plugin: InternalPlugin): PluginContext => {\n /** そのプラグインの i18n 後始末を subscriptions へ積んだか(積むのは初回の addResources だけ) */\n let hasI18nCleanup = false;\n const onEvent = (type: AcmsEventType | AcmsEventType[], handler: LooseHandler): Unsubscribe => {\n const label = toEventLabel(type);\n // ゲートイベントでは cancel(reason) を持つ gateCtx が第2引数で渡る。通知イベントでは undefined のまま\n const wrapped = async (event: AcmsLooseEvent, gateCtx?: AcmsGateContext) => {\n try {\n return await handler(event, gateCtx);\n } catch (err) {\n logHandlerError(plugin.name, label, err);\n throw err;\n }\n };\n const unsubscribe = (eventsBus.on as LooseOn)(type, wrapped);\n plugin.subscriptions.push(unsubscribe);\n plugin.registrations.push({ kind: 'event', key: label });\n return unsubscribe;\n };\n\n return {\n name: plugin.name,\n config: readConfig(plugin.name),\n events: {\n on: onEvent as PluginContext['events']['on'],\n },\n unitEditor: {\n registerUnitDefinition: (name, def) => {\n const unsubscribe = unitDefinitionRegistry.add(name, def);\n plugin.subscriptions.push(unsubscribe);\n plugin.registrations.push({ kind: 'unit-definition', key: name });\n return unsubscribe;\n },\n },\n blockEditor: {\n registerBlockDefinition: (name, factory) => {\n const unsubscribe = blockDefinitionRegistry.add(name, factory);\n plugin.subscriptions.push(unsubscribe);\n plugin.registrations.push({ kind: 'block-definition', key: name });\n return unsubscribe;\n },\n registerExtension: (name, factory) => {\n const unsubscribe = blockExtensionRegistry.add(name, factory);\n plugin.subscriptions.push(unsubscribe);\n plugin.registrations.push({ kind: 'extension', key: name });\n return unsubscribe;\n },\n },\n keyboardShortcuts: {\n registerCategory: (id, category) => {\n const unsubscribe = keyboardShortcutRegistry.registerCategory(\n toKeyboardShortcutRegistrationId(plugin.name, id),\n category\n );\n plugin.subscriptions.push(unsubscribe);\n plugin.registrations.push({ kind: 'keyboard-shortcut-category', key: id });\n return unsubscribe;\n },\n registerShortcut: (id, shortcut) => {\n const unsubscribe = keyboardShortcutRegistry.registerShortcut(\n toKeyboardShortcutRegistrationId(plugin.name, id),\n {\n ...shortcut,\n categoryId: toKeyboardShortcutRegistrationId(plugin.name, shortcut.categoryId),\n }\n );\n plugin.subscriptions.push(unsubscribe);\n plugin.registrations.push({ kind: 'keyboard-shortcut', key: id });\n return unsubscribe;\n },\n },\n dataview: {\n registerColumnType: (name, type) => {\n const unsubscribe = columnTypeRegistry.add(name, type);\n plugin.subscriptions.push(unsubscribe);\n plugin.registrations.push({ kind: 'column-type', key: name });\n return unsubscribe;\n },\n registerAction: (view, name, def) => {\n const unsubscribe = dataviewActionRegistry.add(view, name, def);\n plugin.subscriptions.push(unsubscribe);\n plugin.registrations.push({ kind: 'dataview-action', key: `${view}:${name}` });\n return unsubscribe;\n },\n registerBulkAction: (view, name, def) => {\n const unsubscribe = dataviewBulkActionRegistry.add(view, name, def);\n plugin.subscriptions.push(unsubscribe);\n plugin.registrations.push({ kind: 'dataview-bulk-action', key: `${view}:${name}` });\n return unsubscribe;\n },\n registerMenu: (view, name, def) => {\n const unsubscribe = dataviewMenuRegistry.add(view, name, def);\n plugin.subscriptions.push(unsubscribe);\n plugin.registrations.push({ kind: 'dataview-menu', key: `${view}:${name}` });\n return unsubscribe;\n },\n },\n i18n: {\n // 名前空間をプラグイン名に固定する。プラグイン側で自由に決められると、コアの\n // `translation` や他プラグインとキーが衝突しうるため、名乗りは registry 側で与える。\n addResources: (lng, resources) => {\n i18nResourceRegistry.add(plugin.name, lng, resources);\n plugin.registrations.push({ kind: 'i18n', key: lng });\n // 破棄は名前空間(=プラグイン名)単位なので、何度 addResources しても後始末は 1 つでよい。\n // 呼び出し回数ぶん積むと unregister 時に remove が空振りで複数回走り、購読側の\n // forget(i18next の removeResourceBundle)も無駄に繰り返されるため、初回だけ積む。\n if (!hasI18nCleanup) {\n hasI18nCleanup = true;\n plugin.subscriptions.push(() => i18nResourceRegistry.remove(plugin.name));\n }\n },\n },\n ui: {\n fill: <T>(slotName: string, entry: FillEntry<T>) => {\n fillIdCounter += 1;\n const id = `plugin:${plugin.name}:${slotName}:${fillIdCounter}`;\n // fillStore(Slot 名ごとに異なる T を型消去した FillRenderer = FillRenderer<unknown> で\n // 保持する)へ渡す境界。Fill コンポーネント(@ablogcms/components/slot-fill)の\n // eraseFillRendererType と同じ理由・同じ境界でここでも明示キャストする。\n const render = resolveFillEntry<T>(entry) as FillRenderer;\n fillStore.upsertFill(slotName, id, render);\n const unsubscribe = () => fillStore.removeFill(slotName, id);\n plugin.subscriptions.push(unsubscribe);\n plugin.registrations.push({ kind: 'fill', key: slotName });\n return unsubscribe;\n },\n },\n };\n };\n\n const register = (name: string, definition: PluginDefinition): void => {\n if (plugins.has(name)) {\n throw new Error(`[@ablogcms/plugins] プラグイン名 \"${name}\" は既に登録されています。`);\n }\n\n const plugin: InternalPlugin = { name, version: definition.version, registrations: [], subscriptions: [] };\n // setup() 実行前に登録しておく。setup() の途中で ctx 経由の登録(例: 名前衝突での throw)が\n // 失敗した場合に catch 節でロールバックできるようにするため。\n plugins.set(name, plugin);\n\n try {\n definition.setup(createContext(plugin));\n } catch (err) {\n // 途中まで行われた登録(イベント購読・レジストリ登録・Fill)をすべて解除し、ゾンビ状態の\n // 半登録プラグインを残さない。呼び出し元は同じ名前で再度 register し直せる。\n plugin.subscriptions.forEach((unsubscribe) => unsubscribe());\n plugins.delete(name);\n throw err;\n }\n };\n\n const unregister = (name: string): boolean => {\n const plugin = plugins.get(name);\n if (!plugin) {\n return false;\n }\n plugin.subscriptions.forEach((unsubscribe) => unsubscribe());\n plugins.delete(name);\n return true;\n };\n\n const toRegisteredPlugin = (plugin: InternalPlugin): RegisteredPlugin => ({\n name: plugin.name,\n version: plugin.version,\n registrations: plugin.registrations,\n });\n\n const get = (name: string): RegisteredPlugin | undefined => {\n const plugin = plugins.get(name);\n return plugin ? toRegisteredPlugin(plugin) : undefined;\n };\n\n const getAll = (): RegisteredPlugin[] => Array.from(plugins.values()).map(toRegisteredPlugin);\n\n return { register, unregister, get, getAll, withFillProps };\n}\n","import type { Registry, Unsubscribe } from './types';\n\n/**\n * 名前衝突を検知する汎用レジストリの純粋実装。`ctx.unitEditor.registerUnitDefinition` /\n * `ctx.dataview.registerColumnType` の土台(Directus の `defineInterface` 等と同型)。\n *\n * `add()` が返す `Unsubscribe` は、登録時点の値を閉じ込めて比較してから削除する。\n * 「解除 → 同名で再登録」の後に古い `Unsubscribe` が誤って新しい登録を消してしまう事故を防ぐ\n * (`name` は一意だが、`Unsubscribe` のライフサイクルは呼び出し側が保持し続けるため)。\n */\nexport function createRegistry<T>(label: string): Registry<T> {\n const items = new Map<string, T>();\n const listeners = new Set<() => void>();\n\n // 通知中に購読が増減しても通知対象がぶれないよう、コピーに対して回す\n const notify = () => {\n [...listeners].forEach((listener) => listener());\n };\n\n const add = (name: string, item: T): Unsubscribe => {\n if (items.has(name)) {\n throw new Error(`[@ablogcms/plugins] ${label} \"${name}\" は既に登録されています。`);\n }\n items.set(name, item);\n notify();\n return () => {\n if (items.get(name) === item) {\n items.delete(name);\n notify();\n }\n };\n };\n\n const remove = (name: string): boolean => {\n const removed = items.delete(name);\n if (removed) {\n notify();\n }\n return removed;\n };\n\n const get = (name: string): T | undefined => items.get(name);\n\n const getAll = (): Record<string, T> => Object.fromEntries(items);\n\n const subscribe = (listener: () => void): Unsubscribe => {\n listeners.add(listener);\n return () => {\n listeners.delete(listener);\n };\n };\n\n return { add, remove, get, getAll, subscribe };\n}\n","import type { Registry, Unsubscribe } from './types';\nimport { createRegistry } from './create-registry';\n\n/**\n * `view`(`'entry' | 'module'` 等、将来拡張可能な文字列)ごとに独立した名前空間を持つレジストリ。\n * `ctx.dataview.registerAction(view, name, def)` の土台(design.md Q4「view キー方式」)。\n *\n * 内部では `view` ごとに素の {@link createRegistry} インスタンスを遅延生成して保持する\n * (文字列連結キー方式にしない理由: view 名に区切り文字が含まれた場合の衝突を避けるため)。\n */\nexport interface ViewedRegistry<T> {\n /** `view` 内で `name` が既に登録済みの場合は throw する */\n add(view: string, name: string, item: T): Unsubscribe;\n remove(view: string, name: string): boolean;\n get(view: string, name: string): T | undefined;\n /** 指定した `view` に登録された項目のみを返す。未登録の `view` は空オブジェクト */\n getAll(view: string): Record<string, T>;\n}\n\nexport function createViewedRegistry<T>(label: string): ViewedRegistry<T> {\n const registries = new Map<string, Registry<T>>();\n\n const registryFor = (view: string): Registry<T> => {\n let registry = registries.get(view);\n if (!registry) {\n registry = createRegistry<T>(`${label}(${view})`);\n registries.set(view, registry);\n }\n return registry;\n };\n\n return {\n add: (view, name, item) => registryFor(view).add(name, item),\n remove: (view, name) => registryFor(view).remove(name),\n get: (view, name) => registryFor(view).get(name),\n getAll: (view) => registries.get(view)?.getAll() ?? {},\n };\n}\n","import { createElement, type ComponentType, type FunctionComponent, type ReactNode } from 'react';\nimport { DomFillMount } from '@ablogcms/components/slot-fill';\nimport type { FillRenderer } from '@ablogcms/components/slot-fill';\nimport { isFillComponentEntry } from './with-fill-props';\nimport type { DomFill, FillEntry } from './types';\n\n/**\n * `ctx.ui.fill(slotName, entry)` の `entry` を、Fill ストア(`@ablogcms/components/slot-fill`)に\n * そのまま渡せる `FillRenderer`(`ReactNode`、または `Slot` の `fillProps` を受け取る render-prop\n * 関数)に変換する。\n *\n * 判別は 3 種類:\n * 1. `withFillProps(Component)` が返すマーカーオブジェクト(`FillComponentEntry`)→ `fillProps` を\n * 受け取る render-prop 関数を返す(下記参照)。\n * 2. 仮引数 1 個以上の関数 → `DomFill` として `DomFillMount` でラップする。\n * 3. それ以外(仮引数 0 個の関数)→ props なしの React コンポーネントとして描画する。\n *\n * 2/3 の判別は Why not: 実行時には両者とも「関数」という点で構造的に区別できず\n * (`React.ComponentType` も `DomFill` も型注釈はコンパイル時に消える)、関数名(`.name`)も\n * 拠り所にならない(`vi.fn()` 等のラッパーを経由すると `.name` が `\"Mock\"` 等に上書きされ、\n * テストのモックが大文字始まりになって誤判定するのを実測で確認した)。代わりに\n * `Function.prototype.length`(宣言された仮引数の個数)で判別する(`vi.fn()` 等でラップされても\n * 元関数の `.length` は保たれる)。この 0/1 個の判別は既存の後方互換のため変更しない。\n *\n * 1 のケースをこの判別の外に出している Why not: props を要求するコンポーネント\n * (`({ editor, unit }) => ...`)は仮引数が 1 個になり、`DomFill`(`(el) => ...`)と実行時に\n * 区別できない。`withFillProps` はオブジェクトでラップするため、`typeof entry === 'function'` の\n * 分岐に到達する前に見分けがつき、既存の 0/1 個判別と衝突しない。\n */\nexport function resolveFillEntry(entry: ComponentType | DomFill): ReactNode;\nexport function resolveFillEntry<T>(entry: FillEntry<T>): FillRenderer<T>;\nexport function resolveFillEntry<T>(entry: FillEntry<T>): FillRenderer<T> {\n if (isFillComponentEntry<T>(entry)) {\n // createElement<P>(FunctionComponent<P>, props) はここで P=T を推論できない(T は\n // resolveFillEntry 自身のジェネリックで、createElement 側から見ると「{} と無関係かもしれない\n // 任意の型」にしか見えないため、TS2769 になる。呼び出し境界でのみ型消去する\n // (fill.tsx の eraseFillRendererType と同じ「型消去の境界であることを明示するキャスト」)。\n const Component = entry.Component as FunctionComponent<Record<string, unknown>>;\n // Slot 側(renderFill)は render が関数なら fillProps を渡して呼ぶ実装を既に持つため、\n // ここでは呼び出しを fillProps が判明する Slot の描画タイミングまで遅延させるだけでよい。\n // eslint-disable-next-line react/display-name -- コンポーネント定義ではなく FillRenderer を返す関数(createElement 呼び出しを含むアロー関数がヒューリスティックに誤検知される)\n return (fillProps: T) => createElement(Component, fillProps as Record<string, unknown>);\n }\n if (typeof entry === 'function' && entry.length > 0) {\n return createElement(DomFillMount, { mount: entry as DomFill });\n }\n return createElement(entry as ComponentType);\n}\n","import type { Registry } from './types';\n\n/**\n * コア側の組み込み定義(`core`)と、プラグインが `Registry.add`(`ctx.unitEditor.registerUnitDefinition` /\n * `ctx.dataview.registerColumnType` 経由)で登録した内容をマージする。\n *\n * react-19-migration フェーズ3-1a/3-1b で設計した `unitDefs` / `columnTypes` prop の値計算を\n * レジストリ経由に変える配線(design.md「レジストリ(機能登録)」節)の共通実装。\n * キー衝突時はレジストリ(プラグイン)側を優先する(design.md の例\n * `{ ...coreUnitDefs, ...unitDefinitionRegistry.getAll() }` と同じマージ順)。\n */\nexport function mergeRegistryEntries<T>(core: Record<string, T>, registry: Registry<T>): Record<string, T> {\n return { ...core, ...registry.getAll() };\n}\n","/**\n * ページ全体で 1 つだけ存在するべきシングルトン群。\n *\n * 管理画面は dispatcher ごとに独立した React ルートを `createRoot` する構成\n * (`@ablogcms/react-utils` の `render()`)のため、単一の `<SlotFillProvider>` でページ全体を\n * 包むことができない。`ctx.ui.fill` が書き込む Fill ブリッジをここでシングルトン化しておき、\n * 各ツリーの `<SlotFillProvider store={getGlobalFillBridge()}>` がこれを購読することで、\n * どの独立ツリーの `<Slot>` にも同じ登録内容が届く(design.md「グローバル Fill ブリッジ」節)。\n *\n * `unitDefs` / `columnTypes` prop の値計算(react-19-migration フェーズ3-1a/3-1b)も同じ理由で\n * dispatch 側の複数箇所から同一レジストリを参照する必要があるため、ここでシングルトン化する\n * (実際の prop 配線はフェーズD)。\n *\n * `createPluginRegistry()` 自体は各テストが独立したインスタンスで動かせるよう、これらに依存しない\n * 素の工場関数のまま保つ(`@ablogcms/events` の `createEventBus()` と同じ設計)。\n * 本番の結線(admin.js)でのみ、ここのシングルトンを明示的に注入する。\n */\nimport { createSlotFillStore, type SlotFillStore } from '@ablogcms/components/slot-fill';\nimport type { Action, BulkAction, ColumnTypeDef, Menu, RowData } from '@ablogcms/components/dataview/types';\nimport type { UnitDefInterface } from '@ablogcms/unit-editor/core/types/unit';\nimport type { BlockDefinition } from '@ablogcms/block-editor/types';\nimport type { AnyExtension } from '@ablogcms/block-editor/tiptap/core';\nimport type { Tiptap } from '@ablogcms/block-editor/tiptap';\nimport { createRegistry } from './create-registry';\nimport { createViewedRegistry, type ViewedRegistry } from './create-viewed-registry';\nimport type { Registry } from './types';\n\n/**\n * `ctx.blockEditor.registerBlockDefinition` / `registerExtension` が登録するファクトリの型。\n *\n * Promise を返してもよい(解決側 `features/block-editor/block-definitions.ts` が await する)。\n * 公式 Tiptap 拡張(`@tiptap/extension-*`)はモジュール評価時に Tiptap のクラスを使うため、\n * プラグインはファクトリの中で `await import(...)` して評価自体をチャンクロード後まで遅らせる。\n */\nexport type BlockDefinitionFactory = (tiptap: Tiptap) => BlockDefinition | Promise<BlockDefinition>;\nexport type BlockExtensionFactory = (\n tiptap: Tiptap\n) => AnyExtension | AnyExtension[] | Promise<AnyExtension | AnyExtension[]>;\n\nlet fillBridge: SlotFillStore | undefined;\n\nexport function getGlobalFillBridge(): SlotFillStore {\n if (!fillBridge) {\n fillBridge = createSlotFillStore();\n }\n return fillBridge;\n}\n\nlet unitDefinitionRegistry: Registry<UnitDefInterface> | undefined;\n\nexport function getUnitDefinitionRegistry(): Registry<UnitDefInterface> {\n if (!unitDefinitionRegistry) {\n unitDefinitionRegistry = createRegistry<UnitDefInterface>('unit-definition');\n }\n return unitDefinitionRegistry;\n}\n\nlet columnTypeRegistry: Registry<ColumnTypeDef> | undefined;\n\nexport function getColumnTypeRegistry(): Registry<ColumnTypeDef> {\n if (!columnTypeRegistry) {\n columnTypeRegistry = createRegistry<ColumnTypeDef>('column-type');\n }\n return columnTypeRegistry;\n}\n\nlet blockDefinitionRegistry: Registry<BlockDefinitionFactory> | undefined;\n\nexport function getBlockDefinitionRegistry(): Registry<BlockDefinitionFactory> {\n if (!blockDefinitionRegistry) {\n blockDefinitionRegistry = createRegistry<BlockDefinitionFactory>('block-definition');\n }\n return blockDefinitionRegistry;\n}\n\nlet blockExtensionRegistry: Registry<BlockExtensionFactory> | undefined;\n\nexport function getBlockExtensionRegistry(): Registry<BlockExtensionFactory> {\n if (!blockExtensionRegistry) {\n blockExtensionRegistry = createRegistry<BlockExtensionFactory>('extension');\n }\n return blockExtensionRegistry;\n}\n\nlet dataviewActionRegistry: ViewedRegistry<Action<RowData>> | undefined;\n\nexport function getDataviewActionRegistry(): ViewedRegistry<Action<RowData>> {\n if (!dataviewActionRegistry) {\n dataviewActionRegistry = createViewedRegistry<Action<RowData>>('dataview-action');\n }\n return dataviewActionRegistry;\n}\n\nlet dataviewBulkActionRegistry: ViewedRegistry<BulkAction<RowData>> | undefined;\n\nexport function getDataviewBulkActionRegistry(): ViewedRegistry<BulkAction<RowData>> {\n if (!dataviewBulkActionRegistry) {\n dataviewBulkActionRegistry = createViewedRegistry<BulkAction<RowData>>('dataview-bulk-action');\n }\n return dataviewBulkActionRegistry;\n}\n\nlet dataviewMenuRegistry: ViewedRegistry<Menu<RowData>> | undefined;\n\nexport function getDataviewMenuRegistry(): ViewedRegistry<Menu<RowData>> {\n if (!dataviewMenuRegistry) {\n dataviewMenuRegistry = createViewedRegistry<Menu<RowData>>('dataview-menu');\n }\n return dataviewMenuRegistry;\n}\n"],"mappings":"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","names":["unitDefinitionRegistry","columnTypeRegistry","blockDefinitionRegistry","blockExtensionRegistry","dataviewActionRegistry","dataviewBulkActionRegistry","dataviewMenuRegistry","createSlotFillStore","createSlotFillStore"]}
@@ -150,4 +150,9 @@ export interface Registry<T> {
150
150
  remove(name: string): boolean;
151
151
  get(name: string): T | undefined;
152
152
  getAll(): Record<string, T>;
153
+ /**
154
+ * 登録内容が実際に変わった(`add` / `remove` / `add` の戻り値での解除)あとに呼ばれる購読を追加する。
155
+ * 描画済みの画面が、`adminReady` の中など遅れて行われた登録を取り込むために使う。
156
+ */
157
+ subscribe(listener: () => void): Unsubscribe;
153
158
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ablogcms/plugins",
3
- "version": "3.2.39",
3
+ "version": "3.2.40",
4
4
  "type": "module",
5
5
  "description": "Plugin registry for the a-blog cms admin JS extension platform",
6
6
  "license": "MIT",
@@ -12,17 +12,17 @@
12
12
  },
13
13
  "sideEffects": false,
14
14
  "dependencies": {
15
- "@ablogcms/block-editor": "3.2.39",
16
- "@ablogcms/components": "3.2.39",
17
- "@ablogcms/events": "3.2.39",
18
- "@ablogcms/i18n": "3.2.39",
19
- "@ablogcms/keyboard-shortcut": "3.2.39",
20
- "@ablogcms/types": "3.2.39",
21
- "@ablogcms/unit-editor": "3.2.39"
15
+ "@ablogcms/block-editor": "3.2.40",
16
+ "@ablogcms/components": "3.2.40",
17
+ "@ablogcms/events": "3.2.40",
18
+ "@ablogcms/i18n": "3.2.40",
19
+ "@ablogcms/keyboard-shortcut": "3.2.40",
20
+ "@ablogcms/types": "3.2.40",
21
+ "@ablogcms/unit-editor": "3.2.40"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@testing-library/react": "^16.3.3",
25
- "vitest": "^5.0.5"
25
+ "vitest": "^5.1.0"
26
26
  },
27
27
  "peerDependencies": {
28
28
  "react": "^19",