@voxgig/sdkgen 0.36.0 → 0.37.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 (135) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/cmp/Main.js +0 -12
  3. package/dist/cmp/Main.js.map +1 -1
  4. package/package.json +1 -1
  5. package/project/.sdk/src/cmp/go/Config_go.ts +6 -2
  6. package/project/.sdk/src/cmp/js/EntityBase_js.ts +34 -0
  7. package/project/.sdk/src/cmp/js/Main_js.ts +10 -0
  8. package/project/.sdk/src/cmp/js/ReadmeExplanation_js.ts +33 -0
  9. package/project/.sdk/src/cmp/js/ReadmeHowto_js.ts +123 -0
  10. package/project/.sdk/src/cmp/js/ReadmeModel_js.ts +152 -0
  11. package/project/.sdk/src/cmp/js/ReadmeTopHowto_js.ts +25 -0
  12. package/project/.sdk/src/cmp/js/ReadmeTopQuick_js.ts +65 -0
  13. package/project/.sdk/src/cmp/js/ReadmeTopTest_js.ts +36 -0
  14. package/project/.sdk/src/cmp/js/TestDirect_js.ts +53 -5
  15. package/project/.sdk/src/cmp/js/TestEntity_js.ts +114 -20
  16. package/project/.sdk/src/cmp/js/fragment/Entity.fragment.js +7 -139
  17. package/project/.sdk/src/cmp/js/fragment/EntityBase.fragment.js +149 -0
  18. package/project/.sdk/src/cmp/js/fragment/EntityCreateOp.fragment.js +6 -10
  19. package/project/.sdk/src/cmp/js/fragment/EntityListOp.fragment.js +6 -10
  20. package/project/.sdk/src/cmp/js/fragment/EntityLoadOp.fragment.js +7 -11
  21. package/project/.sdk/src/cmp/js/fragment/EntityRemoveOp.fragment.js +7 -11
  22. package/project/.sdk/src/cmp/js/fragment/EntityUpdateOp.fragment.js +7 -11
  23. package/project/.sdk/src/cmp/js/fragment/Main.fragment.js +2 -0
  24. package/project/.sdk/src/cmp/js/fragment/SdkError.fragment.js +0 -2
  25. package/project/.sdk/src/cmp/lua/Config_lua.ts +6 -2
  26. package/project/.sdk/src/cmp/lua/TestEntity_lua.ts +3 -1
  27. package/project/.sdk/src/cmp/php/Config_php.ts +6 -2
  28. package/project/.sdk/src/cmp/php/TestDirect_php.ts +2 -2
  29. package/project/.sdk/src/cmp/php/TestEntity_php.ts +10 -15
  30. package/project/.sdk/src/cmp/py/Config_py.ts +6 -2
  31. package/project/.sdk/src/cmp/py/TestEntity_py.ts +3 -1
  32. package/project/.sdk/src/cmp/rb/Config_rb.ts +6 -2
  33. package/project/.sdk/src/cmp/ts/Main_ts.ts +7 -0
  34. package/project/.sdk/tm/go/feature/log_feature.go +1 -1
  35. package/project/.sdk/tm/go/test/runner_test.go +16 -2
  36. package/project/.sdk/tm/js/src/Context.js +142 -0
  37. package/project/.sdk/tm/js/src/Control.js +16 -0
  38. package/project/.sdk/tm/js/src/Operation.js +19 -0
  39. package/project/.sdk/tm/js/src/Point.js +24 -0
  40. package/project/.sdk/tm/js/src/README.md +1 -0
  41. package/project/.sdk/tm/js/src/Response.js +19 -0
  42. package/project/.sdk/tm/js/src/Result.js +21 -0
  43. package/project/.sdk/tm/js/src/Spec.js +26 -0
  44. package/project/.sdk/tm/js/src/feature/README.md +1 -0
  45. package/project/.sdk/tm/js/src/feature/base/BaseFeature.js +45 -0
  46. package/project/.sdk/tm/js/src/feature/log/LogFeature.js +46 -47
  47. package/project/.sdk/tm/js/src/feature/test/TestFeature.js +207 -0
  48. package/project/.sdk/tm/js/src/types.js +22 -0
  49. package/project/.sdk/tm/js/src/utility/CleanUtility.js +31 -0
  50. package/project/.sdk/tm/js/src/utility/DoneUtility.js +11 -4
  51. package/project/.sdk/tm/js/src/utility/FeatureAddUtility.js +42 -0
  52. package/project/.sdk/tm/js/src/utility/FeatureHookUtility.js +25 -0
  53. package/project/.sdk/tm/js/src/utility/FeatureInitUtility.js +11 -0
  54. package/project/.sdk/tm/js/src/utility/FetcherUtility.js +28 -0
  55. package/project/.sdk/tm/js/src/utility/MakeContextUtility.js +11 -0
  56. package/project/.sdk/tm/js/src/utility/MakeErrorUtility.js +55 -0
  57. package/project/.sdk/tm/js/src/utility/MakeFetchDefUtility.js +44 -0
  58. package/project/.sdk/tm/js/src/utility/MakeOptionsUtility.js +93 -0
  59. package/project/.sdk/tm/js/src/utility/MakePointUtility.js +77 -0
  60. package/project/.sdk/tm/js/src/utility/MakeRequestUtility.js +63 -0
  61. package/project/.sdk/tm/js/src/utility/MakeResponseUtility.js +55 -0
  62. package/project/.sdk/tm/js/src/utility/MakeResultUtility.js +54 -0
  63. package/project/.sdk/tm/js/src/utility/MakeSpecUtility.js +58 -0
  64. package/project/.sdk/tm/js/src/utility/MakeUrlUtility.js +40 -0
  65. package/project/.sdk/tm/js/src/utility/ParamUtility.js +61 -0
  66. package/project/.sdk/tm/js/src/utility/PrepareAuthUtility.js +41 -0
  67. package/project/.sdk/tm/js/src/utility/PrepareBodyUtility.js +25 -0
  68. package/project/.sdk/tm/js/src/utility/PrepareHeadersUtility.js +18 -0
  69. package/project/.sdk/tm/js/src/utility/{MethodUtility.js → PrepareMethodUtility.js} +7 -7
  70. package/project/.sdk/tm/js/src/utility/PrepareParamsUtility.js +25 -0
  71. package/project/.sdk/tm/js/src/utility/PreparePathUtility.js +13 -0
  72. package/project/.sdk/tm/js/src/utility/PrepareQueryUtility.js +26 -0
  73. package/project/.sdk/tm/js/src/utility/README.md +1 -0
  74. package/project/.sdk/tm/js/src/utility/ResultBasicUtility.js +34 -0
  75. package/project/.sdk/tm/js/src/utility/ResultBodyUtility.js +18 -0
  76. package/project/.sdk/tm/js/src/utility/ResultHeadersUtility.js +22 -0
  77. package/project/.sdk/tm/js/src/utility/StructUtility.js +2219 -1078
  78. package/project/.sdk/tm/js/src/utility/TransformRequestUtility.js +28 -0
  79. package/project/.sdk/tm/js/src/utility/TransformResponseUtility.js +31 -0
  80. package/project/.sdk/tm/js/src/utility/Utility.js +61 -61
  81. package/project/.sdk/tm/js/test/README.md +1 -0
  82. package/project/.sdk/tm/js/test/exists.test.js +16 -0
  83. package/project/.sdk/tm/js/test/runner.js +323 -107
  84. package/project/.sdk/tm/js/test/utility/Custom.test.js +41 -63
  85. package/project/.sdk/tm/js/test/utility/PrimaryUtility.test.js +390 -116
  86. package/project/.sdk/tm/js/test/utility/StructUtility.test.js +728 -175
  87. package/project/.sdk/tm/js/test/utility/index.js +9 -0
  88. package/project/.sdk/tm/js/test/utility.js +72 -0
  89. package/project/.sdk/tm/lua/test/primary_utility_test.lua +1213 -0
  90. package/project/.sdk/tm/lua/test/runner.lua +2 -2
  91. package/project/.sdk/tm/lua/test/struct_runner.lua +602 -0
  92. package/project/.sdk/tm/lua/test/struct_utility_test.lua +959 -0
  93. package/project/.sdk/tm/lua/utility/struct/struct.lua +10 -0
  94. package/project/.sdk/tm/php/feature/TestFeature.php +59 -96
  95. package/project/.sdk/tm/php/test/PrimaryUtilityTest.php +1309 -0
  96. package/project/.sdk/tm/php/test/Runner.php +24 -1
  97. package/project/.sdk/tm/php/test/StructRunner.php +275 -0
  98. package/project/.sdk/tm/php/test/StructUtilityTest.php +1336 -0
  99. package/project/.sdk/tm/php/utility/Fetcher.php +6 -2
  100. package/project/.sdk/tm/php/utility/MakeOptions.php +5 -1
  101. package/project/.sdk/tm/php/utility/MakeResult.php +3 -0
  102. package/project/.sdk/tm/php/utility/Param.php +9 -7
  103. package/project/.sdk/tm/php/utility/struct/Struct.php +312 -208
  104. package/project/.sdk/tm/py/test/runner.py +13 -0
  105. package/project/.sdk/tm/py/test/struct_runner.py +411 -0
  106. package/project/.sdk/tm/py/test/test_primary_utility.py +1101 -0
  107. package/project/.sdk/tm/py/test/test_struct_utility.py +751 -0
  108. package/project/.sdk/tm/rb/test/primary_utility_test.rb +1083 -0
  109. package/project/.sdk/tm/rb/test/runner.rb +5 -0
  110. package/project/.sdk/tm/rb/test/struct_runner.rb +309 -0
  111. package/project/.sdk/tm/rb/test/struct_utility_test.rb +670 -0
  112. package/src/cmp/Main.ts +1 -16
  113. package/project/.sdk/src/cmp/js/Quick_js.ts +0 -78
  114. package/project/.sdk/src/cmp/js/TestAcceptEntity_js.ts +0 -13
  115. package/project/.sdk/src/cmp/js/TestAccept_js.ts +0 -18
  116. package/project/.sdk/tm/js/src/utility/AuthUtility.js +0 -21
  117. package/project/.sdk/tm/js/src/utility/BodyUtility.js +0 -29
  118. package/project/.sdk/tm/js/src/utility/ErrorUtility.js +0 -33
  119. package/project/.sdk/tm/js/src/utility/FindparamUtility.js +0 -31
  120. package/project/.sdk/tm/js/src/utility/FullurlUtility.js +0 -39
  121. package/project/.sdk/tm/js/src/utility/HeadersUtility.js +0 -13
  122. package/project/.sdk/tm/js/src/utility/JoinurlUtility.js +0 -14
  123. package/project/.sdk/tm/js/src/utility/OperatorUtility.js +0 -44
  124. package/project/.sdk/tm/js/src/utility/OptionsUtility.js +0 -54
  125. package/project/.sdk/tm/js/src/utility/ParamsUtility.js +0 -21
  126. package/project/.sdk/tm/js/src/utility/QueryUtility.js +0 -21
  127. package/project/.sdk/tm/js/src/utility/ReqformUtility.js +0 -32
  128. package/project/.sdk/tm/js/src/utility/RequestUtility.js +0 -48
  129. package/project/.sdk/tm/js/src/utility/ResbasicUtility.js +0 -27
  130. package/project/.sdk/tm/js/src/utility/ResbodyUtility.js +0 -15
  131. package/project/.sdk/tm/js/src/utility/ResformUtility.js +0 -34
  132. package/project/.sdk/tm/js/src/utility/ResheadersUtility.js +0 -19
  133. package/project/.sdk/tm/js/src/utility/ResponseUtility.js +0 -37
  134. package/project/.sdk/tm/js/src/utility/ResultUtility.js +0 -28
  135. package/project/.sdk/tm/js/src/utility/SpecUtility.js +0 -35
@@ -75,3 +75,16 @@ class ProjectNameTestRunner:
75
75
  else:
76
76
  out.append(item)
77
77
  return out
78
+
79
+
80
+ # Module-level convenience functions.
81
+ def load_env_local():
82
+ ProjectNameTestRunner.load_env_local()
83
+
84
+
85
+ def env_override(m):
86
+ return ProjectNameTestRunner.env_override(m)
87
+
88
+
89
+ def entity_list_to_data(lst):
90
+ return ProjectNameTestRunner.entity_list_to_data(lst)
@@ -0,0 +1,411 @@
1
+ # Vendored from voxgig/struct/py
2
+ # Test runner that uses the test model in build/test.
3
+
4
+ import os
5
+ import json
6
+ import re
7
+ from typing import Any, Dict, List, Callable, TypedDict, Optional, Union
8
+
9
+ from utility.voxgig_struct import voxgig_struct as vs
10
+
11
+
12
+ NULLMARK = '__NULL__' # Value is JSON null
13
+ UNDEFMARK = '__UNDEF__' # Value is not present (thus, undefined)
14
+ EXISTSMARK = '__EXISTS__' # Value exists (not undefined).
15
+
16
+
17
+ class RunPack(TypedDict):
18
+ spec: Dict[str, Any]
19
+ runset: Callable
20
+ runsetflags: Callable
21
+ subject: Callable
22
+ client: Optional[Any]
23
+
24
+
25
+ def makeRunner(testfile: str, client: Any):
26
+
27
+ def runner(
28
+ name: str,
29
+ store: Any = None,
30
+ ) -> RunPack:
31
+ store = store or {}
32
+
33
+ utility = client.utility()
34
+ structUtils = utility.struct
35
+
36
+ spec = resolve_spec(name, testfile)
37
+ clients = resolve_clients(client, spec, store, structUtils)
38
+ subject = resolve_subject(name, utility)
39
+
40
+ def runsetflags(testspec, flags, testsubject):
41
+ nonlocal subject, clients
42
+
43
+ subject = testsubject or subject
44
+ flags = resolve_flags(flags)
45
+ testspecmap = fixJSON(testspec, flags)
46
+ testset = testspecmap['set']
47
+
48
+ for entry in testset:
49
+ try:
50
+ entry = resolve_entry(entry, flags)
51
+
52
+ testpack = resolve_testpack(name, entry, subject, client, clients)
53
+ args = resolve_args(entry, testpack, utility, structUtils)
54
+
55
+ # Execute the test function
56
+ res = testpack["subject"](*args)
57
+ res = fixJSON(res, flags)
58
+ entry['res'] = res
59
+ check_result(entry, args, res, structUtils)
60
+
61
+ except Exception as err:
62
+ handle_error(entry, err, structUtils)
63
+
64
+ def runset(testspec, testsubject):
65
+ return runsetflags(testspec, {}, testsubject)
66
+
67
+ runpack = {
68
+ "spec": spec,
69
+ "runset": runset,
70
+ "runsetflags": runsetflags,
71
+ "subject": subject,
72
+ "client": client
73
+ }
74
+
75
+ return runpack
76
+
77
+ return runner
78
+
79
+
80
+ def resolve_spec(name: str, testfile: str) -> Dict[str, Any]:
81
+ with open(testfile, 'r', encoding='utf-8') as f:
82
+ alltests = json.load(f)
83
+
84
+ if 'primary' in alltests and name in alltests['primary']:
85
+ spec = alltests['primary'][name]
86
+ elif name in alltests:
87
+ spec = alltests[name]
88
+ else:
89
+ spec = alltests
90
+
91
+ return spec
92
+
93
+
94
+ def resolve_clients(client: Any, spec: Dict[str, Any], store: Any, structUtils: Any) -> Dict[str, Any]:
95
+ clients = {}
96
+ if 'DEF' in spec and 'client' in spec['DEF']:
97
+ for client_name, client_val in structUtils.items(spec['DEF']['client']):
98
+ # Get client options
99
+ client_opts = client_val.get('test', {}).get('options', {})
100
+
101
+ # Apply store injections if needed
102
+ if isinstance(store, dict) and structUtils.inject:
103
+ structUtils.inject(client_opts, store)
104
+
105
+ # Create and store the client using the passed client object
106
+ clients[client_name] = client.tester(client_opts)
107
+
108
+ return clients
109
+
110
+
111
+ def resolve_subject(name: str, container: Any):
112
+ return getattr(container, name, getattr(container.struct, name, None))
113
+
114
+
115
+ def check_result(entry, args, res, structUtils):
116
+ matched = False
117
+
118
+ if 'match' in entry:
119
+ result = {'in': entry.get('in'), 'args': args, 'out': entry.get('res'), 'ctx': entry.get('ctx')}
120
+ match(
121
+ entry['match'],
122
+ result,
123
+ structUtils
124
+ )
125
+ matched = True
126
+
127
+ out = entry.get('out')
128
+
129
+ if out == res:
130
+ return
131
+
132
+ # NOTE: allow match with no out
133
+ if matched and (NULLMARK == out or out is None):
134
+ return
135
+
136
+ try:
137
+ cleaned_res = json.loads(json.dumps(res, default=str))
138
+ except:
139
+ # If can't be serialized just use the original
140
+ cleaned_res = res
141
+
142
+ # Compare result with expected output using deep equality
143
+ if cleaned_res != out:
144
+ raise AssertionError(
145
+ f"Expected: {out}, got: {cleaned_res}\n"
146
+ f"Test: {entry.get('name', 'unknown')}"
147
+ )
148
+
149
+
150
+ def handle_error(entry, err, structUtils):
151
+ # Record the error in the entry
152
+ entry['thrown'] = err
153
+ entry_err = entry.get('err')
154
+
155
+ # If the test expects an error
156
+ if entry_err is not None:
157
+ # If it's any error or matches expected pattern
158
+ if entry_err is True or matchval(entry_err, str(err), structUtils):
159
+ # If we also need to match error details
160
+ if 'match' in entry:
161
+ match(
162
+ entry['match'],
163
+ {
164
+ 'in': entry.get('in'),
165
+ 'out': entry.get('res'),
166
+ 'ctx': entry.get('ctx'),
167
+ 'err': fixJSON(err)
168
+ },
169
+ structUtils
170
+ )
171
+ # Error was expected, continue
172
+ return True
173
+
174
+ # Expected error didn't match the actual error
175
+ raise AssertionError(
176
+ f"ERROR MATCH: [{structUtils.stringify(entry_err)}] <=> [{str(err)}]"
177
+ )
178
+ # If the test doesn't expect an error
179
+ elif isinstance(err, AssertionError):
180
+ # Propagate assertion errors with added context
181
+ raise AssertionError(
182
+ f"{str(err)}\nTest: {entry.get('name', 'unknown')}"
183
+ )
184
+ else:
185
+ # For other errors, include the full error stack
186
+ import traceback
187
+ raise AssertionError(
188
+ f"{traceback.format_exc()}\nTest: {entry.get('name', 'unknown')}"
189
+ )
190
+
191
+
192
+ def resolve_testpack(
193
+ name,
194
+ entry,
195
+ subject,
196
+ client,
197
+ clients,
198
+ ):
199
+ testpack = {
200
+ "client": client,
201
+ "subject": subject,
202
+ "utility": client.utility(),
203
+ }
204
+
205
+ if 'client' in entry:
206
+ testpack["client"] = clients[entry['client']]
207
+ testpack["utility"] = testpack["client"].utility()
208
+ testpack["subject"] = resolve_subject(name, testpack["utility"])
209
+
210
+ return testpack
211
+
212
+
213
+ def resolve_args(entry, testpack, utility, structUtils):
214
+ args = []
215
+
216
+ if 'ctx' in entry:
217
+ args = [entry['ctx']]
218
+ elif 'args' in entry:
219
+ args = entry['args']
220
+ elif 'in' in entry:
221
+ args = [structUtils.clone(entry['in'])]
222
+
223
+ # If we have context or arguments, we might need to patch them
224
+ if ('ctx' in entry or 'args' in entry) and len(args) > 0:
225
+ first = args[0]
226
+ if structUtils.ismap(first):
227
+ # Clone the argument
228
+ first = structUtils.clone(first)
229
+ first = utility.contextify(first)
230
+ args[0] = first
231
+ entry['ctx'] = first
232
+ first.client = testpack["client"]
233
+ first.utility = testpack["utility"]
234
+
235
+ return args
236
+
237
+
238
+ def resolve_flags(flags: Dict[str, Any] = None) -> Dict[str, bool]:
239
+ if flags is None:
240
+ flags = {}
241
+
242
+ flags["null"] = flags.get("null", True)
243
+
244
+ return flags
245
+
246
+
247
+ def resolve_entry(entry: Dict[str, Any], flags: Dict[str, bool]) -> Dict[str, Any]:
248
+ # Set default output value for missing 'out' field
249
+ if 'out' not in entry and flags.get("null", True):
250
+ entry["out"] = NULLMARK
251
+
252
+ return entry
253
+
254
+
255
+ def fixJSON(obj, flags={}):
256
+ # Handle nulls
257
+ if obj is None:
258
+ return NULLMARK if flags.get("null", True) else None
259
+
260
+ # Handle errors
261
+ if isinstance(obj, Exception):
262
+ return {
263
+ **vars(obj),
264
+ 'name': type(obj).__name__,
265
+ 'message': str(obj)
266
+ }
267
+
268
+ # Handle collections recursively
269
+ elif isinstance(obj, list):
270
+ return [fixJSON(item, flags) for item in obj]
271
+ elif isinstance(obj, dict):
272
+ return {k: fixJSON(v, flags) for k, v in obj.items()}
273
+
274
+ # Return everything else unchanged
275
+ return obj
276
+
277
+
278
+ def jsonfallback(obj):
279
+ return f"<non-serializable: {type(obj).__name__}>"
280
+
281
+
282
+ def match(check, base, structUtils):
283
+ base = structUtils.clone(base)
284
+
285
+ # Use walk function to iterate through the check structure
286
+ def walk_apply(_key, val, _parent, path):
287
+ if not structUtils.isnode(val):
288
+ baseval = structUtils.getpath(base, path)
289
+
290
+ if baseval == val:
291
+ return val
292
+
293
+ # Explicit undefined expected
294
+ if UNDEFMARK == val and baseval is None:
295
+ return val
296
+
297
+ # Explicit defined expected
298
+ if EXISTSMARK == val and baseval is not None:
299
+ return val
300
+
301
+ if not matchval(val, baseval, structUtils):
302
+ raise AssertionError(
303
+ f"MATCH: {'.'.join(map(str, path))}: "
304
+ f"[{structUtils.stringify(val)}] <=> [{structUtils.stringify(baseval)}]"
305
+ )
306
+ return val
307
+
308
+ # Use walk to apply the check function to each node
309
+ structUtils.walk(check, walk_apply)
310
+
311
+
312
+ def matchval(check, base, structUtils):
313
+ # Handle undefined special case
314
+ if check == '__UNDEF__' or check == NULLMARK:
315
+ check = None
316
+
317
+ if check == base:
318
+ return True
319
+
320
+ # String-based pattern matching
321
+ if isinstance(check, str):
322
+ # Convert base to string for comparison
323
+ base_str = structUtils.stringify(base)
324
+
325
+ # Check for regex pattern with /pattern/ syntax
326
+ regex_match = re.match(r'^/(.+)/$', check)
327
+
328
+ if regex_match:
329
+ pattern = regex_match.group(1)
330
+ return re.search(pattern, base_str) is not None
331
+ else:
332
+ # Case-insensitive substring check
333
+ return structUtils.stringify(check).lower() in base_str.lower()
334
+
335
+ # Functions automatically pass
336
+ elif callable(check):
337
+ return True
338
+
339
+ # No match
340
+ return False
341
+
342
+
343
+ def nullModifier(val, key, parent, _state=None, _current=None, _store=None):
344
+ if NULLMARK == val:
345
+ parent[key] = None
346
+ elif isinstance(val, str):
347
+ parent[key] = val.replace(NULLMARK, "null")
348
+
349
+
350
+ # StructTestClient shim for running struct tests in the SDK context.
351
+ # Wraps voxgig_struct as the struct utility, providing the client
352
+ # interface expected by the runner.
353
+
354
+ class StructContext:
355
+ def __init__(self):
356
+ self.client = None
357
+ self.utility = None
358
+ self.meta = {}
359
+
360
+
361
+ class StructTestUtility:
362
+ def __init__(self, opts=None):
363
+ self._opts = opts
364
+ self.struct = vs.StructUtility()
365
+
366
+ def contextify(self, ctxmap):
367
+ ctx = StructContext()
368
+ meta = ctxmap.get('meta', {})
369
+ for k, v in meta.items():
370
+ ctx.meta[k] = v
371
+ return ctx
372
+
373
+ def check(self, ctx):
374
+ zed = "ZED"
375
+
376
+ if self._opts is None:
377
+ zed += ""
378
+ else:
379
+ foo = self._opts.get("foo")
380
+ zed += "0" if foo is None else str(foo)
381
+
382
+ zed += "_"
383
+ zed += str(ctx.meta.get("bar"))
384
+
385
+ return {"zed": zed}
386
+
387
+
388
+ class StructTestClient:
389
+ def __init__(self, opts=None):
390
+ self._opts = opts or {}
391
+ self._utility = StructTestUtility(opts)
392
+
393
+ @staticmethod
394
+ def test(opts=None):
395
+ return StructTestClient(opts)
396
+
397
+ def tester(self, opts=None):
398
+ return StructTestClient(self._opts if opts is None else opts)
399
+
400
+ def utility(self):
401
+ return self._utility
402
+
403
+
404
+ # Export the necessary components similar to TypeScript
405
+ __all__ = [
406
+ 'NULLMARK',
407
+ 'UNDEFMARK',
408
+ 'nullModifier',
409
+ 'makeRunner',
410
+ 'StructTestClient',
411
+ ]