@object-ui/core 3.3.0 → 3.3.2

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 (101) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +20 -1
  3. package/dist/actions/ActionRunner.d.ts +9 -0
  4. package/dist/actions/ActionRunner.js +41 -4
  5. package/dist/adapters/ValueDataSource.js +3 -1
  6. package/dist/registry/Registry.d.ts +47 -0
  7. package/dist/registry/Registry.js +92 -0
  8. package/dist/utils/filter-converter.js +25 -5
  9. package/package.json +32 -8
  10. package/.turbo/turbo-build.log +0 -4
  11. package/src/__benchmarks__/core.bench.ts +0 -64
  12. package/src/__tests__/protocols/DndProtocol.test.ts +0 -186
  13. package/src/__tests__/protocols/KeyboardProtocol.test.ts +0 -177
  14. package/src/__tests__/protocols/NotificationProtocol.test.ts +0 -142
  15. package/src/__tests__/protocols/ResponsiveProtocol.test.ts +0 -176
  16. package/src/__tests__/protocols/SharingProtocol.test.ts +0 -188
  17. package/src/actions/ActionEngine.ts +0 -268
  18. package/src/actions/ActionRunner.ts +0 -717
  19. package/src/actions/TransactionManager.ts +0 -521
  20. package/src/actions/UndoManager.ts +0 -215
  21. package/src/actions/__tests__/ActionEngine.test.ts +0 -206
  22. package/src/actions/__tests__/ActionRunner.params.test.ts +0 -134
  23. package/src/actions/__tests__/ActionRunner.test.ts +0 -711
  24. package/src/actions/__tests__/TransactionManager.test.ts +0 -447
  25. package/src/actions/__tests__/UndoManager.test.ts +0 -320
  26. package/src/actions/index.ts +0 -12
  27. package/src/adapters/ApiDataSource.ts +0 -376
  28. package/src/adapters/README.md +0 -180
  29. package/src/adapters/ValueDataSource.ts +0 -459
  30. package/src/adapters/__tests__/ApiDataSource.test.ts +0 -418
  31. package/src/adapters/__tests__/ValueDataSource.test.ts +0 -571
  32. package/src/adapters/__tests__/resolveDataSource.test.ts +0 -144
  33. package/src/adapters/index.ts +0 -15
  34. package/src/adapters/resolveDataSource.ts +0 -79
  35. package/src/builder/__tests__/schema-builder.test.ts +0 -235
  36. package/src/builder/schema-builder.ts +0 -584
  37. package/src/data-scope/DataScopeManager.ts +0 -269
  38. package/src/data-scope/ViewDataProvider.ts +0 -282
  39. package/src/data-scope/__tests__/DataScopeManager.test.ts +0 -211
  40. package/src/data-scope/__tests__/ViewDataProvider.test.ts +0 -270
  41. package/src/data-scope/index.ts +0 -24
  42. package/src/errors/__tests__/errors.test.ts +0 -292
  43. package/src/errors/index.ts +0 -269
  44. package/src/evaluator/ExpressionCache.ts +0 -206
  45. package/src/evaluator/ExpressionContext.ts +0 -118
  46. package/src/evaluator/ExpressionEvaluator.ts +0 -315
  47. package/src/evaluator/FormulaFunctions.ts +0 -398
  48. package/src/evaluator/SafeExpressionParser.ts +0 -893
  49. package/src/evaluator/__tests__/ExpressionCache.test.ts +0 -135
  50. package/src/evaluator/__tests__/ExpressionContext.test.ts +0 -110
  51. package/src/evaluator/__tests__/ExpressionEvaluator.test.ts +0 -558
  52. package/src/evaluator/__tests__/FormulaFunctions.test.ts +0 -447
  53. package/src/evaluator/index.ts +0 -13
  54. package/src/index.ts +0 -38
  55. package/src/protocols/DndProtocol.ts +0 -168
  56. package/src/protocols/KeyboardProtocol.ts +0 -181
  57. package/src/protocols/NotificationProtocol.ts +0 -150
  58. package/src/protocols/ResponsiveProtocol.ts +0 -210
  59. package/src/protocols/SharingProtocol.ts +0 -185
  60. package/src/protocols/index.ts +0 -13
  61. package/src/query/__tests__/query-ast.test.ts +0 -211
  62. package/src/query/__tests__/window-functions.test.ts +0 -275
  63. package/src/query/index.ts +0 -7
  64. package/src/query/query-ast.ts +0 -341
  65. package/src/registry/PluginScopeImpl.ts +0 -259
  66. package/src/registry/PluginSystem.ts +0 -206
  67. package/src/registry/Registry.ts +0 -219
  68. package/src/registry/WidgetRegistry.ts +0 -316
  69. package/src/registry/__tests__/PluginSystem.test.ts +0 -309
  70. package/src/registry/__tests__/Registry.test.ts +0 -293
  71. package/src/registry/__tests__/WidgetRegistry.test.ts +0 -321
  72. package/src/registry/__tests__/plugin-scope-integration.test.ts +0 -283
  73. package/src/theme/ThemeEngine.ts +0 -530
  74. package/src/theme/__tests__/ThemeEngine.test.ts +0 -668
  75. package/src/theme/index.ts +0 -24
  76. package/src/types/index.ts +0 -21
  77. package/src/utils/__tests__/debug-collector.test.ts +0 -102
  78. package/src/utils/__tests__/debug.test.ts +0 -134
  79. package/src/utils/__tests__/expand-fields.test.ts +0 -120
  80. package/src/utils/__tests__/extract-records.test.ts +0 -50
  81. package/src/utils/__tests__/filter-converter.test.ts +0 -118
  82. package/src/utils/__tests__/merge-views-into-objects.test.ts +0 -110
  83. package/src/utils/__tests__/normalize-quick-filter.test.ts +0 -123
  84. package/src/utils/debug-collector.ts +0 -100
  85. package/src/utils/debug.ts +0 -148
  86. package/src/utils/expand-fields.ts +0 -76
  87. package/src/utils/extract-records.ts +0 -33
  88. package/src/utils/filter-converter.ts +0 -133
  89. package/src/utils/merge-views-into-objects.ts +0 -36
  90. package/src/utils/normalize-quick-filter.ts +0 -78
  91. package/src/validation/__tests__/object-validation-engine.test.ts +0 -567
  92. package/src/validation/__tests__/schema-validator.test.ts +0 -118
  93. package/src/validation/__tests__/validation-engine.test.ts +0 -102
  94. package/src/validation/index.ts +0 -10
  95. package/src/validation/schema-validator.ts +0 -344
  96. package/src/validation/validation-engine.ts +0 -528
  97. package/src/validation/validators/index.ts +0 -25
  98. package/src/validation/validators/object-validation-engine.ts +0 -722
  99. package/tsconfig.json +0 -15
  100. package/tsconfig.tsbuildinfo +0 -1
  101. package/vitest.config.ts +0 -2
@@ -1,341 +0,0 @@
1
- /**
2
- * ObjectUI - Query AST Builder
3
- * Phase 3.3: QuerySchema AST implementation
4
- * ObjectStack Spec v2.0.1: Window functions support
5
- */
6
-
7
- import type {
8
- QueryAST,
9
- QuerySchema,
10
- SelectNode,
11
- FromNode,
12
- WhereNode,
13
- JoinNode,
14
- GroupByNode,
15
- OrderByNode,
16
- LimitNode,
17
- OffsetNode,
18
- AggregateNode,
19
- WindowNode,
20
- WindowFunction,
21
- WindowFrame,
22
- WindowConfig,
23
- FieldNode,
24
- LiteralNode,
25
- OperatorNode,
26
- LogicalOperator,
27
- AdvancedFilterSchema,
28
- AdvancedFilterCondition,
29
- QuerySortConfig,
30
- JoinConfig,
31
- AggregationConfig,
32
- } from '@object-ui/types';
33
-
34
- /**
35
- * Query AST Builder - Converts QuerySchema to AST
36
- */
37
- export class QueryASTBuilder {
38
- build(query: QuerySchema): QueryAST {
39
- const ast: QueryAST = {
40
- select: this.buildSelect(query),
41
- from: this.buildFrom(query),
42
- };
43
-
44
- if (query.filter) {
45
- ast.where = this.buildWhere(query.filter);
46
- }
47
-
48
- if (query.joins && query.joins.length > 0) {
49
- ast.joins = query.joins.map(join => this.buildJoin(join));
50
- }
51
-
52
- if (query.group_by && query.group_by.length > 0) {
53
- ast.group_by = this.buildGroupBy(query.group_by);
54
- }
55
-
56
- if (query.sort && query.sort.length > 0) {
57
- ast.order_by = this.buildOrderBy(query.sort);
58
- }
59
-
60
- if (query.limit !== undefined) {
61
- ast.limit = this.buildLimit(query.limit);
62
- }
63
-
64
- if (query.offset !== undefined) {
65
- ast.offset = this.buildOffset(query.offset);
66
- }
67
-
68
- return ast;
69
- }
70
-
71
- private buildSelect(query: QuerySchema): SelectNode {
72
- const fields: (FieldNode | AggregateNode | WindowNode)[] = [];
73
-
74
- if (query.fields && query.fields.length > 0) {
75
- fields.push(...query.fields.map(field => this.buildField(field)));
76
- } else if (!query.aggregations || query.aggregations.length === 0) {
77
- // Only add '*' if there are no aggregations
78
- fields.push(this.buildField('*'));
79
- }
80
-
81
- if (query.aggregations && query.aggregations.length > 0) {
82
- fields.push(...query.aggregations.map(agg => this.buildAggregation(agg)));
83
- }
84
-
85
- // Add window functions (ObjectStack Spec v2.0.1)
86
- if (query.windows && query.windows.length > 0) {
87
- fields.push(...query.windows.map(win => this.buildWindow(win)));
88
- }
89
-
90
- return {
91
- type: 'select',
92
- fields,
93
- distinct: false,
94
- };
95
- }
96
-
97
- private buildFrom(query: QuerySchema): FromNode {
98
- return {
99
- type: 'from',
100
- table: query.object,
101
- };
102
- }
103
-
104
- private buildWhere(filter: AdvancedFilterSchema): WhereNode {
105
- return {
106
- type: 'where',
107
- condition: this.buildFilterCondition(filter),
108
- };
109
- }
110
-
111
- private buildFilterCondition(filter: AdvancedFilterSchema): OperatorNode {
112
- const operator = filter.operator || 'and';
113
- const operands: (OperatorNode | FieldNode | LiteralNode)[] = [];
114
-
115
- if (filter.conditions && filter.conditions.length > 0) {
116
- operands.push(...filter.conditions.map(cond => this.buildCondition(cond)));
117
- }
118
-
119
- if (filter.groups && filter.groups.length > 0) {
120
- operands.push(...filter.groups.map(group => this.buildFilterCondition(group)));
121
- }
122
-
123
- return {
124
- type: 'operator',
125
- operator: operator as LogicalOperator,
126
- operands,
127
- };
128
- }
129
-
130
- private buildCondition(condition: AdvancedFilterCondition): OperatorNode {
131
- const field = this.buildField(condition.field);
132
-
133
- // Map filter operators to comparison operators
134
- const operatorMap: Record<string, string> = {
135
- 'equals': '=',
136
- 'not_equals': '!=',
137
- 'greater_than': '>',
138
- 'greater_than_or_equal': '>=',
139
- 'less_than': '<',
140
- 'less_than_or_equal': '<=',
141
- 'contains': 'contains',
142
- 'not_contains': 'contains',
143
- 'starts_with': 'starts_with',
144
- 'ends_with': 'ends_with',
145
- 'like': 'like',
146
- 'ilike': 'ilike',
147
- 'in': 'in',
148
- 'not_in': 'not_in',
149
- 'is_null': 'is_null',
150
- 'is_not_null': 'is_not_null',
151
- 'between': 'between',
152
- };
153
-
154
- const operator = operatorMap[condition.operator] || '=';
155
-
156
- // Handle special operators
157
- if (operator === 'between' && condition.values && condition.values.length === 2) {
158
- return {
159
- type: 'operator',
160
- operator: 'between' as any,
161
- operands: [
162
- field,
163
- this.buildLiteral(condition.values[0]),
164
- this.buildLiteral(condition.values[1])
165
- ],
166
- };
167
- }
168
-
169
- if ((operator === 'in' || operator === 'not_in') && condition.values) {
170
- return {
171
- type: 'operator',
172
- operator: operator as any,
173
- operands: [field, ...condition.values.map(v => this.buildLiteral(v))],
174
- };
175
- }
176
-
177
- if (operator === 'is_null' || operator === 'is_not_null') {
178
- return {
179
- type: 'operator',
180
- operator: operator as any,
181
- operands: [field],
182
- };
183
- }
184
-
185
- // Standard binary operator
186
- const value = this.buildLiteral(condition.value);
187
- return {
188
- type: 'operator',
189
- operator: operator as any,
190
- operands: [field, value],
191
- };
192
- }
193
-
194
- private buildJoin(join: JoinConfig): JoinNode {
195
- const onCondition: OperatorNode = {
196
- type: 'operator',
197
- operator: '=',
198
- operands: [
199
- this.buildField(join.on.local_field),
200
- this.buildField(join.on.foreign_field, join.alias || join.object),
201
- ],
202
- };
203
-
204
- return {
205
- type: 'join',
206
- join_type: join.type,
207
- table: join.object,
208
- alias: join.alias,
209
- on: onCondition,
210
- };
211
- }
212
-
213
- private buildGroupBy(fields: string[]): GroupByNode {
214
- return {
215
- type: 'group_by',
216
- fields: fields.map(field => this.buildField(field)),
217
- };
218
- }
219
-
220
- private buildOrderBy(sorts: QuerySortConfig[]): OrderByNode {
221
- return {
222
- type: 'order_by',
223
- fields: sorts.map(sort => ({
224
- field: this.buildField(sort.field),
225
- direction: sort.order,
226
- })),
227
- };
228
- }
229
-
230
- private buildLimit(limit: number): LimitNode {
231
- return {
232
- type: 'limit',
233
- value: limit,
234
- };
235
- }
236
-
237
- private buildOffset(offset: number): OffsetNode {
238
- return {
239
- type: 'offset',
240
- value: offset,
241
- };
242
- }
243
-
244
- private buildField(field: string, table?: string): FieldNode {
245
- const parts = field.split('.');
246
-
247
- if (parts.length === 2) {
248
- return {
249
- type: 'field',
250
- table: parts[0],
251
- name: parts[1],
252
- };
253
- }
254
-
255
- return {
256
- type: 'field',
257
- table,
258
- name: field,
259
- };
260
- }
261
-
262
- private buildLiteral(value: any): LiteralNode {
263
- let dataType: 'string' | 'number' | 'boolean' | 'date' | 'null' = 'string';
264
-
265
- if (value === null || value === undefined) {
266
- dataType = 'null';
267
- } else if (typeof value === 'number') {
268
- dataType = 'number';
269
- } else if (typeof value === 'boolean') {
270
- dataType = 'boolean';
271
- } else if (value instanceof Date) {
272
- dataType = 'date';
273
- }
274
-
275
- return {
276
- type: 'literal',
277
- value,
278
- data_type: dataType,
279
- };
280
- }
281
-
282
- private buildAggregation(agg: AggregationConfig): AggregateNode {
283
- return {
284
- type: 'aggregate',
285
- function: agg.function,
286
- field: agg.field ? this.buildField(agg.field) : undefined,
287
- alias: agg.alias,
288
- distinct: agg.distinct,
289
- };
290
- }
291
-
292
- /**
293
- * Build window function node (ObjectStack Spec v2.0.1)
294
- */
295
- private buildWindow(config: WindowConfig): WindowNode {
296
- const node: WindowNode = {
297
- type: 'window',
298
- function: config.function,
299
- alias: config.alias,
300
- };
301
-
302
- if (config.field) {
303
- node.field = this.buildField(config.field);
304
- }
305
-
306
- if (config.partitionBy && config.partitionBy.length > 0) {
307
- node.partitionBy = config.partitionBy.map(field => this.buildField(field));
308
- }
309
-
310
- if (config.orderBy && config.orderBy.length > 0) {
311
- node.orderBy = config.orderBy.map(sort => ({
312
- field: this.buildField(sort.field),
313
- direction: sort.direction,
314
- }));
315
- }
316
-
317
- if (config.frame) {
318
- node.frame = config.frame;
319
- }
320
-
321
- if (config.offset !== undefined) {
322
- node.offset = config.offset;
323
- }
324
-
325
- if (config.defaultValue !== undefined) {
326
- node.defaultValue = this.buildLiteral(config.defaultValue);
327
- }
328
-
329
- return node;
330
- }
331
-
332
- optimize(ast: QueryAST): QueryAST {
333
- return ast;
334
- }
335
- }
336
-
337
- export const defaultQueryASTBuilder = new QueryASTBuilder();
338
-
339
- export function buildQueryAST(query: QuerySchema): QueryAST {
340
- return defaultQueryASTBuilder.build(query);
341
- }
@@ -1,259 +0,0 @@
1
- /**
2
- * ObjectUI
3
- * Copyright (c) 2024-present ObjectStack Inc.
4
- *
5
- * This source code is licensed under the MIT license found in the
6
- * LICENSE file in the root directory of this source tree.
7
- */
8
-
9
- /**
10
- * @object-ui/core - Plugin Scope Implementation
11
- *
12
- * Section 3.3: Implementation of scoped plugin system to prevent conflicts.
13
- * Provides isolated component registration, state management, and event bus.
14
- *
15
- * @module plugin-scope-impl
16
- * @packageDocumentation
17
- */
18
-
19
- import type {
20
- PluginScope,
21
- PluginScopeConfig,
22
- PluginEventHandler
23
- } from '@object-ui/types';
24
- import type { Registry, ComponentMeta as RegistryComponentMeta } from './Registry.js';
25
-
26
- /**
27
- * Event Bus for scoped plugin events
28
- */
29
- class EventBus {
30
- private listeners = new Map<string, Set<PluginEventHandler>>();
31
-
32
- on(event: string, handler: PluginEventHandler): () => void {
33
- if (!this.listeners.has(event)) {
34
- this.listeners.set(event, new Set());
35
- }
36
- this.listeners.get(event)!.add(handler);
37
-
38
- // Return unsubscribe function
39
- return () => {
40
- this.listeners.get(event)?.delete(handler);
41
- if (this.listeners.get(event)?.size === 0) {
42
- this.listeners.delete(event);
43
- }
44
- };
45
- }
46
-
47
- emit(event: string, data?: any): void {
48
- const handlers = this.listeners.get(event);
49
- if (handlers) {
50
- handlers.forEach(handler => {
51
- try {
52
- handler(data);
53
- } catch (error) {
54
- console.error(`Error in event handler for "${event}":`, error);
55
- }
56
- });
57
- }
58
- }
59
-
60
- cleanup(): void {
61
- this.listeners.clear();
62
- }
63
- }
64
-
65
- /**
66
- * Global event bus for cross-plugin communication
67
- */
68
- const globalEventBus = new EventBus();
69
-
70
- /**
71
- * Plugin Scope Implementation
72
- *
73
- * Provides isolated access to registry, state, and events for each plugin.
74
- */
75
- export class PluginScopeImpl implements PluginScope {
76
- public readonly name: string;
77
- public readonly version: string;
78
-
79
- private registry: Registry;
80
- private state = new Map<string, any>();
81
- private eventBus = new EventBus();
82
- private config: Required<PluginScopeConfig>;
83
-
84
- constructor(
85
- name: string,
86
- version: string,
87
- registry: Registry,
88
- config?: PluginScopeConfig
89
- ) {
90
- this.name = name;
91
- this.version = version;
92
- this.registry = registry;
93
- this.config = {
94
- enableStateIsolation: config?.enableStateIsolation ?? true,
95
- enableEventIsolation: config?.enableEventIsolation ?? true,
96
- allowGlobalEvents: config?.allowGlobalEvents ?? true,
97
- maxStateSize: config?.maxStateSize ?? 5 * 1024 * 1024, // 5MB
98
- };
99
- }
100
-
101
- /**
102
- * Register a component in the scoped namespace
103
- */
104
- registerComponent(type: string, component: any, meta?: any): void {
105
- // Components are registered as "pluginName:type"
106
- const registryMeta: RegistryComponentMeta = {
107
- ...meta,
108
- namespace: this.name,
109
- };
110
- this.registry.register(type, component, registryMeta);
111
- }
112
-
113
- /**
114
- * Get a component from the scoped namespace
115
- */
116
- getComponent(type: string): any | undefined {
117
- // First try scoped lookup
118
- const scoped = this.registry.get(`${this.name}:${type}`);
119
- if (scoped) {
120
- return scoped;
121
- }
122
-
123
- // Fall back to global lookup
124
- return this.registry.get(type);
125
- }
126
-
127
- /**
128
- * Scoped state management
129
- */
130
- useState<T>(key: string, initialValue: T): [T, (value: T | ((prev: T) => T)) => void] {
131
- if (!this.config.enableStateIsolation) {
132
- throw new Error('State isolation is disabled for this plugin');
133
- }
134
-
135
- // Initialize state if not present
136
- if (!this.state.has(key)) {
137
- this.setState(key, initialValue);
138
- }
139
-
140
- const currentValue = this.getState<T>(key) ?? initialValue;
141
-
142
- const setValue = (value: T | ((prev: T) => T)) => {
143
- const newValue = typeof value === 'function'
144
- ? (value as (prev: T) => T)(this.getState<T>(key) ?? initialValue)
145
- : value;
146
- this.setState(key, newValue);
147
- };
148
-
149
- return [currentValue, setValue];
150
- }
151
-
152
- /**
153
- * Get scoped state value
154
- */
155
- getState<T>(key: string): T | undefined {
156
- return this.state.get(key);
157
- }
158
-
159
- /**
160
- * Set scoped state value
161
- */
162
- setState<T>(key: string, value: T): void {
163
- if (!this.config.enableStateIsolation) {
164
- throw new Error('State isolation is disabled for this plugin');
165
- }
166
-
167
- // Check state size limit
168
- const stateSize = this.estimateStateSize();
169
- const valueSize = this.estimateValueSize(value);
170
-
171
- if (stateSize + valueSize > this.config.maxStateSize) {
172
- throw new Error(
173
- `Plugin "${this.name}" exceeded maximum state size of ${this.config.maxStateSize} bytes`
174
- );
175
- }
176
-
177
- this.state.set(key, value);
178
- }
179
-
180
- /**
181
- * Subscribe to scoped events
182
- */
183
- on(event: string, handler: PluginEventHandler): () => void {
184
- if (!this.config.enableEventIsolation) {
185
- // If isolation is disabled, use global event bus
186
- return this.onGlobal(event, handler);
187
- }
188
-
189
- // Scoped event: prefix with plugin name
190
- const scopedEvent = `${this.name}:${event}`;
191
- return this.eventBus.on(scopedEvent, handler);
192
- }
193
-
194
- /**
195
- * Emit a scoped event
196
- */
197
- emit(event: string, data?: any): void {
198
- if (!this.config.enableEventIsolation) {
199
- // If isolation is disabled, emit globally
200
- this.emitGlobal(event, data);
201
- return;
202
- }
203
-
204
- // Scoped event: prefix with plugin name
205
- const scopedEvent = `${this.name}:${event}`;
206
- this.eventBus.emit(scopedEvent, data);
207
- }
208
-
209
- /**
210
- * Emit a global event
211
- */
212
- emitGlobal(event: string, data?: any): void {
213
- if (!this.config.allowGlobalEvents) {
214
- throw new Error('Global events are disabled for this plugin');
215
- }
216
- globalEventBus.emit(event, data);
217
- }
218
-
219
- /**
220
- * Subscribe to global events
221
- */
222
- onGlobal(event: string, handler: PluginEventHandler): () => void {
223
- if (!this.config.allowGlobalEvents) {
224
- throw new Error('Global events are disabled for this plugin');
225
- }
226
- return globalEventBus.on(event, handler);
227
- }
228
-
229
- /**
230
- * Clean up all plugin resources
231
- */
232
- cleanup(): void {
233
- this.state.clear();
234
- this.eventBus.cleanup();
235
- }
236
-
237
- /**
238
- * Estimate total state size in bytes
239
- */
240
- private estimateStateSize(): number {
241
- let size = 0;
242
- for (const value of this.state.values()) {
243
- size += this.estimateValueSize(value);
244
- }
245
- return size;
246
- }
247
-
248
- /**
249
- * Estimate size of a value in bytes
250
- */
251
- private estimateValueSize(value: any): number {
252
- try {
253
- return JSON.stringify(value).length * 2; // UTF-16 encoding
254
- } catch {
255
- // If not serializable, use rough estimate
256
- return 1024; // 1KB default
257
- }
258
- }
259
- }