yini-parser 1.3.2-beta → 1.4.0-beta

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 (36) hide show
  1. package/CHANGELOG.md +40 -1
  2. package/README.md +73 -186
  3. package/dist/YINI.d.ts +4 -4
  4. package/dist/YINI.js +0 -120
  5. package/dist/YINI.js.map +1 -1
  6. package/dist/core/astBuilder.d.ts +2 -0
  7. package/dist/core/astBuilder.js +97 -14
  8. package/dist/core/astBuilder.js.map +1 -1
  9. package/dist/core/internalTypes.d.ts +5 -0
  10. package/dist/core/options/defaultParserOptions.js +1 -7
  11. package/dist/core/options/defaultParserOptions.js.map +1 -1
  12. package/dist/core/options/optionsFunctions.js +7 -5
  13. package/dist/core/options/optionsFunctions.js.map +1 -1
  14. package/dist/core/resultMetadataBuilder.js +0 -1
  15. package/dist/core/resultMetadataBuilder.js.map +1 -1
  16. package/dist/dev/main.d.ts +1 -1
  17. package/dist/dev/main.js +27 -5
  18. package/dist/dev/main.js.map +1 -1
  19. package/dist/dev/quick-test-samples/defect-inputs.d.ts +37 -0
  20. package/dist/dev/quick-test-samples/defect-inputs.js +106 -0
  21. package/dist/dev/quick-test-samples/defect-inputs.js.map +1 -0
  22. package/dist/dev/quick-test-samples/valid-inputs.d.ts +21 -0
  23. package/dist/dev/quick-test-samples/valid-inputs.js +422 -0
  24. package/dist/dev/quick-test-samples/valid-inputs.js.map +1 -0
  25. package/dist/index.js +1 -1
  26. package/dist/index.js.map +1 -1
  27. package/dist/parsers/parseString.d.ts +2 -1
  28. package/dist/parsers/parseString.js +132 -33
  29. package/dist/parsers/parseString.js.map +1 -1
  30. package/dist/types/index.d.ts +1 -1
  31. package/dist/utils/string.d.ts +2 -0
  32. package/dist/utils/string.js +12 -1
  33. package/dist/utils/string.js.map +1 -1
  34. package/examples/nested-output.js +1 -1
  35. package/examples/nested.yini +1 -1
  36. package/package.json +4 -3
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Development-only YINI samples.
3
+ *
4
+ * These inputs are used for quick manual testing during development
5
+ * via src/dev/quick-test-samples/main.ts. They are NOT part of the automated test suite.
6
+ *
7
+ * All real testing belongs in /tests/.
8
+ */
9
+ export declare const validConfigShort = "\n^ App\nname = 'Hello'\n";
10
+ export declare const validConfigBasic = "\n^ App\nname = \"My Application\"\nversion = 1.0\nfeatures = [\"search\", \"logging\"]\n\n // Nested sub-section under App\n ^^ Database\n host = \"localhost\"\n port = 5432\n";
11
+ export declare const validConfigWithObjects = "\n^ App\n name = \"Demo\"\n version = \"1.0.0\"\n features = [ \"search\", \"dark-mode\" ]\n\n^ Database\n host = \"localhost\"\n port = 5432\n auth = { user: \"admin\", pass: \"secret\" }\n";
12
+ /**
13
+ * Covers booleans, nulls, number formats, and deeper nesting
14
+ */
15
+ export declare const validConfigAdvanced = "\n^ Server\n enabled = ON\n timeout = 3.5\n retries = 5\n threshold = 1e-3\n fallback = null\n\n ^^ Logging\n level = \"info\"\n output = { file: \"app.log\", rotate: true }\n";
16
+ /**
17
+ * Covers arrays of objects and realistic structure
18
+ */
19
+ export declare const validConfigComplex = "\n^ App\n services = [\n { name: \"api\", port: 8080 },\n { name: \"web\", port: 3000 },\n { name: \"auth\", port: 9000 }\n ]\n\n^ Security\n roles = [\"admin\", \"user\", \"guest\"]\n enabled = true\n";
20
+ export declare const validConfigComplexBigA = "\n@YINI\n\n// Example A: Corporate SaaS Platform.\n\n^ App\nname = \"Acme Platform\" // Example Platform\ndescription = \"The word \u201CAcme\u201D has been used for over 100 years in technical and business examples.\"\nmeaning = \"It comes from Greek akm\u1E17 (\u1F00\u03BA\u03BC\u03AE), meaning \u201Cthe highest point\u201D or \u201Cbest\u201D.\"\nversion = \"2.3.1\"\ndebug = OFF\nenvironment = \"production\"\nmaintainers = [\"ops@acme.com\", \"dev@acme.com\"]\n\n ^^ Features\n enableSearch = true\n enablePayments = true\n enableAnalytics = false\n experimental = [\"new-ui\", \"streaming-api\"]\n\n ^^ Limits\n maxUsers = 50000\n requestTimeoutMs = 3500\n retryPolicy = { maxRetries: 5, backoff: \"exponential\" }\n\n ^^ Database\n engine = \"postgres\"\n host = \"db.internal.acme.com\"\n port = 5432\n ssl = true\n pool = { min: 5, max: 50 }\n\n ^^^ Credentials\n username = \"app_user\"\n password = \"****\"\n rotateEveryDays = 90\n\n ^^ API\n baseUrl = \"https://api.acme.com\"\n publicEndpoints = [\"/health\", \"/status\"]\n internalEndpoints = [\"/admin\", \"/metrics\"]\n\n ^^^ Auth\n provider = \"oauth2\"\n tokenTTLSeconds = 3600\n scopes = [\"read\", \"write\", \"admin\"]\n\n ^^^^ Clients\n web = { clientId: \"web-123\", redirectUri: \"https://acme.com/callback\" }\n mobile = { clientId: \"mob-456\", redirectUri: \"acme://auth\" }\n\n^ Logging\nlevel = \"info\"\nformat = \"json\"\noutputs = [\"stdout\", \"file\"]\n\n ^^ File\n path = \"/var/log/acme/app.log\"\n maxSizeMB = 100\n rotate = true\n keepFiles = 10\n\n ^^ Metrics\n enabled = true\n endpoint = \"/metrics\"\n sampleRate = 0.25\n\n^ Services\nenabled = true\n\n ^^ Email\n provider = \"smtp\"\n host = \"smtp.acme.com\"\n port = 587\n secure = false\n from = \"no-reply@acme.com\"\n\n ^^^ Credentials\n user = \"mailer\"\n pass = \"mailer-secret\"\n\n ^^ Cache\n type = \"redis\"\n host = \"cache.internal.acme.com\"\n port = 6379\n ttlSeconds = 600\n\n ^^^ Cluster\n nodes = [\n { host: \"cache-1.internal\", port: 6379 },\n { host: \"cache-2.internal\", port: 6379 },\n { host: \"cache-3.internal\", port: 6379 }\n ]\n\n^ Observability\ntracing = true\ntracingProvider = \"opentelemetry\"\ntraceSampleRate = 0.1\n\n ^^ Exporters\n jaeger = { enabled: true, endpoint: \"http://jaeger:14268/api/traces\" }\n prometheus = { enabled: true, endpoint: \"http://prom:9090\" }\n\n^ Security\nallowedIPs = [\"10.0.0.0/8\", \"192.168.0.0/16\"]\nblockedCountries = [\"KP\", \"SD\"]\n\n ^^ Policies\n passwordMinLength = 14\n require2FA = true\n sessionTTLMinutes = 120\n\n ^^^ Lockout\n maxAttempts = 5\n lockoutMinutes = 30\n";
21
+ export declare const validConfigComplexBigB = "\n@YINI\n\n// Example B: High-Security Distributed Control System.\n\n^ App\nname = 'Nebula Control Suite'\ndescription = 'A distributed operations platform for autonomous systems and edge analytics.'\nmeaning = 'Nebula comes from Latin \"nebula\" meaning mist or cloud.'\nversion = '5.0.0-rc.4'\ndebug = ON\nenvironment = 'staging'\nmaintainers = ['infra@nebula.io', 'platform@nebula.io', 'secops@nebula.io']\n\n ^^ Features\n enableSearch = false\n enablePayments = false\n enableAnalytics = true\n experimental = ['vector-engine', 'adaptive-ui', 'ai-routing']\n\n ^^ Limits\n maxUsers = 120000\n requestTimeoutMs = 7200\n retryPolicy = {\n maxRetries: 9,\n backoff: 'fibonacci',\n retryOn: ['timeout', '5xx', 'throttle'],\n schedule: [\n { attempt: 1, delayMs: 80 },\n { attempt: 2, delayMs: 160 },\n { attempt: 3, delayMs: 320 },\n { attempt: 4, delayMs: 640 },\n { attempt: 5, delayMs: 1280 }\n ]\n }\n\n ^^ Database\n engine = 'cockroachdb'\n host = 'cluster.db.nebula.io'\n port = 26257\n ssl = true\n pool = {\n min: 12,\n max: 120,\n warmup: {\n enabled: true,\n strategy: 'aggressive',\n steps: [10, 25, 50, 75, 100],\n healthChecks: [\n { name: 'connectivity', timeoutMs: 300 },\n { name: 'replication', maxLagMs: 200 },\n { name: 'quorum', minNodes: 3 }\n ]\n }\n }\n\n ^^^ Credentials\n username = 'nebula_app'\n password = '****'\n rotateEveryDays = 45\n history = [\n { rotatedAt: '2025-05-10', reason: 'scheduled' },\n { rotatedAt: '2025-03-02', reason: 'key-compromise' },\n { rotatedAt: '2024-12-15', reason: 'policy-change' }\n ]\n\n ^^ API\n baseUrl = 'https://api.nebula.io'\n publicEndpoints = ['/health', '/status', '/version']\n internalEndpoints = ['/admin', '/metrics', '/orchestrator', '/scheduler']\n\n ^^^ Auth\n provider = 'oidc'\n tokenTTLSeconds = 5400\n scopes = ['read', 'write', 'deploy', 'audit']\n\n ^^^^ Clients\n web = {\n clientId: 'nebula-web-prod',\n redirectUri: 'https://nebula.io/auth/callback',\n allowedOrigins: ['https://nebula.io', 'https://console.nebula.io'],\n secrets: [\n { id: 'alpha', value: 'QX7faP9', active: true },\n { id: 'beta', value: 'LM8KdW2', active: true },\n { id: 'legacy', value: 'OLD-KEY-DO-NOT-USE', active: false }\n ]\n }\n\n mobile = {\n clientId: 'nebula-mobile',\n redirectUri: 'nebula://auth',\n platforms: [\n { name: 'ios', minVersion: '15.2', enabled: true },\n { name: 'android', minVersion: '11', enabled: true },\n { name: 'harmonyos', minVersion: '4', enabled: false }\n ],\n refreshPolicy: {\n enabled: true,\n limits: { perHour: 60, perDay: 600 },\n audit: [\n { event: 'refresh', severity: 'info' },\n { event: 'suspicious-location', severity: 'warning' },\n { event: 'credential-stuffing', severity: 'critical' }\n ]\n }\n }\n\n^ Logging\nlevel = 'debug'\nformat = 'ndjson'\noutputs = ['stdout', 'file', 'syslog']\n\n ^^ File\n path = '/srv/log/nebula/nebula.log'\n maxSizeMB = 250\n rotate = true\n keepFiles = 30\n\n ^^ Metrics\n enabled = true\n endpoint = '/internal/metrics'\n sampleRate = 0.75\n\n^ Services\nenabled = true\n\n ^^ Email\n provider = 'ses'\n host = 'email.nebula.io'\n port = 465\n secure = true\n from = 'system@nebula.io'\n\n ^^^ Credentials\n user = 'mailer-nebula'\n pass = 'MAIL-SEC-9921'\n\n ^^ Cache\n type = 'keydb'\n host = 'cache.nebula.internal'\n port = 6380\n ttlSeconds = 1800\n\n ^^^ Cluster\n nodes = [\n { host: 'cache-a.nebula', port: 6380, role: 'primary', zones: ['eu-north-1a'] },\n { host: 'cache-b.nebula', port: 6380, role: 'replica', zones: ['eu-north-1b'] },\n { host: 'cache-c.nebula', port: 6380, role: 'replica', zones: ['eu-north-1c'] },\n { host: 'cache-d.nebula', port: 6380, role: 'observer', zones: ['eu-north-1a'] }\n ]\n\n ^^^ Failover\n strategy = {\n mode: 'predictive',\n thresholds: { errorRate: 0.08, latencyMs: 180 },\n actions: [\n { step: 'drain-traffic', timeoutMs: 1500 },\n { step: 'promote-replica', timeoutMs: 2000 },\n { step: 'resync', propagate: true },\n { step: 'notify', channels: ['pagerduty', 'slack', 'email'] }\n ]\n }\n\n^ Observability\ntracing = true\ntracingProvider = 'tempo'\ntraceSampleRate = 0.35\n\n ^^ Exporters\n jaeger = {\n enabled: false,\n endpoint: 'http://jaeger.internal/api/traces',\n tags: {\n region: 'eu-north',\n environment: 'staging',\n build: { version: '5.0.0-rc.4', commit: 'c8f91d2', dirty: true }\n }\n }\n\n prometheus = {\n enabled: true,\n endpoint: 'http://prometheus.nebula:9090',\n scrapeIntervals: [2, 5, 10, 30],\n retention: { days: 90, maxSeries: 3500000 }\n }\n\n^ Security\nallowedIPs = ['172.16.0.0/12', '100.64.0.0/10']\nblockedCountries = ['KP', 'NG', 'BY']\n\n ^^ Policies\n passwordMinLength = 18\n require2FA = true\n sessionTTLMinutes = 45\n\n ^^^ Lockout\n maxAttempts = 4\n lockoutMinutes = 60\n escalation = {\n enabled: true,\n notify: ['security@nebula.io', 'ciso@nebula.io'],\n rules: [\n { attempts: 3, action: 'captcha' },\n { attempts: 4, action: 'temporary-block', minutes: 120 },\n { attempts: 6, action: 'account-freeze' },\n { attempts: 9, action: 'permanent-block' }\n ]\n }\n";
@@ -0,0 +1,422 @@
1
+ "use strict";
2
+ // src/dev/quick-test-samples/valid-inputs.ts
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.validConfigComplexBigB = exports.validConfigComplexBigA = exports.validConfigComplex = exports.validConfigAdvanced = exports.validConfigWithObjects = exports.validConfigBasic = exports.validConfigShort = void 0;
5
+ /**
6
+ * Development-only YINI samples.
7
+ *
8
+ * These inputs are used for quick manual testing during development
9
+ * via src/dev/quick-test-samples/main.ts. They are NOT part of the automated test suite.
10
+ *
11
+ * All real testing belongs in /tests/.
12
+ */
13
+ exports.validConfigShort = `
14
+ ^ App
15
+ name = 'Hello'
16
+ `;
17
+ exports.validConfigBasic = `
18
+ ^ App
19
+ name = "My Application"
20
+ version = 1.0
21
+ features = ["search", "logging"]
22
+
23
+ // Nested sub-section under App
24
+ ^^ Database
25
+ host = "localhost"
26
+ port = 5432
27
+ `;
28
+ exports.validConfigWithObjects = `
29
+ ^ App
30
+ name = "Demo"
31
+ version = "1.0.0"
32
+ features = [ "search", "dark-mode" ]
33
+
34
+ ^ Database
35
+ host = "localhost"
36
+ port = 5432
37
+ auth = { user: "admin", pass: "secret" }
38
+ `;
39
+ /**
40
+ * Covers booleans, nulls, number formats, and deeper nesting
41
+ */
42
+ exports.validConfigAdvanced = `
43
+ ^ Server
44
+ enabled = ON
45
+ timeout = 3.5
46
+ retries = 5
47
+ threshold = 1e-3
48
+ fallback = null
49
+
50
+ ^^ Logging
51
+ level = "info"
52
+ output = { file: "app.log", rotate: true }
53
+ `;
54
+ /**
55
+ * Covers arrays of objects and realistic structure
56
+ */
57
+ exports.validConfigComplex = `
58
+ ^ App
59
+ services = [
60
+ { name: "api", port: 8080 },
61
+ { name: "web", port: 3000 },
62
+ { name: "auth", port: 9000 }
63
+ ]
64
+
65
+ ^ Security
66
+ roles = ["admin", "user", "guest"]
67
+ enabled = true
68
+ `;
69
+ /*
70
+ Covers:
71
+ - Sections & deep nesting
72
+ - Real-world domain structure
73
+ - Objects in arrays
74
+ - Arrays of objects
75
+ - Scalars of every type
76
+ - Complex policy logic
77
+ - Auth & security modeling
78
+ - Unicode in strings.
79
+ - Strings in double quotes.
80
+ - Large but readable
81
+ */
82
+ exports.validConfigComplexBigA = `
83
+ @YINI
84
+
85
+ // Example A: Corporate SaaS Platform.
86
+
87
+ ^ App
88
+ name = "Acme Platform" // Example Platform
89
+ description = "The word “Acme” has been used for over 100 years in technical and business examples."
90
+ meaning = "It comes from Greek akmḗ (ἀκμή), meaning “the highest point” or “best”."
91
+ version = "2.3.1"
92
+ debug = OFF
93
+ environment = "production"
94
+ maintainers = ["ops@acme.com", "dev@acme.com"]
95
+
96
+ ^^ Features
97
+ enableSearch = true
98
+ enablePayments = true
99
+ enableAnalytics = false
100
+ experimental = ["new-ui", "streaming-api"]
101
+
102
+ ^^ Limits
103
+ maxUsers = 50000
104
+ requestTimeoutMs = 3500
105
+ retryPolicy = { maxRetries: 5, backoff: "exponential" }
106
+
107
+ ^^ Database
108
+ engine = "postgres"
109
+ host = "db.internal.acme.com"
110
+ port = 5432
111
+ ssl = true
112
+ pool = { min: 5, max: 50 }
113
+
114
+ ^^^ Credentials
115
+ username = "app_user"
116
+ password = "****"
117
+ rotateEveryDays = 90
118
+
119
+ ^^ API
120
+ baseUrl = "https://api.acme.com"
121
+ publicEndpoints = ["/health", "/status"]
122
+ internalEndpoints = ["/admin", "/metrics"]
123
+
124
+ ^^^ Auth
125
+ provider = "oauth2"
126
+ tokenTTLSeconds = 3600
127
+ scopes = ["read", "write", "admin"]
128
+
129
+ ^^^^ Clients
130
+ web = { clientId: "web-123", redirectUri: "https://acme.com/callback" }
131
+ mobile = { clientId: "mob-456", redirectUri: "acme://auth" }
132
+
133
+ ^ Logging
134
+ level = "info"
135
+ format = "json"
136
+ outputs = ["stdout", "file"]
137
+
138
+ ^^ File
139
+ path = "/var/log/acme/app.log"
140
+ maxSizeMB = 100
141
+ rotate = true
142
+ keepFiles = 10
143
+
144
+ ^^ Metrics
145
+ enabled = true
146
+ endpoint = "/metrics"
147
+ sampleRate = 0.25
148
+
149
+ ^ Services
150
+ enabled = true
151
+
152
+ ^^ Email
153
+ provider = "smtp"
154
+ host = "smtp.acme.com"
155
+ port = 587
156
+ secure = false
157
+ from = "no-reply@acme.com"
158
+
159
+ ^^^ Credentials
160
+ user = "mailer"
161
+ pass = "mailer-secret"
162
+
163
+ ^^ Cache
164
+ type = "redis"
165
+ host = "cache.internal.acme.com"
166
+ port = 6379
167
+ ttlSeconds = 600
168
+
169
+ ^^^ Cluster
170
+ nodes = [
171
+ { host: "cache-1.internal", port: 6379 },
172
+ { host: "cache-2.internal", port: 6379 },
173
+ { host: "cache-3.internal", port: 6379 }
174
+ ]
175
+
176
+ ^ Observability
177
+ tracing = true
178
+ tracingProvider = "opentelemetry"
179
+ traceSampleRate = 0.1
180
+
181
+ ^^ Exporters
182
+ jaeger = { enabled: true, endpoint: "http://jaeger:14268/api/traces" }
183
+ prometheus = { enabled: true, endpoint: "http://prom:9090" }
184
+
185
+ ^ Security
186
+ allowedIPs = ["10.0.0.0/8", "192.168.0.0/16"]
187
+ blockedCountries = ["KP", "SD"]
188
+
189
+ ^^ Policies
190
+ passwordMinLength = 14
191
+ require2FA = true
192
+ sessionTTLMinutes = 120
193
+
194
+ ^^^ Lockout
195
+ maxAttempts = 5
196
+ lockoutMinutes = 30
197
+ `;
198
+ /*
199
+ Example B covers:
200
+ Covers:
201
+ - Nested arrays inside inline objects.
202
+ - Sections & deep nesting.
203
+ - Real-world domain structure.
204
+ - Objects in arrays.
205
+ - Arrays of objects.
206
+ - Scalars of every type.
207
+ - Complex policy logic.
208
+ - Auth & security modeling.
209
+ - Unicode in strings.
210
+ - Strings in single quotes.
211
+ - Large but readable.
212
+ */
213
+ exports.validConfigComplexBigB = `
214
+ @YINI
215
+
216
+ // Example B: High-Security Distributed Control System.
217
+
218
+ ^ App
219
+ name = 'Nebula Control Suite'
220
+ description = 'A distributed operations platform for autonomous systems and edge analytics.'
221
+ meaning = 'Nebula comes from Latin "nebula" meaning mist or cloud.'
222
+ version = '5.0.0-rc.4'
223
+ debug = ON
224
+ environment = 'staging'
225
+ maintainers = ['infra@nebula.io', 'platform@nebula.io', 'secops@nebula.io']
226
+
227
+ ^^ Features
228
+ enableSearch = false
229
+ enablePayments = false
230
+ enableAnalytics = true
231
+ experimental = ['vector-engine', 'adaptive-ui', 'ai-routing']
232
+
233
+ ^^ Limits
234
+ maxUsers = 120000
235
+ requestTimeoutMs = 7200
236
+ retryPolicy = {
237
+ maxRetries: 9,
238
+ backoff: 'fibonacci',
239
+ retryOn: ['timeout', '5xx', 'throttle'],
240
+ schedule: [
241
+ { attempt: 1, delayMs: 80 },
242
+ { attempt: 2, delayMs: 160 },
243
+ { attempt: 3, delayMs: 320 },
244
+ { attempt: 4, delayMs: 640 },
245
+ { attempt: 5, delayMs: 1280 }
246
+ ]
247
+ }
248
+
249
+ ^^ Database
250
+ engine = 'cockroachdb'
251
+ host = 'cluster.db.nebula.io'
252
+ port = 26257
253
+ ssl = true
254
+ pool = {
255
+ min: 12,
256
+ max: 120,
257
+ warmup: {
258
+ enabled: true,
259
+ strategy: 'aggressive',
260
+ steps: [10, 25, 50, 75, 100],
261
+ healthChecks: [
262
+ { name: 'connectivity', timeoutMs: 300 },
263
+ { name: 'replication', maxLagMs: 200 },
264
+ { name: 'quorum', minNodes: 3 }
265
+ ]
266
+ }
267
+ }
268
+
269
+ ^^^ Credentials
270
+ username = 'nebula_app'
271
+ password = '****'
272
+ rotateEveryDays = 45
273
+ history = [
274
+ { rotatedAt: '2025-05-10', reason: 'scheduled' },
275
+ { rotatedAt: '2025-03-02', reason: 'key-compromise' },
276
+ { rotatedAt: '2024-12-15', reason: 'policy-change' }
277
+ ]
278
+
279
+ ^^ API
280
+ baseUrl = 'https://api.nebula.io'
281
+ publicEndpoints = ['/health', '/status', '/version']
282
+ internalEndpoints = ['/admin', '/metrics', '/orchestrator', '/scheduler']
283
+
284
+ ^^^ Auth
285
+ provider = 'oidc'
286
+ tokenTTLSeconds = 5400
287
+ scopes = ['read', 'write', 'deploy', 'audit']
288
+
289
+ ^^^^ Clients
290
+ web = {
291
+ clientId: 'nebula-web-prod',
292
+ redirectUri: 'https://nebula.io/auth/callback',
293
+ allowedOrigins: ['https://nebula.io', 'https://console.nebula.io'],
294
+ secrets: [
295
+ { id: 'alpha', value: 'QX7faP9', active: true },
296
+ { id: 'beta', value: 'LM8KdW2', active: true },
297
+ { id: 'legacy', value: 'OLD-KEY-DO-NOT-USE', active: false }
298
+ ]
299
+ }
300
+
301
+ mobile = {
302
+ clientId: 'nebula-mobile',
303
+ redirectUri: 'nebula://auth',
304
+ platforms: [
305
+ { name: 'ios', minVersion: '15.2', enabled: true },
306
+ { name: 'android', minVersion: '11', enabled: true },
307
+ { name: 'harmonyos', minVersion: '4', enabled: false }
308
+ ],
309
+ refreshPolicy: {
310
+ enabled: true,
311
+ limits: { perHour: 60, perDay: 600 },
312
+ audit: [
313
+ { event: 'refresh', severity: 'info' },
314
+ { event: 'suspicious-location', severity: 'warning' },
315
+ { event: 'credential-stuffing', severity: 'critical' }
316
+ ]
317
+ }
318
+ }
319
+
320
+ ^ Logging
321
+ level = 'debug'
322
+ format = 'ndjson'
323
+ outputs = ['stdout', 'file', 'syslog']
324
+
325
+ ^^ File
326
+ path = '/srv/log/nebula/nebula.log'
327
+ maxSizeMB = 250
328
+ rotate = true
329
+ keepFiles = 30
330
+
331
+ ^^ Metrics
332
+ enabled = true
333
+ endpoint = '/internal/metrics'
334
+ sampleRate = 0.75
335
+
336
+ ^ Services
337
+ enabled = true
338
+
339
+ ^^ Email
340
+ provider = 'ses'
341
+ host = 'email.nebula.io'
342
+ port = 465
343
+ secure = true
344
+ from = 'system@nebula.io'
345
+
346
+ ^^^ Credentials
347
+ user = 'mailer-nebula'
348
+ pass = 'MAIL-SEC-9921'
349
+
350
+ ^^ Cache
351
+ type = 'keydb'
352
+ host = 'cache.nebula.internal'
353
+ port = 6380
354
+ ttlSeconds = 1800
355
+
356
+ ^^^ Cluster
357
+ nodes = [
358
+ { host: 'cache-a.nebula', port: 6380, role: 'primary', zones: ['eu-north-1a'] },
359
+ { host: 'cache-b.nebula', port: 6380, role: 'replica', zones: ['eu-north-1b'] },
360
+ { host: 'cache-c.nebula', port: 6380, role: 'replica', zones: ['eu-north-1c'] },
361
+ { host: 'cache-d.nebula', port: 6380, role: 'observer', zones: ['eu-north-1a'] }
362
+ ]
363
+
364
+ ^^^ Failover
365
+ strategy = {
366
+ mode: 'predictive',
367
+ thresholds: { errorRate: 0.08, latencyMs: 180 },
368
+ actions: [
369
+ { step: 'drain-traffic', timeoutMs: 1500 },
370
+ { step: 'promote-replica', timeoutMs: 2000 },
371
+ { step: 'resync', propagate: true },
372
+ { step: 'notify', channels: ['pagerduty', 'slack', 'email'] }
373
+ ]
374
+ }
375
+
376
+ ^ Observability
377
+ tracing = true
378
+ tracingProvider = 'tempo'
379
+ traceSampleRate = 0.35
380
+
381
+ ^^ Exporters
382
+ jaeger = {
383
+ enabled: false,
384
+ endpoint: 'http://jaeger.internal/api/traces',
385
+ tags: {
386
+ region: 'eu-north',
387
+ environment: 'staging',
388
+ build: { version: '5.0.0-rc.4', commit: 'c8f91d2', dirty: true }
389
+ }
390
+ }
391
+
392
+ prometheus = {
393
+ enabled: true,
394
+ endpoint: 'http://prometheus.nebula:9090',
395
+ scrapeIntervals: [2, 5, 10, 30],
396
+ retention: { days: 90, maxSeries: 3500000 }
397
+ }
398
+
399
+ ^ Security
400
+ allowedIPs = ['172.16.0.0/12', '100.64.0.0/10']
401
+ blockedCountries = ['KP', 'NG', 'BY']
402
+
403
+ ^^ Policies
404
+ passwordMinLength = 18
405
+ require2FA = true
406
+ sessionTTLMinutes = 45
407
+
408
+ ^^^ Lockout
409
+ maxAttempts = 4
410
+ lockoutMinutes = 60
411
+ escalation = {
412
+ enabled: true,
413
+ notify: ['security@nebula.io', 'ciso@nebula.io'],
414
+ rules: [
415
+ { attempts: 3, action: 'captcha' },
416
+ { attempts: 4, action: 'temporary-block', minutes: 120 },
417
+ { attempts: 6, action: 'account-freeze' },
418
+ { attempts: 9, action: 'permanent-block' }
419
+ ]
420
+ }
421
+ `;
422
+ //# sourceMappingURL=valid-inputs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"valid-inputs.js","sourceRoot":"","sources":["../../../src/dev/quick-test-samples/valid-inputs.ts"],"names":[],"mappings":";AAAA,6CAA6C;;;AAE7C;;;;;;;GAOG;AAEU,QAAA,gBAAgB,GAAG;;;CAG/B,CAAA;AAEY,QAAA,gBAAgB,GAAG;;;;;;;;;;CAU/B,CAAA;AAEY,QAAA,sBAAsB,GAAG;;;;;;;;;;CAUrC,CAAA;AAED;;GAEG;AACU,QAAA,mBAAmB,GAAG;;;;;;;;;;;CAWlC,CAAA;AAED;;GAEG;AACU,QAAA,kBAAkB,GAAG;;;;;;;;;;;CAWjC,CAAA;AAED;;;;;;;;;;;;GAYG;AACU,QAAA,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAmHrC,CAAA;AAED;;;;;;;;;;;;;;GAcG;AACU,QAAA,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgNrC,CAAA"}
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  "use strict";
2
- // File: src/index.ts
2
+ // src/index.ts
3
3
  var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
5
5
  };
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,qBAAqB;;;;;;AAErB;;;;;;;;;;;;;EAaE;AAEF,kDAAyB;AAEzB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wFAAwF;AACxF,kBAAe,cAAI,CAAA,CAAC,sBAAsB;AAE7B,QAAA,KAAK,GAAG,cAAI,CAAC,KAAK,CAAA;AAClB,QAAA,SAAS,GAAG,cAAI,CAAC,SAAS,CAAA;AAC1B,QAAA,UAAU,GAAG,cAAI,CAAC,UAAU,CAAA;AAC5B,QAAA,UAAU,GAAG,cAAI,CAAC,UAAU,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,eAAe;;;;;;AAEf;;;;;;;;;;;;;EAaE;AAEF,kDAAyB;AAEzB;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wFAAwF;AACxF,kBAAe,cAAI,CAAA,CAAC,sBAAsB;AAE7B,QAAA,KAAK,GAAG,cAAI,CAAC,KAAK,CAAA;AAClB,QAAA,SAAS,GAAG,cAAI,CAAC,SAAS,CAAA;AAC1B,QAAA,UAAU,GAAG,cAAI,CAAC,UAAU,CAAA;AAC5B,QAAA,UAAU,GAAG,cAAI,CAAC,UAAU,CAAA"}
@@ -1,2 +1,3 @@
1
- declare const parseStringLiteral: (raw: string) => string;
1
+ import { IParsedStringInput } from '../core/internalTypes';
2
+ declare const parseStringLiteral: ({ strKind, value }: IParsedStringInput) => string;
2
3
  export default parseStringLiteral;
@@ -1,40 +1,139 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const env_1 = require("../config/env");
4
- const print_1 = require("../utils/print");
5
- const parseStringLiteral = (raw) => {
6
- (0, print_1.debugPrint)('-> Entered parseStringLiteral(..)');
7
- (0, print_1.debugPrint)('raw = >>>' + raw + '<<<');
8
- /*
9
- Extracts an optional prefix (C, c, H, or h) and identifies whether
10
- the string is triple-quoted, double-quoted, or single-quoted.
11
- */
12
- const prefixMatch = raw.match(/^(C|c|H|h|R|r)?("""|"|')/);
13
- (0, print_1.debugPrint)('prefixMatch:');
14
- if ((0, env_1.isDebug)()) {
15
- console.debug(prefixMatch);
3
+ const isHex = (c) => /^[0-9a-fA-F]$/.test(c);
4
+ const isOctal = (c) => /^[0-7]$/.test(c);
5
+ function normalizeHyperWhitespace(input) {
6
+ return input.replace(/[\s\r\n]+/g, ' ').trim();
7
+ }
8
+ const parseClassicEscapes = (input) => {
9
+ let result = '';
10
+ for (let i = 0; i < input.length; i++) {
11
+ const ch = input[i];
12
+ if (ch !== '\\') {
13
+ // Control character enforcement (U+0000–U+001F except tab).
14
+ const code = ch.charCodeAt(0);
15
+ if (code < 0x20 && ch !== '\t') {
16
+ throw new Error(`Unescaped control character U+${code
17
+ .toString(16)
18
+ .padStart(4, '0')}`);
19
+ }
20
+ result += ch;
21
+ continue;
22
+ }
23
+ // Escape sequence.
24
+ const next = input[++i];
25
+ if (!next)
26
+ throw new Error('Invalid escape sequence at end of string');
27
+ switch (next) {
28
+ case '\\':
29
+ result += '\\';
30
+ break;
31
+ case '"':
32
+ result += '"';
33
+ break;
34
+ case "'":
35
+ result += "'";
36
+ break;
37
+ case '/':
38
+ result += '/';
39
+ break;
40
+ case '0':
41
+ result += '\0';
42
+ break;
43
+ case '?':
44
+ result += '?';
45
+ break;
46
+ case 'a':
47
+ result += '\x07';
48
+ break;
49
+ case 'b':
50
+ result += '\b';
51
+ break;
52
+ case 'f':
53
+ result += '\f';
54
+ break;
55
+ case 'n':
56
+ result += '\n';
57
+ break;
58
+ case 'r':
59
+ result += '\r';
60
+ break;
61
+ case 't':
62
+ result += '\t';
63
+ break;
64
+ case 'v':
65
+ result += '\v';
66
+ break;
67
+ case 'x': {
68
+ const hex = input.slice(i + 1, i + 3);
69
+ if (hex.length !== 2 || ![...hex].every(isHex)) {
70
+ throw new Error(`Invalid \\x escape`);
71
+ }
72
+ result += String.fromCharCode(parseInt(hex, 16));
73
+ i += 2;
74
+ break;
75
+ }
76
+ case 'u': {
77
+ const hex = input.slice(i + 1, i + 5);
78
+ if (hex.length !== 4 || ![...hex].every(isHex)) {
79
+ throw new Error(`Invalid \\u escape`);
80
+ }
81
+ result += String.fromCharCode(parseInt(hex, 16));
82
+ i += 4;
83
+ break;
84
+ }
85
+ case 'U': {
86
+ const hex = input.slice(i + 1, i + 9);
87
+ if (hex.length !== 8 || ![...hex].every(isHex)) {
88
+ throw new Error(`Invalid \\U escape`);
89
+ }
90
+ const codePoint = parseInt(hex, 16);
91
+ result += String.fromCodePoint(codePoint);
92
+ i += 8;
93
+ break;
94
+ }
95
+ case 'o': {
96
+ let oct = '';
97
+ let j = i + 1;
98
+ while (j < input.length &&
99
+ oct.length < 3 &&
100
+ isOctal(input[j])) {
101
+ oct += input[j];
102
+ j++;
103
+ }
104
+ if (oct.length === 0) {
105
+ throw new Error(`Invalid \\o escape`);
106
+ }
107
+ const value = parseInt(oct, 8);
108
+ if (value > 0o377) {
109
+ throw new Error(`Octal escape out of range`);
110
+ }
111
+ result += String.fromCharCode(value);
112
+ i += oct.length;
113
+ if (j < input.length && /[0-9]/.test(input[j])) {
114
+ throw new Error(`Invalid \\o escape`);
115
+ }
116
+ break;
117
+ }
118
+ default:
119
+ throw new Error(`Invalid escape sequence \\${next}`);
120
+ }
16
121
  }
17
- let prefix = prefixMatch ? prefixMatch[1]?.toUpperCase() : '';
18
- (0, print_1.debugPrint)(' prefix = ' + prefix);
19
- let quoteType = prefixMatch ? prefixMatch[2] : '';
20
- (0, print_1.debugPrint)(' quoteType = ' + quoteType);
21
- (0, print_1.debugPrint)('quoteType.length = ' + quoteType.length);
22
- // Extracts the substring after removing the initial prefix (if any)
23
- // and quotes at the start (prefix.length + quoteType.length) and the
24
- // quotes at the end (-quoteType.length).
25
- let inner = raw.slice((prefix?.length || 0) + quoteType.length, -quoteType.length);
26
- (0, print_1.debugPrint)('inner (raw) = ' + inner);
27
- if (prefix === 'C') {
28
- inner = inner
29
- .replace(/\\n/g, '\n')
30
- .replace(/\\t/g, '\t')
31
- .replace(/\\r/g, '\r');
32
- }
33
- else if (prefix === 'H') {
34
- inner = inner.replace(/[\s\n\r]+/g, ' ').trim();
122
+ return result;
123
+ };
124
+ const parseStringLiteral = ({ strKind, value }) => {
125
+ switch (strKind) {
126
+ case 'raw':
127
+ case 'triple-raw':
128
+ return value;
129
+ case 'classic':
130
+ case 'triple-classic':
131
+ return parseClassicEscapes(value);
132
+ case 'hyper':
133
+ return normalizeHyperWhitespace(value);
134
+ default:
135
+ throw new Error(`Unknown string kind: ${strKind}`);
35
136
  }
36
- (0, print_1.debugPrint)('inner (reformat) = ' + inner);
37
- return inner;
38
137
  };
39
138
  exports.default = parseStringLiteral;
40
139
  //# sourceMappingURL=parseString.js.map