@dickpy/dsh-imagegen 1.5.0 → 1.5.1

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/lib/index.js CHANGED
@@ -1,4 +1,5 @@
1
- import { SettingsConflictError, installSettingsSection, settingsNamespace } from "@deepseek-ai/dsh-settings";
1
+ import * as settingsModule from "@deepseek-ai/dsh-settings";
2
+ import { SettingsConflictError } from "@deepseek-ai/dsh-settings";
2
3
  import z from "schemastery";
3
4
  import { createHash, randomUUID } from "node:crypto";
4
5
  import { promises } from "node:fs";
@@ -7,6 +8,29 @@ import path from "node:path";
7
8
  import { fileURLToPath } from "node:url";
8
9
  import { spawn } from "node:child_process";
9
10
  import { defineTool } from "@deepseek-ai/dsh-tools";
11
+ //#region src/settings-compat.ts
12
+ const compatModule = settingsModule;
13
+ /** Brand namespaces where the installed settings package still exposes it. */
14
+ function settingsNamespaceCompat(value) {
15
+ return compatModule.settingsNamespace?.(value) ?? value;
16
+ }
17
+ /**
18
+ * Register an optional settings section across the rc.7 and alpha.2 APIs.
19
+ * rc.7 exposes a module helper; alpha.2 moves the helper onto the provider.
20
+ */
21
+ function installSettingsSectionCompat(ctx, ns, schema, entry, hooks) {
22
+ const legacyInstaller = compatModule.installSettingsSection;
23
+ if (legacyInstaller !== void 0) {
24
+ legacyInstaller(ctx, ns, schema, entry, hooks);
25
+ return;
26
+ }
27
+ ctx.inject(["settings"], (sctx) => {
28
+ const provider = sctx.get("settings");
29
+ if (provider.installSection === void 0) throw new TypeError("dsh-settings does not expose installSection");
30
+ provider.installSection(ctx, ns, schema, entry, hooks);
31
+ });
32
+ }
33
+ //#endregion
10
34
  //#region src/protocol.ts
11
35
  /**
12
36
  * Wire contract shared by the host and client halves of dsh-imagegen: the
@@ -16,7 +40,7 @@ import { defineTool } from "@deepseek-ai/dsh-tools";
16
40
  /** Settings namespace this plugin owns (host settings seam + bridge). */
17
41
  const IMAGEGEN_SETTINGS_NAMESPACE = "dsh-imagegen";
18
42
  /** Published package version shared by the host updater and the client UI. */
19
- const PLUGIN_VERSION = "1.5.0";
43
+ const PLUGIN_VERSION = "1.5.1";
20
44
  /** Same-origin route family (loopback-only, mirroring the dsh-ssh fence). */
21
45
  const SETTINGS_API = {
22
46
  describe: "/api/dsh-imagegen/settings/describe",
@@ -2312,7 +2336,7 @@ function makeRoutes(deps) {
2312
2336
  }
2313
2337
  const expectedRevision = typeof body.expectedRevision === "number" ? body.expectedRevision : void 0;
2314
2338
  try {
2315
- await deps.settings.mutate(settingsNamespace(ns), body.ops, expectedRevision);
2339
+ await deps.settings.mutate(ns, body.ops, expectedRevision);
2316
2340
  } catch (error) {
2317
2341
  writeJson(res, 200, failureOf(error));
2318
2342
  return;
@@ -3446,7 +3470,7 @@ const inject = [
3446
3470
  "commands"
3447
3471
  ];
3448
3472
  /** The branded settings namespace of this plugin (the card edits it). */
3449
- const ImageGenSettingsNamespace = settingsNamespace(IMAGEGEN_SETTINGS_NAMESPACE);
3473
+ const ImageGenSettingsNamespace = settingsNamespaceCompat(IMAGEGEN_SETTINGS_NAMESPACE);
3450
3474
  const Config = z.object({
3451
3475
  enabled: z.boolean().default(true),
3452
3476
  announceToAgent: z.boolean().default(true),
@@ -3656,7 +3680,7 @@ function apply(ctx, config) {
3656
3680
  text: guidanceFor(value.channels, value.defaultChannelId)
3657
3681
  });
3658
3682
  };
3659
- installSettingsSection(ctx, ImageGenSettingsNamespace, Config, config ?? {}, {
3683
+ installSettingsSectionCompat(ctx, ImageGenSettingsNamespace, Config, config ?? {}, {
3660
3684
  setSource: (source) => {
3661
3685
  current = source;
3662
3686
  sync();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dickpy/dsh-imagegen",
3
3
  "description": "AI image generation plugin for the dsh web GUI: text-to-image and image-to-image through configurable provider channels (gpt-image-2 / grok-imagine-image / nanobanana series / seedream-5.0-pro / dall-e-3, with native xAI Grok Imagine, Google Nano Banana and ByteDance Seedream request shaping), with per-channel model catalogs and a New Session / Image Generation tab entry opening a three-column studio beside the native conversation.",
4
- "version": "1.5.0",
4
+ "version": "1.5.1",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",
7
7
  "exports": {
@@ -15,9 +15,12 @@
15
15
  },
16
16
  "client": {
17
17
  "inject": [
18
- "@deepseek-ai/dsh-client-runtime",
18
+ "@deepseek-ai/dsh-client-store",
19
+ "@deepseek-ai/dsh-api-remotes",
20
+ "@deepseek-ai/dsh-api-session-controller",
19
21
  "@deepseek-ai/dsh-client-connection",
20
22
  "@deepseek-ai/dsh-client-ui-conversation",
23
+ "@deepseek-ai/dsh-client-ui-renderer",
21
24
  "@deepseek-ai/dsh-client-ui-settings",
22
25
  "@deepseek-ai/dsh-client-ui-tool"
23
26
  ],
@@ -28,21 +31,24 @@
28
31
  "schemastery": "^3.18.0"
29
32
  },
30
33
  "devDependencies": {
31
- "@deepseek-ai/cordis": "^4.0.1",
32
- "@deepseek-ai/dsh-attachment": "0.1.0-rc.7",
33
- "@deepseek-ai/dsh-client-connection": "0.1.0-rc.7",
34
- "@deepseek-ai/dsh-client-locale": "0.1.0-rc.7",
35
- "@deepseek-ai/dsh-client-runtime": "0.1.0-rc.7",
36
- "@deepseek-ai/dsh-client-ui-conversation": "0.1.0-rc.7",
37
- "@deepseek-ai/dsh-client-ui-primitives": "0.1.0-rc.7",
38
- "@deepseek-ai/dsh-client-ui-settings": "0.1.0-rc.7",
39
- "@deepseek-ai/dsh-client-ui-slots": "0.1.0-rc.7",
40
- "@deepseek-ai/dsh-commands": "0.1.0-rc.7",
41
- "@deepseek-ai/dsh-host-webserver": "0.1.0-rc.7",
42
- "@deepseek-ai/dsh-llm": "0.1.0-rc.7",
43
- "@deepseek-ai/dsh-settings": "0.1.0-rc.7",
44
- "@deepseek-ai/dsh-system-prompt": "0.1.0-rc.7",
45
- "@deepseek-ai/dsh-tools": "0.1.0-rc.7",
34
+ "@deepseek-ai/cordis": "^4.0.2",
35
+ "@deepseek-ai/dsh-api-remotes": "0.1.2-alpha.2",
36
+ "@deepseek-ai/dsh-api-session-controller": "0.1.2-alpha.2",
37
+ "@deepseek-ai/dsh-attachment": "0.1.2-alpha.2",
38
+ "@deepseek-ai/dsh-client-connection": "0.1.2-alpha.2",
39
+ "@deepseek-ai/dsh-client-locale": "0.1.2-alpha.2",
40
+ "@deepseek-ai/dsh-client-store": "0.1.2-alpha.2",
41
+ "@deepseek-ai/dsh-client-ui-conversation": "0.1.2-alpha.2",
42
+ "@deepseek-ai/dsh-client-ui-primitives": "0.1.2-alpha.2",
43
+ "@deepseek-ai/dsh-client-ui-renderer": "0.1.2-alpha.2",
44
+ "@deepseek-ai/dsh-client-ui-settings": "0.1.2-alpha.2",
45
+ "@deepseek-ai/dsh-client-ui-slots": "0.1.2-alpha.2",
46
+ "@deepseek-ai/dsh-commands": "0.1.2-alpha.2",
47
+ "@deepseek-ai/dsh-host-webserver": "0.1.2-alpha.2",
48
+ "@deepseek-ai/dsh-llm": "0.1.2-alpha.2",
49
+ "@deepseek-ai/dsh-settings": "0.1.2-alpha.2",
50
+ "@deepseek-ai/dsh-system-prompt": "0.1.2-alpha.2",
51
+ "@deepseek-ai/dsh-tools": "0.1.2-alpha.2",
46
52
  "@types/node": "^22.20.0",
47
53
  "@types/react": "~18.3.1",
48
54
  "@types/react-dom": "^18.3.5",
@@ -11,7 +11,8 @@
11
11
  import { useEffect, useRef, useState, type CSSProperties, type PointerEvent as ReactPointerEvent } from 'react'
12
12
  import { createPortal } from 'react-dom'
13
13
  import { Button, Pill } from '@deepseek-ai/dsh-client-ui-primitives'
14
- import type { ISessions, SessionId } from '@deepseek-ai/dsh-client-runtime/client'
14
+ import type { SessionId } from '@deepseek-ai/dsh-api-remotes/client'
15
+ import type { ISessions } from '@deepseek-ai/dsh-api-session-controller/client'
15
16
  import type { ImageAttachmentRef } from '@deepseek-ai/dsh-attachment'
16
17
  import type { ImageGenApi } from './api.ts'
17
18
  import { errorMessage, tt } from './helpers.ts'
@@ -14,7 +14,7 @@
14
14
 
15
15
  import { useEffect, useRef, useState } from 'react'
16
16
  import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots'
17
- import { createSnapshotStore, type SnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'
17
+ import { createSnapshotStore, type SnapshotStore } from '@deepseek-ai/dsh-client-store'
18
18
  import { CardForm, booleanField, secretField, textField, type CardActions, type CardShell, type FieldState as CardFieldState } from './settings-form.ts'
19
19
  import { ChannelsForm, type ChannelDraft, type ChannelsFormActions, type ChannelsFormState } from './channels-form.ts'
20
20
  import type { ImageGenScope } from './settings-scope.ts'
@@ -12,7 +12,7 @@
12
12
  * - path ops never navigate *inside* the channels array.
13
13
  */
14
14
 
15
- import { createSnapshotStore, type SnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'
15
+ import { createSnapshotStore, type SnapshotStore } from '@deepseek-ai/dsh-client-store'
16
16
  import type { ChannelConfig, ModelMapping } from '../protocol.ts'
17
17
  import type { ImageGenScope, SettingsOp } from './settings-scope.ts'
18
18
 
@@ -1,6 +1,6 @@
1
1
  import type { ImageAttachmentRef } from '@deepseek-ai/dsh-attachment'
2
2
  import type { ConversationController, IConversation } from '@deepseek-ai/dsh-client-ui-conversation/client'
3
- import type { SessionId } from '@deepseek-ai/dsh-client-runtime/client'
3
+ import type { SessionId } from '@deepseek-ai/dsh-api-remotes/client'
4
4
 
5
5
  /** Document event used to bridge chat tool results into the image workspace. */
6
6
  export const CHAT_IMAGE_EVENT = 'dsh-imagegen:chat-images'
@@ -1,7 +1,10 @@
1
1
  /** Inline renderer for image-generation tool-result attachments. */
2
2
 
3
3
  import type { ImageAttachmentRef } from '@deepseek-ai/dsh-attachment'
4
- import type { ClientContext, SessionId, ToolCallBlock } from '@deepseek-ai/dsh-client-runtime/client'
4
+ import type { Context as ClientContext } from '@deepseek-ai/cordis'
5
+ import type { SessionId } from '@deepseek-ai/dsh-api-remotes/client'
6
+ import type { ToolCallBlock } from '@deepseek-ai/dsh-client-ui-conversation/client'
7
+ import type {} from '@deepseek-ai/dsh-client-ui-renderer/client'
5
8
  import { useEffect, useMemo, useState } from 'react'
6
9
  import { AGENT_IMAGE_API } from '../protocol.ts'
7
10
  import { CHAT_IMAGE_EVENT } from './conversation-sync.ts'
@@ -31,10 +34,9 @@ function isSettled(block: ToolCallBlock): block is Extract<ToolCallBlock, { kind
31
34
  return 'kind' in block
32
35
  }
33
36
 
34
- function imageRefsOf(block: ToolCallBlock): ImageAttachmentRef[] {
35
- if (!isSettled(block)) return []
36
- const resultContent = block.resultView?.card === 'generic' ? block.resultView.content ?? [] : []
37
- return [...block.content, ...resultContent]
37
+ function imageRefsOf(block: ToolCallBlock): ImageAttachmentRef[] {
38
+ if (!isSettled(block)) return []
39
+ return block.content
38
40
  .flatMap(content => content.type === 'image' ? [content.attachment] : [])
39
41
  }
40
42
 
@@ -169,8 +171,12 @@ export function registerImageToolviews(ctx: ClientContext): void {
169
171
  }
170
172
 
171
173
  ctx.slots.inject('tool.call.toolview', function* () {
172
- for (const key of ['generate_image', 'edit_image', 'get_image_generation_task']) {
173
- yield ctx.slots.register({ name: 'tool.call.toolview', key }, ImageToolView)
174
- }
175
- })
176
- }
174
+ for (const key of ['generate_image', 'edit_image', 'get_image_generation_task']) {
175
+ yield ctx.slots.register({
176
+ name: 'tool.call.toolview',
177
+ key,
178
+ inject: (sessionId: string) => ({ sessionId: sessionId as SessionId }),
179
+ }, ImageToolView)
180
+ }
181
+ })
182
+ }
@@ -11,12 +11,13 @@
11
11
  * whole boot when a plugin apply throws, and an external plugin must not take
12
12
  * the GUI down.
13
13
  */
14
- import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'
15
- import type { ISessions } from '@deepseek-ai/dsh-client-runtime/client'
14
+ import type { Context as ClientContext } from '@deepseek-ai/cordis'
15
+ import type { ISessions } from '@deepseek-ai/dsh-api-session-controller/client'
16
16
  import type { ConnectionHandle } from '@deepseek-ai/dsh-client-connection/client'
17
17
  // Type-only: pulls the locale plugin's Context merge (ctx.locale).
18
- import type {} from '@deepseek-ai/dsh-client-locale/client'
19
- // Type-only: pulls the LocaleNamespaceMap merge table.
18
+ import type {} from '@deepseek-ai/dsh-client-locale/client'
19
+ import type {} from '@deepseek-ai/dsh-client-ui-renderer/client'
20
+ // Type-only: pulls the LocaleNamespaceMap merge table.
20
21
  import type {} from '@deepseek-ai/dsh-client-ui-slots'
21
22
  import { ImageGenApi } from './api.ts'
22
23
  import { ImageGenController } from './controller.ts'
@@ -15,7 +15,7 @@
15
15
  * and both get the `position: relative` base in panel.module.css.
16
16
  */
17
17
 
18
- import type { ISessions } from '@deepseek-ai/dsh-client-runtime/client'
18
+ import type { ISessions } from '@deepseek-ai/dsh-api-session-controller/client'
19
19
  import { createRoot, type Root } from 'react-dom/client'
20
20
  import type { ImageGenApi } from './api.ts'
21
21
  import type { ImageGenController } from './controller.ts'
@@ -7,7 +7,8 @@
7
7
  * sibling UI package).
8
8
  */
9
9
 
10
- import { createSnapshotStore, type SettingsScope, type SettingsScopeSnapshot, type SnapshotStore } from '@deepseek-ai/dsh-client-runtime/client'
10
+ import { createSnapshotStore, type SnapshotStore } from '@deepseek-ai/dsh-client-store'
11
+ import type { SettingsScope, SettingsScopeSnapshot } from '@deepseek-ai/dsh-client-ui-settings/client'
11
12
 
12
13
  /** The write one field's staged text performs when the card is saved. */
13
14
  export type FieldWrite =
@@ -10,10 +10,10 @@
10
10
 
11
11
  import {
12
12
  createSnapshotStore,
13
- type SettingsScope,
14
- type SettingsScopeSnapshot,
15
13
  type SnapshotStore,
16
- } from '@deepseek-ai/dsh-client-runtime/client'
14
+ } from '@deepseek-ai/dsh-client-store'
15
+ import type { SettingsPathOpView } from '@deepseek-ai/dsh-api-remotes/client'
16
+ import type { SettingsScope, SettingsScopeSnapshot } from '@deepseek-ai/dsh-client-ui-settings/client'
17
17
  import { SETTINGS_API, type ChannelConfig } from '../protocol.ts'
18
18
 
19
19
  /** The fields this plugin's settings card edits. */
@@ -158,6 +158,10 @@ class BridgeScopeController<T> implements SettingsScope<T> {
158
158
  return this.enqueue(() => this.writeOps([{ op: 'unset', path: [field] }]))
159
159
  }
160
160
 
161
+ mutate(ops: readonly SettingsPathOpView[], expectedRevision?: number): Promise<void> {
162
+ return this.enqueue(() => this.writeOps([...ops] as SettingsOp[], expectedRevision))
163
+ }
164
+
161
165
  /** Apply several path ops in one revision-fenced mutate call (atomic save).
162
166
  * Path ops may address plain-object fields (e.g. `channelSecrets.<id>`),
163
167
  * but never navigate *inside* arrays — write array fields wholesale. */
@@ -206,8 +210,8 @@ class BridgeScopeController<T> implements SettingsScope<T> {
206
210
  this.accept(view, writable)
207
211
  }
208
212
 
209
- private async writeOps(ops: SettingsOp[]): Promise<void> {
210
- const revision = this.getSnapshot().revision
213
+ private async writeOps(ops: SettingsOp[], expectedRevision?: number): Promise<void> {
214
+ const revision = expectedRevision ?? this.getSnapshot().revision
211
215
  let response
212
216
  try {
213
217
  response = await this.api.mutate({
package/src/index.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  */
9
9
 
10
10
  import type { Context } from '@deepseek-ai/cordis'
11
- import { installSettingsSection, settingsNamespace } from '@deepseek-ai/dsh-settings'
11
+ import { installSettingsSectionCompat, settingsNamespaceCompat } from './settings-compat.ts'
12
12
  import z from 'schemastery'
13
13
  // Type-only: pulls the webServer Context merge (route registration).
14
14
  import type {} from '@deepseek-ai/dsh-host-webserver'
@@ -43,7 +43,7 @@ export { listTemplates, readTemplateImage, refreshTemplates, clearTemplateMemo }
43
43
  export { checkForUpdate, clearUpdateCache, compareVersions, CURRENT_VERSION, installUpdate, profileFromProcess } from './updater.ts'
44
44
 
45
45
  /** The branded settings namespace of this plugin (the card edits it). */
46
- export const ImageGenSettingsNamespace = settingsNamespace(IMAGEGEN_SETTINGS_NAMESPACE)
46
+ export const ImageGenSettingsNamespace = settingsNamespaceCompat(IMAGEGEN_SETTINGS_NAMESPACE)
47
47
 
48
48
  /**
49
49
  * Plugin config, validated by the same-named schemastery schema.
@@ -323,7 +323,7 @@ export function apply(ctx: Context, config?: Config): void {
323
323
  })
324
324
  }
325
325
 
326
- installSettingsSection(ctx, ImageGenSettingsNamespace, Config, config ?? {}, {
326
+ installSettingsSectionCompat(ctx, ImageGenSettingsNamespace, Config, config ?? {}, {
327
327
  setSource: (source) => {
328
328
  current = source
329
329
  sync()
package/src/protocol.ts CHANGED
@@ -8,7 +8,7 @@
8
8
  export const IMAGEGEN_SETTINGS_NAMESPACE = 'dsh-imagegen'
9
9
 
10
10
  /** Published package version shared by the host updater and the client UI. */
11
- export const PLUGIN_VERSION = '1.5.0'
11
+ export const PLUGIN_VERSION = '1.5.1'
12
12
 
13
13
  /** Same-origin route family (loopback-only, mirroring the dsh-ssh fence). */
14
14
  export const SETTINGS_API = {
package/src/routes.ts CHANGED
@@ -9,7 +9,7 @@ import type { IncomingMessage, ServerResponse } from 'node:http'
9
9
  import { randomUUID } from 'node:crypto'
10
10
  import type { WebRoute } from '@deepseek-ai/dsh-host-webserver'
11
11
  import type { ImageAttachmentRef, ImageMediaType, SaveImageAttachment } from '@deepseek-ai/dsh-attachment'
12
- import { SettingsConflictError, settingsNamespace, type SettingsDescriptor } from '@deepseek-ai/dsh-settings'
12
+ import { SettingsConflictError, type SettingsDescriptor } from '@deepseek-ai/dsh-settings'
13
13
  import type { UpstreamConfig } from './engine.ts'
14
14
  import { enhancePrompt, listImageModels, listPromptModels, type PromptModelConfig } from './prompt-enhancer.ts'
15
15
  import { normalizeImageModels } from './image-models.ts'
@@ -563,7 +563,9 @@ export function makeRoutes(deps: ImageGenRoutesDeps): WebRoute[] {
563
563
  }
564
564
  const expectedRevision = typeof body.expectedRevision === 'number' ? body.expectedRevision : undefined
565
565
  try {
566
- await deps.settings.mutate(settingsNamespace(ns), body.ops, expectedRevision)
566
+ // The alpha.2 settings package no longer exports settingsNamespace;
567
+ // the bridge already checked this value against our fixed namespace.
568
+ await deps.settings.mutate(ns, body.ops, expectedRevision)
567
569
  } catch (error) {
568
570
  writeJson(res, 200, failureOf(error))
569
571
  return
@@ -0,0 +1,60 @@
1
+ /** Compatibility helpers for the dsh-settings API transition. */
2
+
3
+ import type { Context } from '@deepseek-ai/cordis'
4
+ import * as settingsModule from '@deepseek-ai/dsh-settings'
5
+ import type { SettingsNamespace, SettingsSectionHooks } from '@deepseek-ai/dsh-settings'
6
+ import type z from 'schemastery'
7
+
8
+ type SettingsModuleCompat = {
9
+ settingsNamespace?: (value: string) => SettingsNamespace
10
+ installSettingsSection?: <T>(
11
+ ctx: Context,
12
+ ns: SettingsNamespace,
13
+ schema: z<T>,
14
+ entry: T,
15
+ hooks: SettingsSectionHooks<T>,
16
+ ) => void
17
+ }
18
+
19
+ interface SettingsProviderCompat {
20
+ installSection?: <T>(
21
+ owner: Context,
22
+ ns: SettingsNamespace,
23
+ schema: z<T>,
24
+ entry: T,
25
+ hooks: SettingsSectionHooks<T>,
26
+ ) => void
27
+ }
28
+
29
+ const compatModule = settingsModule as unknown as SettingsModuleCompat
30
+
31
+ /** Brand namespaces where the installed settings package still exposes it. */
32
+ export function settingsNamespaceCompat(value: string): SettingsNamespace {
33
+ return compatModule.settingsNamespace?.(value) ?? value as SettingsNamespace
34
+ }
35
+
36
+ /**
37
+ * Register an optional settings section across the rc.7 and alpha.2 APIs.
38
+ * rc.7 exposes a module helper; alpha.2 moves the helper onto the provider.
39
+ */
40
+ export function installSettingsSectionCompat<T>(
41
+ ctx: Context,
42
+ ns: SettingsNamespace,
43
+ schema: z<T>,
44
+ entry: T,
45
+ hooks: SettingsSectionHooks<T>,
46
+ ): void {
47
+ const legacyInstaller = compatModule.installSettingsSection
48
+ if (legacyInstaller !== undefined) {
49
+ legacyInstaller(ctx, ns, schema, entry, hooks)
50
+ return
51
+ }
52
+
53
+ ctx.inject(['settings'], (sctx) => {
54
+ const provider = sctx.get('settings') as unknown as SettingsProviderCompat
55
+ if (provider.installSection === undefined) {
56
+ throw new TypeError('dsh-settings does not expose installSection')
57
+ }
58
+ provider.installSection(ctx, ns, schema, entry, hooks)
59
+ })
60
+ }