@elastic/isomer-sdk 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (288) hide show
  1. package/NOTICE.txt +0 -286
  2. package/README.md +13 -2
  3. package/THIRD_PARTY_LICENSES.md +1 -885
  4. package/dist/author/authored_fields.d.ts +93 -18
  5. package/dist/author/authored_fields.d.ts.map +1 -1
  6. package/dist/author/authored_fields.js +63 -43
  7. package/dist/author/authored_fields.js.map +1 -1
  8. package/dist/author/authoring_input.d.ts +7 -0
  9. package/dist/author/authoring_input.d.ts.map +1 -0
  10. package/dist/author/authoring_input.js +66 -0
  11. package/dist/author/authoring_input.js.map +1 -0
  12. package/dist/author/authoring_model.d.ts +33 -0
  13. package/dist/author/authoring_model.d.ts.map +1 -0
  14. package/dist/author/authoring_model.js +142 -0
  15. package/dist/author/authoring_model.js.map +1 -0
  16. package/dist/author/declarations.d.ts +13 -0
  17. package/dist/author/declarations.d.ts.map +1 -0
  18. package/dist/author/declarations.js +288 -0
  19. package/dist/author/declarations.js.map +1 -0
  20. package/dist/author/index.d.ts +4 -1
  21. package/dist/author/index.d.ts.map +1 -1
  22. package/dist/author/index.js +4 -1
  23. package/dist/author/index.js.map +1 -1
  24. package/dist/author/jsx_shim.d.ts +50 -30
  25. package/dist/author/jsx_shim.d.ts.map +1 -1
  26. package/dist/author/jsx_shim.js +146 -138
  27. package/dist/author/jsx_shim.js.map +1 -1
  28. package/dist/author/print_jsx.d.ts +19 -0
  29. package/dist/author/print_jsx.d.ts.map +1 -0
  30. package/dist/author/print_jsx.js +372 -0
  31. package/dist/author/print_jsx.js.map +1 -0
  32. package/dist/author/prompt.d.ts +2 -2
  33. package/dist/author/prompt.d.ts.map +1 -1
  34. package/dist/author/prompt.js +7 -2
  35. package/dist/author/prompt.js.map +1 -1
  36. package/dist/author/schema_to_typescript.d.ts +37 -0
  37. package/dist/author/schema_to_typescript.d.ts.map +1 -0
  38. package/dist/author/schema_to_typescript.js +289 -0
  39. package/dist/author/schema_to_typescript.js.map +1 -0
  40. package/dist/cjs/author/authored_fields.js +68 -47
  41. package/dist/cjs/author/authored_fields.js.map +1 -1
  42. package/dist/cjs/author/authoring_input.js +70 -0
  43. package/dist/cjs/author/authoring_input.js.map +1 -0
  44. package/dist/cjs/author/authoring_model.js +149 -0
  45. package/dist/cjs/author/authoring_model.js.map +1 -0
  46. package/dist/cjs/author/declarations.js +292 -0
  47. package/dist/cjs/author/declarations.js.map +1 -0
  48. package/dist/cjs/author/index.js +10 -1
  49. package/dist/cjs/author/index.js.map +1 -1
  50. package/dist/cjs/author/jsx_shim.js +148 -140
  51. package/dist/cjs/author/jsx_shim.js.map +1 -1
  52. package/dist/cjs/author/print_jsx.js +378 -0
  53. package/dist/cjs/author/print_jsx.js.map +1 -0
  54. package/dist/cjs/author/prompt.js +7 -2
  55. package/dist/cjs/author/prompt.js.map +1 -1
  56. package/dist/cjs/author/schema_to_typescript.js +298 -0
  57. package/dist/cjs/author/schema_to_typescript.js.map +1 -0
  58. package/dist/cjs/composition/body_node_base.js +11 -4
  59. package/dist/cjs/composition/body_node_base.js.map +1 -1
  60. package/dist/cjs/composition/error.js +2 -0
  61. package/dist/cjs/composition/error.js.map +1 -1
  62. package/dist/cjs/composition/index.js +2 -1
  63. package/dist/cjs/composition/index.js.map +1 -1
  64. package/dist/cjs/composition/structured_value.js.map +1 -1
  65. package/dist/cjs/define/frame.js +2 -3
  66. package/dist/cjs/define/frame.js.map +1 -1
  67. package/dist/cjs/define/index.js +7 -1
  68. package/dist/cjs/define/index.js.map +1 -1
  69. package/dist/cjs/define/primitive_example.js +26 -0
  70. package/dist/cjs/define/primitive_example.js.map +1 -0
  71. package/dist/cjs/define/primitive_icon.js +17 -0
  72. package/dist/cjs/define/primitive_icon.js.map +1 -0
  73. package/dist/cjs/define/primitive_module.js +24 -7
  74. package/dist/cjs/define/primitive_module.js.map +1 -1
  75. package/dist/cjs/define/slack_assets.js.map +1 -1
  76. package/dist/cjs/entries/index.js +8 -2
  77. package/dist/cjs/entries/index.js.map +1 -1
  78. package/dist/cjs/entries/markdown.js +2 -1
  79. package/dist/cjs/entries/markdown.js.map +1 -1
  80. package/dist/cjs/entries/slack.js +1 -2
  81. package/dist/cjs/entries/slack.js.map +1 -1
  82. package/dist/cjs/pack/capabilities.js +17 -11
  83. package/dist/cjs/pack/capabilities.js.map +1 -1
  84. package/dist/cjs/pack/compose.js +4 -1
  85. package/dist/cjs/pack/compose.js.map +1 -1
  86. package/dist/cjs/pack/index.js +2 -1
  87. package/dist/cjs/pack/index.js.map +1 -1
  88. package/dist/cjs/pack/primitive_pack.js +27 -5
  89. package/dist/cjs/pack/primitive_pack.js.map +1 -1
  90. package/dist/cjs/render/anchors.js +1 -1
  91. package/dist/cjs/render/anchors.js.map +1 -1
  92. package/dist/cjs/render/format_number.js +2 -1
  93. package/dist/cjs/render/format_number.js.map +1 -1
  94. package/dist/cjs/render/html/envelope.js +12 -6
  95. package/dist/cjs/render/html/envelope.js.map +1 -1
  96. package/dist/cjs/render/layout_check.js.map +1 -1
  97. package/dist/cjs/render/markdown/builder.js +18 -8
  98. package/dist/cjs/render/markdown/builder.js.map +1 -1
  99. package/dist/cjs/render/markdown/format.js +70 -30
  100. package/dist/cjs/render/markdown/format.js.map +1 -1
  101. package/dist/cjs/render/markdown/host_tags.js +67 -0
  102. package/dist/cjs/render/markdown/host_tags.js.map +1 -0
  103. package/dist/cjs/render/markdown/index.js +3 -1
  104. package/dist/cjs/render/markdown/index.js.map +1 -1
  105. package/dist/cjs/render/markdown/split.js +325 -0
  106. package/dist/cjs/render/markdown/split.js.map +1 -0
  107. package/dist/cjs/render/primitive_dispatch.js +11 -25
  108. package/dist/cjs/render/primitive_dispatch.js.map +1 -1
  109. package/dist/cjs/render/react/content.js +3 -1
  110. package/dist/cjs/render/react/content.js.map +1 -1
  111. package/dist/cjs/render/slack/format.js +1 -601
  112. package/dist/cjs/render/slack/format.js.map +1 -1
  113. package/dist/cjs/render/slack/index.js +1 -2
  114. package/dist/cjs/render/slack/index.js.map +1 -1
  115. package/dist/cjs/render/slack/markdown_content.js +172 -115
  116. package/dist/cjs/render/slack/markdown_content.js.map +1 -1
  117. package/dist/cjs/testing/conformance.js +40 -24
  118. package/dist/cjs/testing/conformance.js.map +1 -1
  119. package/dist/cjs/testing/icon.js +248 -0
  120. package/dist/cjs/testing/icon.js.map +1 -0
  121. package/dist/cjs/testing/index.js +3 -1
  122. package/dist/cjs/testing/index.js.map +1 -1
  123. package/dist/cjs/testing/registration.js +5 -3
  124. package/dist/cjs/testing/registration.js.map +1 -1
  125. package/dist/cjs/validate/authoring_schema.js +17 -1
  126. package/dist/cjs/validate/authoring_schema.js.map +1 -1
  127. package/dist/cjs/validate/index.js +2 -1
  128. package/dist/cjs/validate/index.js.map +1 -1
  129. package/dist/cjs/validate/json_schema.js +10 -3
  130. package/dist/cjs/validate/json_schema.js.map +1 -1
  131. package/dist/cjs/validate/url.js +2 -1
  132. package/dist/cjs/validate/url.js.map +1 -1
  133. package/dist/cjs/validate/validation.js +2 -45
  134. package/dist/cjs/validate/validation.js.map +1 -1
  135. package/dist/composition/body_node_base.d.ts +7 -6
  136. package/dist/composition/body_node_base.d.ts.map +1 -1
  137. package/dist/composition/body_node_base.js +10 -3
  138. package/dist/composition/body_node_base.js.map +1 -1
  139. package/dist/composition/error.d.ts +2 -0
  140. package/dist/composition/error.d.ts.map +1 -1
  141. package/dist/composition/error.js +2 -0
  142. package/dist/composition/error.js.map +1 -1
  143. package/dist/composition/index.d.ts +1 -1
  144. package/dist/composition/index.d.ts.map +1 -1
  145. package/dist/composition/index.js +1 -1
  146. package/dist/composition/index.js.map +1 -1
  147. package/dist/composition/structured_value.d.ts +3 -3
  148. package/dist/composition/structured_value.d.ts.map +1 -1
  149. package/dist/composition/structured_value.js.map +1 -1
  150. package/dist/define/frame.d.ts +9 -20
  151. package/dist/define/frame.d.ts.map +1 -1
  152. package/dist/define/frame.js +2 -3
  153. package/dist/define/frame.js.map +1 -1
  154. package/dist/define/index.d.ts +3 -1
  155. package/dist/define/index.d.ts.map +1 -1
  156. package/dist/define/index.js +3 -1
  157. package/dist/define/index.js.map +1 -1
  158. package/dist/define/primitive_example.d.ts +30 -0
  159. package/dist/define/primitive_example.d.ts.map +1 -0
  160. package/dist/define/primitive_example.js +21 -0
  161. package/dist/define/primitive_example.js.map +1 -0
  162. package/dist/define/primitive_icon.d.ts +11 -0
  163. package/dist/define/primitive_icon.d.ts.map +1 -0
  164. package/dist/define/primitive_icon.js +14 -0
  165. package/dist/define/primitive_icon.js.map +1 -0
  166. package/dist/define/primitive_module.d.ts +45 -24
  167. package/dist/define/primitive_module.d.ts.map +1 -1
  168. package/dist/define/primitive_module.js +24 -7
  169. package/dist/define/primitive_module.js.map +1 -1
  170. package/dist/define/slack_assets.d.ts +1 -1
  171. package/dist/define/slack_assets.d.ts.map +1 -1
  172. package/dist/define/slack_assets.js.map +1 -1
  173. package/dist/entries/index.d.ts +3 -3
  174. package/dist/entries/index.d.ts.map +1 -1
  175. package/dist/entries/index.js +3 -3
  176. package/dist/entries/index.js.map +1 -1
  177. package/dist/entries/markdown.d.ts +1 -1
  178. package/dist/entries/markdown.d.ts.map +1 -1
  179. package/dist/entries/markdown.js +1 -1
  180. package/dist/entries/markdown.js.map +1 -1
  181. package/dist/entries/slack.d.ts +1 -1
  182. package/dist/entries/slack.d.ts.map +1 -1
  183. package/dist/entries/slack.js +1 -1
  184. package/dist/entries/slack.js.map +1 -1
  185. package/dist/pack/capabilities.d.ts +24 -10
  186. package/dist/pack/capabilities.d.ts.map +1 -1
  187. package/dist/pack/capabilities.js +17 -11
  188. package/dist/pack/capabilities.js.map +1 -1
  189. package/dist/pack/compose.d.ts +4 -1
  190. package/dist/pack/compose.d.ts.map +1 -1
  191. package/dist/pack/compose.js +4 -1
  192. package/dist/pack/compose.js.map +1 -1
  193. package/dist/pack/index.d.ts +2 -2
  194. package/dist/pack/index.d.ts.map +1 -1
  195. package/dist/pack/index.js +2 -2
  196. package/dist/pack/index.js.map +1 -1
  197. package/dist/pack/primitive_pack.d.ts +14 -3
  198. package/dist/pack/primitive_pack.d.ts.map +1 -1
  199. package/dist/pack/primitive_pack.js +25 -4
  200. package/dist/pack/primitive_pack.js.map +1 -1
  201. package/dist/render/anchors.d.ts +2 -2
  202. package/dist/render/anchors.d.ts.map +1 -1
  203. package/dist/render/anchors.js +1 -1
  204. package/dist/render/anchors.js.map +1 -1
  205. package/dist/render/format_number.d.ts.map +1 -1
  206. package/dist/render/format_number.js +2 -1
  207. package/dist/render/format_number.js.map +1 -1
  208. package/dist/render/html/envelope.d.ts +3 -3
  209. package/dist/render/html/envelope.js +12 -6
  210. package/dist/render/html/envelope.js.map +1 -1
  211. package/dist/render/layout_check.d.ts +1 -1
  212. package/dist/render/layout_check.d.ts.map +1 -1
  213. package/dist/render/layout_check.js.map +1 -1
  214. package/dist/render/markdown/builder.d.ts +5 -4
  215. package/dist/render/markdown/builder.d.ts.map +1 -1
  216. package/dist/render/markdown/builder.js +16 -7
  217. package/dist/render/markdown/builder.js.map +1 -1
  218. package/dist/render/markdown/format.d.ts +9 -0
  219. package/dist/render/markdown/format.d.ts.map +1 -1
  220. package/dist/render/markdown/format.js +67 -31
  221. package/dist/render/markdown/format.js.map +1 -1
  222. package/dist/render/markdown/host_tags.d.ts +14 -0
  223. package/dist/render/markdown/host_tags.d.ts.map +1 -0
  224. package/dist/render/markdown/host_tags.js +63 -0
  225. package/dist/render/markdown/host_tags.js.map +1 -0
  226. package/dist/render/markdown/index.d.ts +1 -0
  227. package/dist/render/markdown/index.d.ts.map +1 -1
  228. package/dist/render/markdown/index.js +1 -0
  229. package/dist/render/markdown/index.js.map +1 -1
  230. package/dist/render/markdown/split.d.ts +32 -0
  231. package/dist/render/markdown/split.d.ts.map +1 -0
  232. package/dist/render/markdown/split.js +321 -0
  233. package/dist/render/markdown/split.js.map +1 -0
  234. package/dist/render/primitive_dispatch.d.ts +6 -6
  235. package/dist/render/primitive_dispatch.d.ts.map +1 -1
  236. package/dist/render/primitive_dispatch.js +12 -26
  237. package/dist/render/primitive_dispatch.js.map +1 -1
  238. package/dist/render/react/content.d.ts +1 -1
  239. package/dist/render/react/content.d.ts.map +1 -1
  240. package/dist/render/react/content.js +3 -1
  241. package/dist/render/react/content.js.map +1 -1
  242. package/dist/render/slack/format.d.ts +0 -25
  243. package/dist/render/slack/format.d.ts.map +1 -1
  244. package/dist/render/slack/format.js +1 -599
  245. package/dist/render/slack/format.js.map +1 -1
  246. package/dist/render/slack/index.d.ts +1 -1
  247. package/dist/render/slack/index.d.ts.map +1 -1
  248. package/dist/render/slack/index.js +1 -1
  249. package/dist/render/slack/index.js.map +1 -1
  250. package/dist/render/slack/markdown_content.d.ts +10 -4
  251. package/dist/render/slack/markdown_content.d.ts.map +1 -1
  252. package/dist/render/slack/markdown_content.js +169 -113
  253. package/dist/render/slack/markdown_content.js.map +1 -1
  254. package/dist/testing/conformance.d.ts +19 -14
  255. package/dist/testing/conformance.d.ts.map +1 -1
  256. package/dist/testing/conformance.js +40 -24
  257. package/dist/testing/conformance.js.map +1 -1
  258. package/dist/testing/icon.d.ts +11 -0
  259. package/dist/testing/icon.d.ts.map +1 -0
  260. package/dist/testing/icon.js +244 -0
  261. package/dist/testing/icon.js.map +1 -0
  262. package/dist/testing/index.d.ts +2 -1
  263. package/dist/testing/index.d.ts.map +1 -1
  264. package/dist/testing/index.js +1 -0
  265. package/dist/testing/index.js.map +1 -1
  266. package/dist/testing/registration.d.ts +1 -1
  267. package/dist/testing/registration.d.ts.map +1 -1
  268. package/dist/testing/registration.js +6 -4
  269. package/dist/testing/registration.js.map +1 -1
  270. package/dist/validate/authoring_schema.d.ts +6 -0
  271. package/dist/validate/authoring_schema.d.ts.map +1 -1
  272. package/dist/validate/authoring_schema.js +15 -0
  273. package/dist/validate/authoring_schema.js.map +1 -1
  274. package/dist/validate/index.d.ts +1 -1
  275. package/dist/validate/index.d.ts.map +1 -1
  276. package/dist/validate/index.js +1 -1
  277. package/dist/validate/index.js.map +1 -1
  278. package/dist/validate/json_schema.d.ts.map +1 -1
  279. package/dist/validate/json_schema.js +10 -3
  280. package/dist/validate/json_schema.js.map +1 -1
  281. package/dist/validate/url.d.ts.map +1 -1
  282. package/dist/validate/url.js +2 -1
  283. package/dist/validate/url.js.map +1 -1
  284. package/dist/validate/validation.d.ts +1 -11
  285. package/dist/validate/validation.d.ts.map +1 -1
  286. package/dist/validate/validation.js +3 -46
  287. package/dist/validate/validation.js.map +1 -1
  288. package/package.json +3 -2
@@ -2,7 +2,7 @@ import { type ReactElement, type ReactNode } from 'react';
2
2
  import type { z, ZodObject } from 'zod';
3
3
  import type { Composition } from '../composition/composition.js';
4
4
  import type { PrimitiveNode } from '../define/primitive_module.js';
5
- import { type AuthoredChildBrand, type AuthoredTextBrand } from './authored_fields.js';
5
+ import { type AuthoredChildBrand, type AuthoredNodesBrand, type AuthoredTextBrand, type AuthoredToItemBrand, type ItemSchema } from './authored_fields.js';
6
6
  import { type AuthorComponent } from './jsx.js';
7
7
  /** A {@link Composition} whose `body` is the pack's own authoring node type. */
8
8
  export type AuthorComposition<TNode extends PrimitiveNode = PrimitiveNode> = Omit<Composition<TNode>, 'body'> & {
@@ -31,7 +31,7 @@ type SchemaOf<P> = P extends {
31
31
  } ? S extends ZodObject ? S : never : never;
32
32
  /** A bare `ZodObject` has a string index; a real shape does not. */
33
33
  type LooseSchema<TSchema> = string extends keyof ShapeOf<TSchema> ? true : false;
34
- type Branded<F> = F extends AuthoredChildBrand<string, unknown> | AuthoredTextBrand ? F : F extends {
34
+ type Branded<F> = F extends AuthoredChildBrand<string, unknown> | AuthoredTextBrand | AuthoredNodesBrand ? F : F extends {
35
35
  _zod: {
36
36
  def: {
37
37
  type: 'optional' | 'nullable' | 'default' | 'readonly';
@@ -40,47 +40,61 @@ type Branded<F> = F extends AuthoredChildBrand<string, unknown> | AuthoredTextBr
40
40
  };
41
41
  } ? Branded<Inner> : F;
42
42
  type AuthoredNames<TSchema> = {
43
- [K in keyof ShapeOf<TSchema>]: Branded<ShapeOf<TSchema>[K]> extends AuthoredChildBrand<string, unknown> | AuthoredTextBrand ? K : never;
43
+ [K in keyof ShapeOf<TSchema>]: Branded<ShapeOf<TSchema>[K]> extends AuthoredChildBrand<string, unknown> | AuthoredTextBrand | AuthoredNodesBrand ? K : never;
44
44
  }[keyof ShapeOf<TSchema>];
45
- type AuthorPropsForSchema<TSchema extends ZodObject> = LooseSchema<TSchema> extends true ? Record<string, unknown> & {
45
+ type SlotValue<V> = NonNullable<V> extends readonly (infer Item)[] ? Item : NonNullable<V>;
46
+ /**
47
+ * A field holding body nodes, or an array of them: its `type` is wide or one of
48
+ * the pack's `TTypes`, so a discriminated config object does not count.
49
+ */
50
+ type NodeSlotKey<T, TTypes extends string> = {
51
+ [K in keyof T]-?: K extends 'type' ? never : SlotValue<T[K]> extends {
52
+ type: infer Type extends string;
53
+ } ? string extends Type ? K : [Type] extends [TTypes] ? K : never : never;
54
+ }[keyof T];
55
+ type IsUnion<T, U = T> = [T] extends [never] ? false : T extends unknown ? [U] extends [T] ? false : true : false;
56
+ /** `T` when it is exactly one key. */
57
+ type Only<T> = [T] extends [never] ? never : IsUnion<T> extends true ? never : T;
58
+ /**
59
+ * The one unbranded node field JSX children fill. With several, each stays a
60
+ * prop, as at runtime.
61
+ */
62
+ type PassthroughSlot<TSchema extends ZodObject, TTypes extends string> = Only<Exclude<NodeSlotKey<z.infer<TSchema>, TTypes>, AuthoredNames<TSchema>>>;
63
+ type FilledNames<TSchema extends ZodObject, TTypes extends string> = AuthoredNames<TSchema> | PassthroughSlot<TSchema, TTypes>;
64
+ type AuthorPropsForSchema<TSchema extends ZodObject, TTypes extends string> = LooseSchema<TSchema> extends true ? Record<string, unknown> & {
46
65
  children?: ReactNode;
47
- } : Omit<z.infer<TSchema>, 'type' | AuthoredNames<TSchema>> & Partial<Pick<z.infer<TSchema>, Extract<AuthoredNames<TSchema>, keyof z.infer<TSchema>>>> & {
66
+ } : Omit<z.infer<TSchema>, 'type' | FilledNames<TSchema, TTypes>> & Partial<Pick<z.infer<TSchema>, Extract<FilledNames<TSchema, TTypes>, keyof z.infer<TSchema>>>> & {
48
67
  children?: ReactNode;
49
68
  };
50
- type AuthorPropsFor<P> = [SchemaOf<P>] extends [never] ? Record<string, unknown> & {
69
+ type AuthorPropsFor<P, TTypes extends string> = [SchemaOf<P>] extends [never] ? Record<string, unknown> & {
51
70
  children?: ReactNode;
52
- } : AuthorPropsForSchema<SchemaOf<P>>;
53
- type ChildEntry<F> = 0 extends 1 & F ? NoKeys : F extends AuthoredChildBrand<infer Name, infer Props> ? [Name] extends [string] ? {
71
+ } : AuthorPropsForSchema<SchemaOf<P>, TTypes>;
72
+ type ChildEntry<F, TOwner extends string, TDepth extends unknown[]> = 0 extends 1 & F ? NoKeys : F extends AuthoredChildBrand<infer Name, infer Props> ? [Name] extends [string] ? {
54
73
  [K in Capitalize<Name>]: AuthorComponent<Props & {
55
74
  children?: ReactNode;
56
- }, Name>;
57
- } : NoKeys : NoKeys;
75
+ }, `${TOwner}.${K}`>;
76
+ } & (F extends AuthoredToItemBrand ? NoKeys : ChildComponentsOf<ItemSchema<F>, TOwner, [...TDepth, unknown]>) : NoKeys : NoKeys;
58
77
  type UnionToIntersection<T> = (T extends unknown ? (value: T) => void : never) extends (value: infer R) => void ? R : never;
59
- type ChildComponentsOf<TSchema> = LooseSchema<TSchema> extends true ? NoKeys : UnionToIntersection<{
60
- [K in keyof ShapeOf<TSchema>]: ChildEntry<Branded<ShapeOf<TSchema>[K]>>;
78
+ /** Child components branded on `TSchema`, then on each child's item schema, five levels deep. */
79
+ type ChildComponentsOf<TSchema, TOwner extends string, TDepth extends unknown[] = []> = TDepth['length'] extends 5 ? NoKeys : LooseSchema<TSchema> extends true ? NoKeys : UnionToIntersection<{
80
+ [K in keyof ShapeOf<TSchema>]: ChildEntry<Branded<ShapeOf<TSchema>[K]>, TOwner, TDepth>;
61
81
  }[keyof ShapeOf<TSchema>]>;
62
- type ChildMapOfTuple<TPrimitives> = TPrimitives extends readonly [
63
- infer Head,
64
- ...infer Tail
65
- ] ? ChildComponentsOf<SchemaOf<Head>> & ChildMapOfTuple<Tail extends readonly {
66
- type: string;
67
- }[] ? Tail : []> : NoKeys;
68
- type ChildComponentMap<TPrimitives extends readonly {
69
- type: string;
70
- }[]> = ChildMapOfTuple<TPrimitives>;
82
+ /** A primitive's child components, as members of its own component. */
83
+ type MembersOf<P, TType extends string> = [SchemaOf<P>] extends [never] ? NoKeys : ChildComponentsOf<SchemaOf<P>, TType>;
71
84
  /**
72
85
  * PascalCase authoring components keyed from a pack's primitive `type` list.
73
- * `slideFrame` becomes `SlideFrame`. Props come from the primitive's schema.
86
+ * `slideFrame` becomes `SlideFrame`. Props come from the primitive's schema,
87
+ * and each branded child is a member: `SlideFrame.Footer`.
74
88
  */
75
89
  export type PrimitiveComponentMap<TPrimitives extends readonly {
76
90
  type: string;
77
91
  }[]> = {
78
- [P in TPrimitives[number] as Capitalize<LiteralType<P['type'] & string>>]: AuthorComponent<AuthorPropsFor<P>, P['type'] & string>;
92
+ [P in TPrimitives[number] as Capitalize<LiteralType<P['type'] & string>>]: AuthorComponent<AuthorPropsFor<P, LiteralType<TPrimitives[number]['type'] & string>>, P['type'] & string> & MembersOf<P, P['type'] & string>;
79
93
  };
80
94
  /**
81
95
  * A pack's JSX authoring front: the root `Composition` element, one
82
- * PascalCase component per primitive and per branded child, and the conversion
83
- * back to plain data.
96
+ * PascalCase component per primitive with its branded children as members,
97
+ * and the conversion back to plain data.
84
98
  */
85
99
  export type JsxShim<TNode extends PrimitiveNode = PrimitiveNode, TPrimitives extends readonly {
86
100
  type: string;
@@ -90,14 +104,19 @@ export type JsxShim<TNode extends PrimitiveNode = PrimitiveNode, TPrimitives ext
90
104
  Composition: AuthorComponent<CompositionAuthorProps<TNode>, 'view'>;
91
105
  /** Throws when the root is not the `Composition` element, or when a child is not a registered primitive. */
92
106
  toComposition: (element: ReactElement<CompositionAuthorProps<TNode>>) => AuthorComposition<TNode>;
93
- } & PrimitiveComponentMap<TPrimitives> & ChildComponentMap<TPrimitives>;
107
+ } & PrimitiveComponentMap<TPrimitives>;
94
108
  /**
95
109
  * Builds a pack's {@link JsxShim} from its primitive list.
96
110
  *
97
111
  * Branded schema fields ({@link fromChildren}, {@link fromTextChildren}) fill
98
- * from JSX children, and each child type becomes a component. A primitive with
99
- * no brand still fills its one child slot, array or single node, from
100
- * {@link PrimitiveDefinition.children}.
112
+ * from JSX children, and each child becomes a member of its primitive's
113
+ * component. Several branded fields share one children list, and each takes
114
+ * the elements of its member.
115
+ * Elements of a registered primitive that match none of them fill the one
116
+ * unbranded child slot from {@link PrimitiveDefinition.children}, or the one
117
+ * {@link fromNodeChildren} field. A primitive with no brand still fills that slot, array or
118
+ * single node, from its children. That field is optional on the component's
119
+ * props.
101
120
  */
102
121
  export declare const buildJsxShim: <const TPrimitives extends readonly {
103
122
  type: string;
@@ -107,10 +126,11 @@ export declare const buildJsxShim: <const TPrimitives extends readonly {
107
126
  /** React children as a flat array, with fragments inlined. */
108
127
  export declare const flattenChildren: (children: ReactNode) => ReactNode[];
109
128
  /**
110
- * Children joined into one string. Throws on an element child, so a prop typed as text stays text. Collapses whitespace unless `{ collapseWhitespace: false }`.
129
+ * Children joined into one string. Throws on an element child, so a prop typed as text stays text. Collapses whitespace unless `{ collapseWhitespace: false }`; `{ dedent: true }` reads it as source, lines joined by `\n`.
111
130
  */
112
131
  export declare const textFromChildren: (children: ReactNode, options?: {
113
132
  collapseWhitespace?: boolean;
133
+ dedent?: boolean;
114
134
  }) => string;
115
135
  export {};
116
136
  //# sourceMappingURL=jsx_shim.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"jsx_shim.d.ts","sourceRoot":"","sources":["../../src/author/jsx_shim.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"jsx_shim.d.ts","sourceRoot":"","sources":["../../src/author/jsx_shim.ts"],"names":[],"mappings":"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"}
@@ -7,26 +7,24 @@
7
7
  import { Children, Fragment, isValidElement, } from 'react';
8
8
  import { IsomerError } from '../composition/error.js';
9
9
  import { readAuthoredSpec, } from './authored_fields.js';
10
+ import { capitalize, createAuthoringModel, memberType, } from './authoring_model.js';
10
11
  import { authorType, defineAuthorComponent } from './jsx.js';
11
12
  /**
12
13
  * Builds a pack's {@link JsxShim} from its primitive list.
13
14
  *
14
15
  * Branded schema fields ({@link fromChildren}, {@link fromTextChildren}) fill
15
- * from JSX children, and each child type becomes a component. A primitive with
16
- * no brand still fills its one child slot, array or single node, from
17
- * {@link PrimitiveDefinition.children}.
16
+ * from JSX children, and each child becomes a member of its primitive's
17
+ * component. Several branded fields share one children list, and each takes
18
+ * the elements of its member.
19
+ * Elements of a registered primitive that match none of them fill the one
20
+ * unbranded child slot from {@link PrimitiveDefinition.children}, or the one
21
+ * {@link fromNodeChildren} field. A primitive with no brand still fills that slot, array or
22
+ * single node, from its children. That field is optional on the component's
23
+ * props.
18
24
  */
19
25
  export const buildJsxShim = (primitives = []) => {
20
26
  const primitiveTypes = new Set(primitives.map((primitive) => primitive.type));
21
- const childSlotsByType = new Map(primitives.map((primitive) => [
22
- primitive.type,
23
- inferChildSlots('children' in primitive ? primitive.children : undefined),
24
- ]));
25
- const { authoredByType, childComponents } = collectAuthored(primitives);
26
- assertUniqueComponentNames([
27
- ...primitives.map(({ type }) => type),
28
- ...childComponents.keys(),
29
- ]);
27
+ const { authoredByType, membersByType, childSlotsByType } = createAuthoringModel(primitives);
30
28
  const view = defineAuthorComponent('view');
31
29
  const env = {
32
30
  authoredByType,
@@ -36,56 +34,15 @@ export const buildJsxShim = (primitives = []) => {
36
34
  return {
37
35
  Composition: view,
38
36
  toComposition: (element) => toAuthorComposition(element, env),
39
- ...Object.fromEntries(primitives.map((primitive) => [
40
- capitalize(primitive.type),
41
- defineAuthorComponent(primitive.type),
42
- ])),
43
- ...Object.fromEntries([...childComponents].map(([type, component]) => [
37
+ ...Object.fromEntries(primitives.map(({ type }) => [
44
38
  capitalize(type),
45
- component,
39
+ Object.assign(defineAuthorComponent(type), Object.fromEntries((membersByType.get(type) ?? []).map(({ member, authorType }) => [
40
+ member,
41
+ defineAuthorComponent(authorType),
42
+ ]))),
46
43
  ])),
47
44
  };
48
45
  };
49
- const collectAuthored = (primitives) => {
50
- const authoredByType = new Map();
51
- const signatures = new Map();
52
- const childComponents = new Map();
53
- for (const primitive of primitives) {
54
- const schema = primitive.schema;
55
- if (!isZodType(schema)) {
56
- continue;
57
- }
58
- const spec = readAuthoredSpec(schema);
59
- if (spec.children.length === 0 && spec.text.length === 0) {
60
- continue;
61
- }
62
- authoredByType.set(primitive.type, spec);
63
- for (const field of spec.children) {
64
- const prior = signatures.get(field.childType);
65
- if (prior !== undefined && prior !== field.signature) {
66
- throw new IsomerError('DUPLICATE_AUTHORED_CHILD', `Child type "${field.childType}" is branded with two different item shapes.`);
67
- }
68
- signatures.set(field.childType, field.signature);
69
- if (!childComponents.has(field.childType)) {
70
- childComponents.set(field.childType, defineAuthorComponent(field.childType));
71
- }
72
- }
73
- }
74
- return { authoredByType, childComponents };
75
- };
76
- /** Throws when two types, or a type and the root `Composition`, capitalize to one component name. */
77
- const assertUniqueComponentNames = (types) => {
78
- const typeByName = new Map([['Composition', 'view']]);
79
- for (const type of types) {
80
- const name = capitalize(type);
81
- const earlier = typeByName.get(name);
82
- if (earlier !== undefined && earlier !== type) {
83
- throw new IsomerError('DUPLICATE_PRIMITIVE_TYPE', `buildJsxShim: "${earlier}" and "${type}" both become the component ${name}`);
84
- }
85
- typeByName.set(name, type);
86
- }
87
- };
88
- const isZodType = (schema) => typeof schema === 'object' && schema !== null && '_zod' in schema;
89
46
  const toAuthorComposition = (element, env) => {
90
47
  requireAuthorElement(element, 'view');
91
48
  const props = element.props;
@@ -125,12 +82,12 @@ const bodyNodeFromElement = (node, env) => {
125
82
  for (const [key, value] of Object.entries(withoutChildren(element))) {
126
83
  const childField = authored?.children.find((field) => field.field === key);
127
84
  converted[key] = childField
128
- ? valueFromChildField(value, childField, env)
85
+ ? valueFromChildField(value, childField, env, type)
129
86
  : convertPropValue(value, key, slots, parseChild);
130
87
  }
131
88
  const rawChildren = element.props.children;
132
89
  if (authored && (authored.children.length > 0 || authored.text.length > 0)) {
133
- fillAuthoredFields(converted, authored, rawChildren, env);
90
+ fillAuthoredFields(converted, authored, rawChildren, env, slots, parseChild, type);
134
91
  }
135
92
  else {
136
93
  const unique = slots.length === 1 ? slots[0] : undefined;
@@ -138,7 +95,7 @@ const bodyNodeFromElement = (node, env) => {
138
95
  converted[unique.field] === undefined &&
139
96
  rawChildren !== undefined) {
140
97
  const nested = flattenChildren(rawChildren).map(parseChild);
141
- converted[unique.field] = unique.array ? nested : nested[0];
98
+ converted[unique.field] = slotValue(nested, unique);
142
99
  }
143
100
  else if (slots.length === 0 && flattenChildren(rawChildren).length > 0) {
144
101
  throw new IsomerError('UNEXPECTED_CHILDREN', `<${capitalize(type)}> takes no JSX children; set its fields through props.`);
@@ -146,31 +103,79 @@ const bodyNodeFromElement = (node, env) => {
146
103
  }
147
104
  return omitUndefined({ ...converted, type });
148
105
  };
149
- const fillAuthoredFields = (converted, authored, rawChildren, env) => {
150
- for (const field of authored.children) {
151
- if (converted[field.field] !== undefined || rawChildren === undefined) {
152
- continue;
106
+ const fillAuthoredFields = (converted, authored, rawChildren, env, slots, parseChild, parentType) => {
107
+ const unsetChildren = authored.children.filter((field) => converted[field.field] === undefined);
108
+ const unsetText = authored.text.filter((field) => converted[field.field] === undefined);
109
+ const brandedFields = new Set(authored.children.map((field) => field.field));
110
+ const unbranded = slots.filter(({ field }) => !brandedFields.has(field));
111
+ const slot = unbranded.length === 1 ? unbranded[0] : undefined;
112
+ const slotOpen = slot !== undefined && converted[slot.field] === undefined;
113
+ if (rawChildren === undefined ||
114
+ (unsetChildren.length === 0 && unsetText.length === 0 && !slotOpen)) {
115
+ for (const field of unsetText) {
116
+ if (!field.optional) {
117
+ throw new IsomerError('MISSING_AUTHORED_TEXT', `Expected text children for "${field.field}".`);
118
+ }
153
119
  }
154
- converted[field.field] = itemsFromBrand(rawChildren, field, env);
120
+ return;
155
121
  }
156
- for (const field of authored.text) {
157
- if (converted[field.field] !== undefined) {
122
+ const flat = flattenChildren(rawChildren);
123
+ const elements = flat.filter(isAuthorElement);
124
+ const textNodes = flat.filter((child) => !isAuthorElement(child));
125
+ const claimed = new Set();
126
+ for (const field of unsetChildren) {
127
+ const matching = elements.filter((child) => getAuthorType(child) === memberType(parentType, field));
128
+ if (matching.length === 0) {
129
+ if (flat.length === 0 && field.array && !field.optional) {
130
+ converted[field.field] = [];
131
+ }
158
132
  continue;
159
133
  }
160
- if (rawChildren === undefined) {
134
+ for (const child of matching) {
135
+ claimed.add(child);
136
+ }
137
+ converted[field.field] = valueFromBrand(matching, field, env, parentType);
138
+ }
139
+ const brandedTypes = new Set(authored.children.map((field) => memberType(parentType, field)));
140
+ const stray = elements.filter((child) => brandedTypes.has(getAuthorType(child)) && !claimed.has(child));
141
+ if (stray.length > 0) {
142
+ const childType = getAuthorType(stray[0]);
143
+ throw new IsomerError('UNEXPECTED_CHILDREN', `<${capitalize(parentType)}> already has its <${capitalize(childType)}>.`);
144
+ }
145
+ const leftover = elements.filter((child) => !claimed.has(child));
146
+ if (slotOpen && slot && leftover.length > 0) {
147
+ const nested = leftover.map(parseChild);
148
+ converted[slot.field] = slotValue(nested, slot);
149
+ }
150
+ else if (leftover.length > 0) {
151
+ const childType = getAuthorType(leftover[0]);
152
+ const [empty, ...moreEmpty] = unsetChildren.filter(({ field }) => converted[field] === undefined);
153
+ if (empty && moreEmpty.length === 0 && !slotOpen) {
154
+ throw new IsomerError('AUTHOR_TYPE_MISMATCH', `Expected <${capitalize(memberType(parentType, empty))}> but received <${capitalize(childType)}>.`);
155
+ }
156
+ throw new IsomerError('UNEXPECTED_CHILDREN', `<${capitalize(childType)}> is not a child of <${capitalize(parentType)}>.`);
157
+ }
158
+ if (textNodes.length === 0) {
159
+ for (const field of unsetText) {
161
160
  if (!field.optional) {
162
161
  throw new IsomerError('MISSING_AUTHORED_TEXT', `Expected text children for "${field.field}".`);
163
162
  }
164
- continue;
165
163
  }
166
- converted[field.field] = textFromChildren(rawChildren, {
164
+ return;
165
+ }
166
+ if (unsetText.length === 0) {
167
+ throw new IsomerError('EXPECTED_JSX_ELEMENT', 'Expected an Isomer JSX element.');
168
+ }
169
+ for (const field of unsetText) {
170
+ converted[field.field] = textFromChildren(textNodes, {
167
171
  collapseWhitespace: field.collapseWhitespace,
172
+ ...(field.dedent ? { dedent: true } : {}),
168
173
  });
169
174
  }
170
175
  };
171
- const valueFromChildField = (value, field, env) => {
176
+ const valueFromChildField = (value, field, env, owner) => {
172
177
  if (isJsxNodes(value)) {
173
- return itemsFromBrand(value, field, env);
178
+ return valueFromBrand(flattenChildren(value), field, env, owner);
174
179
  }
175
180
  return value;
176
181
  };
@@ -184,47 +189,76 @@ const isJsxNodes = (value) => {
184
189
  return (Array.isArray(value) &&
185
190
  value.some((item) => isAuthorElement(item) || isValidElement(item)));
186
191
  };
187
- const itemsFromBrand = (children, field, env) => flattenChildren(children).map((child) => itemFromElement(child, field, env));
188
- const itemFromElement = (child, field, env) => {
189
- const element = requireAuthorElement(child, field.childType);
192
+ /** The field's array of items, or its one item; throws on a second element for a single-item field. */
193
+ const valueFromBrand = (elements, field, env, owner) => {
194
+ const items = elements.map((child) => itemFromElement(child, field, env, owner));
195
+ if (field.array) {
196
+ return items;
197
+ }
198
+ if (items.length > 1) {
199
+ throw new IsomerError('UNEXPECTED_CHILDREN', `"${field.field}" takes one <${capitalize(memberType(owner, field))}>, not ${items.length}.`);
200
+ }
201
+ return items[0];
202
+ };
203
+ const itemFromElement = (child, field, env, owner) => {
204
+ const element = requireAuthorElement(child, memberType(owner, field));
190
205
  if (field.toItem) {
191
206
  return field.toItem(element.props, {
192
207
  parseChildren: (nested) => flattenChildren(nested).map((node) => bodyNodeFromElement(node, env)),
193
208
  });
194
209
  }
195
210
  const props = withoutChildren(element);
196
- const nestedChildren = element.props.children;
211
+ const nested = flattenChildren(element.props.children);
212
+ const elements = nested.filter(isAuthorElement);
213
+ const text = nested.filter((child) => !isAuthorElement(child));
197
214
  if (field.textField !== undefined &&
198
215
  props[field.textField] === undefined &&
199
- nestedChildren !== undefined &&
200
- !hasAuthorElement(nestedChildren)) {
201
- props[field.textField] = textFromChildren(nestedChildren);
216
+ text.length > 0) {
217
+ props[field.textField] = textFromChildren(text);
202
218
  }
203
- fillNestedBrands(field.itemSchema, props, nestedChildren, env);
219
+ fillNestedBrands(field.itemSchema, props, elements, env, owner, capitalize(memberType(owner, field)));
204
220
  return props;
205
221
  };
206
- const fillNestedBrands = (itemSchema, props, children, env) => {
207
- if (children === undefined) {
222
+ const fillNestedBrands = (itemSchema, props, elements, env, owner, parent) => {
223
+ if (elements.length === 0) {
208
224
  return;
209
225
  }
210
- const spec = readAuthoredSpec(itemSchema);
211
- for (const field of spec.children) {
226
+ const fields = readAuthoredSpec(itemSchema).children;
227
+ const claimed = new Set();
228
+ for (const field of fields) {
212
229
  if (props[field.field] !== undefined) {
213
230
  continue;
214
231
  }
215
- const matching = flattenChildren(children).filter((child) => isAuthorElement(child) && getAuthorType(child) === field.childType);
216
- if (matching.length === 0) {
217
- continue;
232
+ const matching = elements.filter((child) => getAuthorType(child) === memberType(owner, field));
233
+ if (matching.length > 0) {
234
+ matching.forEach((child) => claimed.add(child));
235
+ props[field.field] = valueFromBrand(matching, field, env, owner);
218
236
  }
219
- props[field.field] = matching.map((child) => itemFromElement(child, field, env));
220
237
  }
238
+ const [stray] = elements.filter((child) => !claimed.has(child));
239
+ if (stray) {
240
+ const strayType = getAuthorType(stray);
241
+ const held = fields.some((field) => memberType(owner, field) === strayType);
242
+ throw new IsomerError('UNEXPECTED_CHILDREN', held
243
+ ? `<${parent}> already has its <${capitalize(strayType)}>.`
244
+ : `<${capitalize(strayType)}> is not a child of <${parent}>.`);
245
+ }
246
+ };
247
+ /** The slot's nodes, or its one node; throws on a second node for a single-node slot. */
248
+ const slotValue = (nodes, { array, field }) => {
249
+ if (array) {
250
+ return nodes;
251
+ }
252
+ if (nodes.length > 1) {
253
+ throw new IsomerError('UNEXPECTED_CHILDREN', `"${field}" takes one node, not ${nodes.length}.`);
254
+ }
255
+ return nodes[0];
221
256
  };
222
- const hasAuthorElement = (children) => flattenChildren(children).some((child) => isAuthorElement(child));
223
257
  const convertPropValue = (value, field, slots, parseChild) => {
224
- const asArray = slots.some((slot) => slot.field === field && slot.array);
258
+ const array = slots.some((slot) => slot.field === field && slot.array);
225
259
  const fromJsx = nodesFromJsx(value, parseChild);
226
260
  if (fromJsx) {
227
- return asArray ? fromJsx : fromJsx[0];
261
+ return slotValue(fromJsx, { array, field });
228
262
  }
229
263
  if (Array.isArray(value)) {
230
264
  return value.flatMap((item) => {
@@ -250,51 +284,6 @@ const isAuthorElement = (value) => {
250
284
  const component = value.type;
251
285
  return typeof component[authorType] === 'string';
252
286
  };
253
- /**
254
- * Reads {@link PrimitiveDefinition.children} against a probe node so JSX can
255
- * fill the same fields a tree walk would visit. Paths like `body[0]` are array
256
- * slots; a bare `header` is a single nested node.
257
- */
258
- const inferChildSlots = (children) => {
259
- if (typeof children !== 'function') {
260
- return [];
261
- }
262
- const probeNode = { type: '__probe__' };
263
- const node = new Proxy({}, {
264
- get(_target, prop) {
265
- if (prop === 'type') {
266
- return probeNode.type;
267
- }
268
- return [probeNode];
269
- },
270
- });
271
- try {
272
- const refs = children(node);
273
- if (!Array.isArray(refs)) {
274
- return [];
275
- }
276
- const byField = new Map();
277
- for (const ref of refs) {
278
- if (!isChildRef(ref)) {
279
- continue;
280
- }
281
- const field = /^[A-Za-z_]\w*/.exec(ref.path)?.[0];
282
- if (!field) {
283
- continue;
284
- }
285
- byField.set(field, byField.get(field) === true || /\[\d+\]/.test(ref.path));
286
- }
287
- return [...byField.entries()].map(([field, array]) => ({ array, field }));
288
- }
289
- catch {
290
- return [];
291
- }
292
- };
293
- const isChildRef = (value) => typeof value === 'object' &&
294
- value !== null &&
295
- 'path' in value &&
296
- typeof value.path === 'string';
297
- const capitalize = (value) => `${value.slice(0, 1).toUpperCase()}${value.slice(1)}`;
298
287
  /** React children as a flat array, with fragments inlined. */
299
288
  export const flattenChildren = (children) => Children.toArray(children).flatMap((child) => {
300
289
  if (isValidElement(child) && child.type === Fragment) {
@@ -303,8 +292,24 @@ export const flattenChildren = (children) => Children.toArray(children).flatMap(
303
292
  }
304
293
  return child;
305
294
  });
295
+ const lineBreak = /\r\n|[\n\r\u2028\u2029]/;
296
+ // A template literal's source: the break after the opening backtick, the
297
+ // indentation before the closing one, and the indentation every line shares go.
298
+ const dedentSource = (text) => {
299
+ const all = text.split(lineBreak);
300
+ const start = all.length > 1 && all[0] === '' ? 1 : 0;
301
+ const end = all.length - start > 1 && /^ *$/.test(all[all.length - 1])
302
+ ? all.length - 1
303
+ : all.length;
304
+ const lines = all.slice(start, end);
305
+ const indents = lines
306
+ .filter((line) => line.trim() !== '')
307
+ .map((line) => line.length - line.trimStart().length);
308
+ const shared = indents.length > 0 ? Math.min(...indents) : 0;
309
+ return lines.map((line) => line.slice(shared)).join('\n');
310
+ };
306
311
  /**
307
- * Children joined into one string. Throws on an element child, so a prop typed as text stays text. Collapses whitespace unless `{ collapseWhitespace: false }`.
312
+ * Children joined into one string. Throws on an element child, so a prop typed as text stays text. Collapses whitespace unless `{ collapseWhitespace: false }`; `{ dedent: true }` reads it as source, lines joined by `\n`.
308
313
  */
309
314
  export const textFromChildren = (children, options) => {
310
315
  const text = flattenChildren(children)
@@ -315,6 +320,9 @@ export const textFromChildren = (children, options) => {
315
320
  throw new IsomerError('EXPECTED_TEXT_CHILDREN', 'Expected text children.');
316
321
  })
317
322
  .join('');
323
+ if (options?.dedent) {
324
+ return dedentSource(text);
325
+ }
318
326
  if (options?.collapseWhitespace === false) {
319
327
  return text.trim();
320
328
  }