@workos/oagen-emitters 0.2.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.
- package/.husky/pre-commit +1 -0
- package/.oxfmtrc.json +8 -1
- package/.release-please-manifest.json +1 -1
- package/CHANGELOG.md +15 -0
- package/README.md +129 -0
- package/dist/index.d.mts +10 -1
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +11943 -2728
- package/dist/index.mjs.map +1 -1
- package/docs/sdk-architecture/go.md +338 -0
- package/docs/sdk-architecture/php.md +315 -0
- package/docs/sdk-architecture/python.md +511 -0
- package/oagen.config.ts +298 -2
- package/package.json +9 -5
- package/scripts/generate-php.js +13 -0
- package/scripts/git-push-with-published-oagen.sh +21 -0
- package/smoke/sdk-dotnet.ts +17 -3
- package/smoke/sdk-elixir.ts +17 -3
- package/smoke/sdk-go.ts +137 -46
- package/smoke/sdk-kotlin.ts +23 -4
- package/smoke/sdk-node.ts +15 -3
- package/smoke/sdk-php.ts +28 -26
- package/smoke/sdk-python.ts +5 -2
- package/smoke/sdk-ruby.ts +17 -3
- package/smoke/sdk-rust.ts +16 -3
- package/src/go/client.ts +141 -0
- package/src/go/enums.ts +196 -0
- package/src/go/fixtures.ts +212 -0
- package/src/go/index.ts +81 -0
- package/src/go/manifest.ts +36 -0
- package/src/go/models.ts +254 -0
- package/src/go/naming.ts +191 -0
- package/src/go/resources.ts +827 -0
- package/src/go/tests.ts +751 -0
- package/src/go/type-map.ts +82 -0
- package/src/go/wrappers.ts +261 -0
- package/src/index.ts +3 -0
- package/src/node/client.ts +167 -122
- package/src/node/enums.ts +13 -4
- package/src/node/errors.ts +42 -233
- package/src/node/field-plan.ts +726 -0
- package/src/node/fixtures.ts +15 -5
- package/src/node/index.ts +65 -16
- package/src/node/models.ts +264 -96
- package/src/node/naming.ts +52 -25
- package/src/node/resources.ts +621 -172
- package/src/node/sdk-errors.ts +41 -0
- package/src/node/tests.ts +71 -27
- package/src/node/type-map.ts +4 -2
- package/src/node/utils.ts +56 -64
- package/src/node/wrappers.ts +151 -0
- package/src/php/client.ts +171 -0
- package/src/php/enums.ts +67 -0
- package/src/php/errors.ts +9 -0
- package/src/php/fixtures.ts +181 -0
- package/src/php/index.ts +96 -0
- package/src/php/manifest.ts +36 -0
- package/src/php/models.ts +310 -0
- package/src/php/naming.ts +298 -0
- package/src/php/resources.ts +561 -0
- package/src/php/tests.ts +533 -0
- package/src/php/type-map.ts +90 -0
- package/src/php/utils.ts +18 -0
- package/src/php/wrappers.ts +151 -0
- package/src/python/client.ts +337 -0
- package/src/python/enums.ts +313 -0
- package/src/python/fixtures.ts +196 -0
- package/src/python/index.ts +95 -0
- package/src/python/manifest.ts +38 -0
- package/src/python/models.ts +688 -0
- package/src/python/naming.ts +209 -0
- package/src/python/resources.ts +1322 -0
- package/src/python/tests.ts +1335 -0
- package/src/python/type-map.ts +93 -0
- package/src/python/wrappers.ts +191 -0
- package/src/shared/model-utils.ts +255 -0
- package/src/shared/naming-utils.ts +107 -0
- package/src/shared/non-spec-services.ts +54 -0
- package/src/shared/resolved-ops.ts +109 -0
- package/src/shared/wrapper-utils.ts +59 -0
- package/test/go/client.test.ts +92 -0
- package/test/go/enums.test.ts +132 -0
- package/test/go/errors.test.ts +9 -0
- package/test/go/models.test.ts +265 -0
- package/test/go/resources.test.ts +408 -0
- package/test/go/tests.test.ts +143 -0
- package/test/node/client.test.ts +199 -94
- package/test/node/enums.test.ts +75 -3
- package/test/node/errors.test.ts +2 -41
- package/test/node/models.test.ts +109 -20
- package/test/node/naming.test.ts +37 -4
- package/test/node/resources.test.ts +662 -30
- package/test/node/serializers.test.ts +36 -7
- package/test/node/type-map.test.ts +11 -0
- package/test/php/client.test.ts +94 -0
- package/test/php/enums.test.ts +173 -0
- package/test/php/errors.test.ts +9 -0
- package/test/php/models.test.ts +497 -0
- package/test/php/resources.test.ts +644 -0
- package/test/php/tests.test.ts +118 -0
- package/test/python/client.test.ts +200 -0
- package/test/python/enums.test.ts +228 -0
- package/test/python/errors.test.ts +16 -0
- package/test/python/manifest.test.ts +74 -0
- package/test/python/models.test.ts +716 -0
- package/test/python/resources.test.ts +617 -0
- package/test/python/tests.test.ts +202 -0
- package/src/node/common.ts +0 -273
- package/src/node/config.ts +0 -71
- package/src/node/serializers.ts +0 -744
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { describe, it, expect } from 'vitest';
|
|
2
|
-
import { generateSerializers } from '../../src/node/
|
|
2
|
+
import { generateSerializers } from '../../src/node/models.js';
|
|
3
3
|
import type { EmitterContext, ApiSpec, Model, Service } from '@workos/oagen';
|
|
4
|
+
import { defaultSdkBehavior } from '@workos/oagen';
|
|
4
5
|
|
|
5
6
|
const emptySpec: ApiSpec = {
|
|
6
7
|
name: 'Test',
|
|
@@ -9,6 +10,7 @@ const emptySpec: ApiSpec = {
|
|
|
9
10
|
services: [],
|
|
10
11
|
models: [],
|
|
11
12
|
enums: [],
|
|
13
|
+
sdk: defaultSdkBehavior(),
|
|
12
14
|
};
|
|
13
15
|
|
|
14
16
|
const ctx: EmitterContext = {
|
|
@@ -30,7 +32,13 @@ describe('generateSerializers', () => {
|
|
|
30
32
|
name: 'getOrganization',
|
|
31
33
|
httpMethod: 'get',
|
|
32
34
|
path: '/organizations/{id}',
|
|
33
|
-
pathParams: [
|
|
35
|
+
pathParams: [
|
|
36
|
+
{
|
|
37
|
+
name: 'id',
|
|
38
|
+
type: { kind: 'primitive', type: 'string' },
|
|
39
|
+
required: true,
|
|
40
|
+
},
|
|
41
|
+
],
|
|
34
42
|
queryParams: [],
|
|
35
43
|
headerParams: [],
|
|
36
44
|
response: { kind: 'model', name: 'Organization' },
|
|
@@ -90,7 +98,13 @@ describe('generateSerializers', () => {
|
|
|
90
98
|
name: 'getOrganization',
|
|
91
99
|
httpMethod: 'get',
|
|
92
100
|
path: '/organizations/{id}',
|
|
93
|
-
pathParams: [
|
|
101
|
+
pathParams: [
|
|
102
|
+
{
|
|
103
|
+
name: 'id',
|
|
104
|
+
type: { kind: 'primitive', type: 'string' },
|
|
105
|
+
required: true,
|
|
106
|
+
},
|
|
107
|
+
],
|
|
94
108
|
queryParams: [],
|
|
95
109
|
headerParams: [],
|
|
96
110
|
response: { kind: 'model', name: 'Organization' },
|
|
@@ -151,7 +165,13 @@ describe('generateSerializers', () => {
|
|
|
151
165
|
name: 'getOrganization',
|
|
152
166
|
httpMethod: 'get',
|
|
153
167
|
path: '/organizations/{id}',
|
|
154
|
-
pathParams: [
|
|
168
|
+
pathParams: [
|
|
169
|
+
{
|
|
170
|
+
name: 'id',
|
|
171
|
+
type: { kind: 'primitive', type: 'string' },
|
|
172
|
+
required: true,
|
|
173
|
+
},
|
|
174
|
+
],
|
|
155
175
|
queryParams: [],
|
|
156
176
|
headerParams: [],
|
|
157
177
|
response: { kind: 'model', name: 'Organization' },
|
|
@@ -303,12 +323,18 @@ describe('generateSerializers', () => {
|
|
|
303
323
|
fields: [
|
|
304
324
|
{
|
|
305
325
|
name: 'before',
|
|
306
|
-
type: {
|
|
326
|
+
type: {
|
|
327
|
+
kind: 'nullable',
|
|
328
|
+
inner: { kind: 'primitive', type: 'string' },
|
|
329
|
+
},
|
|
307
330
|
required: false,
|
|
308
331
|
},
|
|
309
332
|
{
|
|
310
333
|
name: 'after',
|
|
311
|
-
type: {
|
|
334
|
+
type: {
|
|
335
|
+
kind: 'nullable',
|
|
336
|
+
inner: { kind: 'primitive', type: 'string' },
|
|
337
|
+
},
|
|
312
338
|
required: false,
|
|
313
339
|
},
|
|
314
340
|
],
|
|
@@ -375,7 +401,10 @@ describe('generateSerializers', () => {
|
|
|
375
401
|
},
|
|
376
402
|
{
|
|
377
403
|
name: 'data',
|
|
378
|
-
type: {
|
|
404
|
+
type: {
|
|
405
|
+
kind: 'array',
|
|
406
|
+
items: { kind: 'model', name: 'Connection' },
|
|
407
|
+
},
|
|
379
408
|
required: true,
|
|
380
409
|
},
|
|
381
410
|
{
|
|
@@ -60,6 +60,17 @@ describe('mapTypeRef', () => {
|
|
|
60
60
|
expect(mapTypeRef(ref)).toBe('string | number');
|
|
61
61
|
});
|
|
62
62
|
|
|
63
|
+
it('deduplicates union variants', () => {
|
|
64
|
+
const ref: TypeRef = {
|
|
65
|
+
kind: 'union',
|
|
66
|
+
variants: [
|
|
67
|
+
{ kind: 'model', name: 'AuthenticationFactorTotp' },
|
|
68
|
+
{ kind: 'model', name: 'AuthenticationFactorTotp' },
|
|
69
|
+
],
|
|
70
|
+
};
|
|
71
|
+
expect(mapTypeRef(ref)).toBe('AuthenticationFactorTotp');
|
|
72
|
+
});
|
|
73
|
+
|
|
63
74
|
it('maps map type', () => {
|
|
64
75
|
const ref: TypeRef = {
|
|
65
76
|
kind: 'map',
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import type { EmitterContext, ApiSpec, Service, Model } from '@workos/oagen';
|
|
3
|
+
import { defaultSdkBehavior } from '@workos/oagen';
|
|
4
|
+
import { generateClient } from '../../src/php/client.js';
|
|
5
|
+
|
|
6
|
+
const models: Model[] = [
|
|
7
|
+
{
|
|
8
|
+
name: 'Organization',
|
|
9
|
+
fields: [{ name: 'id', type: { kind: 'primitive', type: 'string' }, required: true }],
|
|
10
|
+
},
|
|
11
|
+
];
|
|
12
|
+
|
|
13
|
+
const services: Service[] = [
|
|
14
|
+
{
|
|
15
|
+
name: 'Organizations',
|
|
16
|
+
operations: [
|
|
17
|
+
{
|
|
18
|
+
name: 'listOrganizations',
|
|
19
|
+
httpMethod: 'get',
|
|
20
|
+
path: '/organizations',
|
|
21
|
+
pathParams: [],
|
|
22
|
+
queryParams: [],
|
|
23
|
+
headerParams: [],
|
|
24
|
+
response: { kind: 'model', name: 'Organization' },
|
|
25
|
+
errors: [],
|
|
26
|
+
injectIdempotencyKey: false,
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
];
|
|
31
|
+
|
|
32
|
+
const emptySpec: ApiSpec = {
|
|
33
|
+
name: 'Test',
|
|
34
|
+
version: '1.0.0',
|
|
35
|
+
baseUrl: 'https://api.example.com',
|
|
36
|
+
services,
|
|
37
|
+
models,
|
|
38
|
+
enums: [],
|
|
39
|
+
sdk: defaultSdkBehavior(),
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const ctx: EmitterContext = {
|
|
43
|
+
namespace: 'workos',
|
|
44
|
+
namespacePascal: 'WorkOS',
|
|
45
|
+
spec: emptySpec,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
describe('generateClient', () => {
|
|
49
|
+
it('only generates the main client file', () => {
|
|
50
|
+
const result = generateClient(emptySpec, ctx);
|
|
51
|
+
|
|
52
|
+
expect(result).toHaveLength(1);
|
|
53
|
+
expect(result[0].path).toBe('lib/WorkOS.php');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
it('generates main client class with namespace', () => {
|
|
57
|
+
const result = generateClient(emptySpec, ctx);
|
|
58
|
+
|
|
59
|
+
expect(result[0].content).toContain('class WorkOS');
|
|
60
|
+
expect(result[0].content).toContain('namespace WorkOS;');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('generates resource accessor methods', () => {
|
|
64
|
+
const result = generateClient(emptySpec, ctx);
|
|
65
|
+
|
|
66
|
+
expect(result[0].content).toContain('public function organizations(): Organizations');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('includes constructor with config options', () => {
|
|
70
|
+
const result = generateClient(emptySpec, ctx);
|
|
71
|
+
|
|
72
|
+
expect(result[0].content).toContain('?string $apiKey = null');
|
|
73
|
+
expect(result[0].content).toContain('?string $clientId = null');
|
|
74
|
+
expect(result[0].content).toContain("string $baseUrl = 'https://api.example.com'");
|
|
75
|
+
expect(result[0].content).toContain('int $timeout = 60');
|
|
76
|
+
expect(result[0].content).toContain('int $maxRetries = 3');
|
|
77
|
+
expect(result[0].content).toContain(
|
|
78
|
+
'new HttpClient($apiKey, $clientId, $baseUrl, $timeout, $maxRetries, $handler)',
|
|
79
|
+
);
|
|
80
|
+
expect(result[0].content).not.toContain('self::$apiKey = $apiKey;');
|
|
81
|
+
expect(result[0].content).not.toContain('self::$clientId = $clientId;');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('includes non-spec service accessors', () => {
|
|
85
|
+
const result = generateClient(emptySpec, ctx);
|
|
86
|
+
|
|
87
|
+
expect(result[0].content).toContain('public function passwordless(): Passwordless');
|
|
88
|
+
expect(result[0].content).toContain('public function vault(): Vault');
|
|
89
|
+
expect(result[0].content).toContain('public function webhookVerification(): WebhookVerification');
|
|
90
|
+
expect(result[0].content).toContain('public function actions(): Actions');
|
|
91
|
+
expect(result[0].content).toContain('public function sessionManager(): SessionManager');
|
|
92
|
+
expect(result[0].content).toContain('public function pkce(): PKCEHelper');
|
|
93
|
+
});
|
|
94
|
+
});
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import type { EmitterContext, ApiSpec, Enum } from '@workos/oagen';
|
|
3
|
+
import { defaultSdkBehavior } from '@workos/oagen';
|
|
4
|
+
import { generateEnums } from '../../src/php/enums.js';
|
|
5
|
+
import { initializeEnumDedup } from '../../src/php/naming.js';
|
|
6
|
+
|
|
7
|
+
const emptySpec: ApiSpec = {
|
|
8
|
+
name: 'Test',
|
|
9
|
+
version: '1.0.0',
|
|
10
|
+
baseUrl: '',
|
|
11
|
+
services: [],
|
|
12
|
+
models: [],
|
|
13
|
+
enums: [],
|
|
14
|
+
sdk: defaultSdkBehavior(),
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const ctx: EmitterContext = {
|
|
18
|
+
namespace: 'workos',
|
|
19
|
+
namespacePascal: 'WorkOS',
|
|
20
|
+
spec: emptySpec,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
describe('generateEnums', () => {
|
|
24
|
+
it('returns empty array for no enums', () => {
|
|
25
|
+
expect(generateEnums([], ctx)).toEqual([]);
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('generates a string-backed enum', () => {
|
|
29
|
+
const enums: Enum[] = [
|
|
30
|
+
{
|
|
31
|
+
name: 'OrganizationStatus',
|
|
32
|
+
values: [
|
|
33
|
+
{ name: 'ACTIVE', value: 'active' },
|
|
34
|
+
{ name: 'INACTIVE', value: 'inactive' },
|
|
35
|
+
],
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
|
|
39
|
+
const result = generateEnums(enums, ctx);
|
|
40
|
+
|
|
41
|
+
expect(result).toHaveLength(1);
|
|
42
|
+
expect(result[0].path).toBe('lib/Resource/OrganizationStatus.php');
|
|
43
|
+
expect(result[0].content).toContain('enum OrganizationStatus: string');
|
|
44
|
+
expect(result[0].content).toContain("case Active = 'active';");
|
|
45
|
+
expect(result[0].content).toContain("case Inactive = 'inactive';");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('generates an int-backed enum', () => {
|
|
49
|
+
const enums: Enum[] = [
|
|
50
|
+
{
|
|
51
|
+
name: 'Priority',
|
|
52
|
+
values: [
|
|
53
|
+
{ name: 'LOW', value: 1 },
|
|
54
|
+
{ name: 'MEDIUM', value: 2 },
|
|
55
|
+
{ name: 'HIGH', value: 3 },
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
const result = generateEnums(enums, ctx);
|
|
61
|
+
|
|
62
|
+
expect(result[0].content).toContain('enum Priority: int');
|
|
63
|
+
expect(result[0].content).toContain('case Low = 1;');
|
|
64
|
+
expect(result[0].content).toContain('case Medium = 2;');
|
|
65
|
+
expect(result[0].content).toContain('case High = 3;');
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('generates correct namespace', () => {
|
|
69
|
+
const enums: Enum[] = [
|
|
70
|
+
{
|
|
71
|
+
name: 'Status',
|
|
72
|
+
values: [{ name: 'ACTIVE', value: 'active' }],
|
|
73
|
+
},
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
const result = generateEnums(enums, ctx);
|
|
77
|
+
|
|
78
|
+
expect(result[0].content).toContain('namespace WorkOS\\Resource;');
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('collapses duplicate enums with identical values into one file', () => {
|
|
82
|
+
const enums: Enum[] = [
|
|
83
|
+
{
|
|
84
|
+
name: 'Order',
|
|
85
|
+
values: [
|
|
86
|
+
{ name: 'ASC', value: 'asc' },
|
|
87
|
+
{ name: 'DESC', value: 'desc' },
|
|
88
|
+
],
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: 'ConnectionOrder',
|
|
92
|
+
values: [
|
|
93
|
+
{ name: 'ASC', value: 'asc' },
|
|
94
|
+
{ name: 'DESC', value: 'desc' },
|
|
95
|
+
],
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: 'ApiKeyOrder',
|
|
99
|
+
values: [
|
|
100
|
+
{ name: 'ASC', value: 'asc' },
|
|
101
|
+
{ name: 'DESC', value: 'desc' },
|
|
102
|
+
],
|
|
103
|
+
},
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
// Initialize dedup before generating
|
|
107
|
+
initializeEnumDedup(enums);
|
|
108
|
+
const result = generateEnums(enums, ctx);
|
|
109
|
+
|
|
110
|
+
// Should produce only one file (the shortest name: Order)
|
|
111
|
+
expect(result).toHaveLength(1);
|
|
112
|
+
expect(result[0].path).toBe('lib/Resource/Order.php');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
it('adds PHPDoc @deprecated for deprecated enum values', () => {
|
|
116
|
+
const enums: Enum[] = [
|
|
117
|
+
{
|
|
118
|
+
name: 'ConnectionType',
|
|
119
|
+
values: [
|
|
120
|
+
{ name: 'SAML', value: 'saml' },
|
|
121
|
+
{ name: 'OAUTH', value: 'oauth', deprecated: true },
|
|
122
|
+
],
|
|
123
|
+
},
|
|
124
|
+
];
|
|
125
|
+
|
|
126
|
+
const result = generateEnums(enums, ctx);
|
|
127
|
+
|
|
128
|
+
expect(result).toHaveLength(1);
|
|
129
|
+
// The non-deprecated value should not have a PHPDoc
|
|
130
|
+
expect(result[0].content).not.toContain('/** @deprecated */\n case Saml');
|
|
131
|
+
// The deprecated value should have a PHPDoc
|
|
132
|
+
expect(result[0].content).toContain('/** @deprecated */');
|
|
133
|
+
// Verify the deprecated case follows the PHPDoc
|
|
134
|
+
const lines = result[0].content.split('\n');
|
|
135
|
+
const deprecatedIdx = lines.findIndex((l: string) => l.includes('@deprecated'));
|
|
136
|
+
expect(deprecatedIdx).toBeGreaterThan(-1);
|
|
137
|
+
expect(lines[deprecatedIdx + 1]).toContain("= 'oauth';");
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('adds PHPDoc with description and @deprecated for enum values', () => {
|
|
141
|
+
const enums: Enum[] = [
|
|
142
|
+
{
|
|
143
|
+
name: 'ConnectionType',
|
|
144
|
+
values: [
|
|
145
|
+
{ name: 'SAML', value: 'saml' },
|
|
146
|
+
{ name: 'OAUTH', value: 'oauth', description: 'Use OIDC instead', deprecated: true },
|
|
147
|
+
],
|
|
148
|
+
},
|
|
149
|
+
];
|
|
150
|
+
|
|
151
|
+
const result = generateEnums(enums, ctx);
|
|
152
|
+
|
|
153
|
+
expect(result[0].content).toContain('Use OIDC instead');
|
|
154
|
+
expect(result[0].content).toContain('@deprecated');
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('deduplicates case names', () => {
|
|
158
|
+
const enums: Enum[] = [
|
|
159
|
+
{
|
|
160
|
+
name: 'DupEnum',
|
|
161
|
+
values: [
|
|
162
|
+
{ name: 'FOO_BAR', value: 'foo_bar' },
|
|
163
|
+
{ name: 'FOO__BAR', value: 'foo__bar' },
|
|
164
|
+
],
|
|
165
|
+
},
|
|
166
|
+
];
|
|
167
|
+
|
|
168
|
+
const result = generateEnums(enums, ctx);
|
|
169
|
+
|
|
170
|
+
expect(result[0].content).toContain('case FooBar =');
|
|
171
|
+
expect(result[0].content).toContain('case FooBar2 =');
|
|
172
|
+
});
|
|
173
|
+
});
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { generateErrors } from '../../src/php/errors.js';
|
|
3
|
+
|
|
4
|
+
describe('generateErrors', () => {
|
|
5
|
+
it('returns empty array (errors are now hand-maintained)', () => {
|
|
6
|
+
const result = generateErrors();
|
|
7
|
+
expect(result).toEqual([]);
|
|
8
|
+
});
|
|
9
|
+
});
|