+8
-1
src/Console/GenerateCommand.php
+8
-1
src/Console/GenerateCommand.php
···
38
38
39
39
try {
40
40
$sources = config('schema.sources', []);
41
-
$loader = new SchemaLoader($sources);
41
+
$loader = new SchemaLoader(
42
+
sources: $sources,
43
+
useCache: config('schema.cache.enabled', true),
44
+
cacheTtl: config('schema.cache.schema_ttl', 3600),
45
+
cachePrefix: config('schema.cache.prefix', 'schema'),
46
+
dnsResolutionEnabled: config('schema.dns_resolution.enabled', true),
47
+
httpTimeout: config('schema.http.timeout', 10)
48
+
);
42
49
43
50
$generator = new DTOGenerator(
44
51
schemaLoader: $loader,
+2
-2
src/Generator/DTOGenerator.php
+2
-2
src/Generator/DTOGenerator.php
···
61
61
$this->outputDirectory = rtrim($outputDirectory, '/');
62
62
$this->typeParser = $typeParser ?? new TypeParser(schemaLoader: $schemaLoader);
63
63
$this->namespaceResolver = $namespaceResolver ?? new NamespaceResolver($baseNamespace);
64
-
$this->templateRenderer = $templateRenderer ?? new TemplateRenderer;
65
-
$this->fileWriter = $fileWriter ?? new FileWriter;
64
+
$this->templateRenderer = $templateRenderer ?? new TemplateRenderer();
65
+
$this->fileWriter = $fileWriter ?? new FileWriter();
66
66
}
67
67
68
68
/**
+76
-5
src/Parser/SchemaLoader.php
+76
-5
src/Parser/SchemaLoader.php
···
3
3
namespace SocialDept\Schema\Parser;
4
4
5
5
use Illuminate\Support\Facades\Cache;
6
+
use Illuminate\Support\Facades\Http;
6
7
use SocialDept\Schema\Exceptions\SchemaNotFoundException;
7
8
use SocialDept\Schema\Exceptions\SchemaParseException;
8
9
···
36
37
* Cache key prefix.
37
38
*/
38
39
protected string $cachePrefix;
40
+
41
+
/**
42
+
* Whether DNS resolution is enabled.
43
+
*/
44
+
protected bool $dnsResolutionEnabled;
45
+
46
+
/**
47
+
* HTTP timeout for schema fetching.
48
+
*/
49
+
protected int $httpTimeout;
39
50
40
51
/**
41
52
* Create a new SchemaLoader instance.
···
46
57
array $sources,
47
58
bool $useCache = true,
48
59
int $cacheTtl = 3600,
49
-
string $cachePrefix = 'schema'
60
+
string $cachePrefix = 'schema',
61
+
bool $dnsResolutionEnabled = true,
62
+
int $httpTimeout = 10
50
63
) {
51
64
$this->sources = $sources;
52
65
$this->useCache = $useCache;
53
66
$this->cacheTtl = $cacheTtl;
54
67
$this->cachePrefix = $cachePrefix;
68
+
$this->dnsResolutionEnabled = $dnsResolutionEnabled;
69
+
$this->httpTimeout = $httpTimeout;
55
70
}
56
71
57
72
/**
···
117
132
}
118
133
}
119
134
135
+
// Try DNS resolution as fallback if enabled
136
+
if ($this->dnsResolutionEnabled) {
137
+
$schema = $this->loadViaDns($nsid);
138
+
139
+
if ($schema !== null) {
140
+
return $schema;
141
+
}
142
+
}
143
+
120
144
throw SchemaNotFoundException::forNsid($nsid);
121
145
}
122
146
···
127
151
{
128
152
// Try NSID-based path (app.bsky.feed.post -> app/bsky/feed/post.json)
129
153
$nsidPath = $this->nsidToPath($nsid);
130
-
$jsonPath = $source . '/' . $nsidPath . '.json';
154
+
$jsonPath = $source.'/'.$nsidPath.'.json';
131
155
132
156
if (file_exists($jsonPath)) {
133
157
return $this->loadJsonFile($jsonPath, $nsid);
134
158
}
135
159
136
160
// Try PHP file
137
-
$phpPath = $source . '/' . $nsidPath . '.php';
161
+
$phpPath = $source.'/'.$nsidPath.'.php';
138
162
139
163
if (file_exists($phpPath)) {
140
164
return $this->loadPhpFile($phpPath, $nsid);
141
165
}
142
166
143
167
// Try flat structure (app.bsky.feed.post.json)
144
-
$flatJsonPath = $source . '/' . $nsid . '.json';
168
+
$flatJsonPath = $source.'/'.$nsid.'.json';
145
169
146
170
if (file_exists($flatJsonPath)) {
147
171
return $this->loadJsonFile($flatJsonPath, $nsid);
148
172
}
149
173
150
-
$flatPhpPath = $source . '/' . $nsid . '.php';
174
+
$flatPhpPath = $source.'/'.$nsid.'.php';
151
175
152
176
if (file_exists($flatPhpPath)) {
153
177
return $this->loadPhpFile($flatPhpPath, $nsid);
···
241
265
public function getCachedNsids(): array
242
266
{
243
267
return array_keys($this->memoryCache);
268
+
}
269
+
270
+
/**
271
+
* Load schema via DNS resolution from official sources.
272
+
*/
273
+
protected function loadViaDns(string $nsid): ?array
274
+
{
275
+
// Try to fetch from official AT Protocol GitHub repository
276
+
$urls = $this->getOfficialSchemaUrls($nsid);
277
+
278
+
foreach ($urls as $url) {
279
+
try {
280
+
$response = Http::timeout($this->httpTimeout)
281
+
->get($url);
282
+
283
+
if ($response->successful()) {
284
+
$data = $response->json();
285
+
286
+
if (is_array($data) && isset($data['lexicon'])) {
287
+
return $data;
288
+
}
289
+
}
290
+
} catch (\Exception $e) {
291
+
// Continue to next URL
292
+
continue;
293
+
}
294
+
}
295
+
296
+
return null;
297
+
}
298
+
299
+
/**
300
+
* Get official schema URLs for an NSID.
301
+
*
302
+
* @return array<string>
303
+
*/
304
+
protected function getOfficialSchemaUrls(string $nsid): array
305
+
{
306
+
$path = str_replace('.', '/', $nsid);
307
+
308
+
return [
309
+
// Official AT Protocol lexicons repository (main branch)
310
+
"https://raw.githubusercontent.com/bluesky-social/atproto/main/lexicons/{$path}.json",
311
+
312
+
// Fallback to legacy location
313
+
"https://raw.githubusercontent.com/bluesky-social/atproto/main/lexicons/{$nsid}.json",
314
+
];
244
315
}
245
316
}
+11
-9
src/SchemaServiceProvider.php
+11
-9
src/SchemaServiceProvider.php
···
18
18
return new Parser\SchemaLoader(
19
19
sources: config('schema.sources', []),
20
20
useCache: config('schema.cache.enabled', true),
21
-
cacheTtl: config('schema.cache.ttl', 3600),
22
-
cachePrefix: config('schema.cache.prefix', 'schema')
21
+
cacheTtl: config('schema.cache.schema_ttl', 3600),
22
+
cachePrefix: config('schema.cache.prefix', 'schema'),
23
+
dnsResolutionEnabled: config('schema.dns_resolution.enabled', true),
24
+
httpTimeout: config('schema.http.timeout', 10)
23
25
);
24
26
});
25
27
···
86
88
87
89
// Register AT Protocol validation rules
88
90
$validator->extend('nsid', function ($attribute, $value) {
89
-
$rule = new Validation\Rules\Nsid;
91
+
$rule = new Validation\Rules\Nsid();
90
92
$failed = false;
91
93
$rule->validate($attribute, $value, function () use (&$failed) {
92
94
$failed = true;
···
96
98
}, 'The :attribute is not a valid NSID.');
97
99
98
100
$validator->extend('did', function ($attribute, $value) {
99
-
$rule = new Validation\Rules\Did;
101
+
$rule = new Validation\Rules\Did();
100
102
$failed = false;
101
103
$rule->validate($attribute, $value, function () use (&$failed) {
102
104
$failed = true;
···
106
108
}, 'The :attribute is not a valid DID.');
107
109
108
110
$validator->extend('handle', function ($attribute, $value) {
109
-
$rule = new Validation\Rules\Handle;
111
+
$rule = new Validation\Rules\Handle();
110
112
$failed = false;
111
113
$rule->validate($attribute, $value, function () use (&$failed) {
112
114
$failed = true;
···
116
118
}, 'The :attribute is not a valid handle.');
117
119
118
120
$validator->extend('at_uri', function ($attribute, $value) {
119
-
$rule = new Validation\Rules\AtUri;
121
+
$rule = new Validation\Rules\AtUri();
120
122
$failed = false;
121
123
$rule->validate($attribute, $value, function () use (&$failed) {
122
124
$failed = true;
···
126
128
}, 'The :attribute is not a valid AT URI.');
127
129
128
130
$validator->extend('at_datetime', function ($attribute, $value) {
129
-
$rule = new Validation\Rules\AtDatetime;
131
+
$rule = new Validation\Rules\AtDatetime();
130
132
$failed = false;
131
133
$rule->validate($attribute, $value, function () use (&$failed) {
132
134
$failed = true;
···
136
138
}, 'The :attribute is not a valid AT Protocol datetime.');
137
139
138
140
$validator->extend('cid', function ($attribute, $value) {
139
-
$rule = new Validation\Rules\Cid;
141
+
$rule = new Validation\Rules\Cid();
140
142
$failed = false;
141
143
$rule->validate($attribute, $value, function () use (&$failed) {
142
144
$failed = true;
···
172
174
}, 'The :attribute must be at least :min_graphemes graphemes.');
173
175
174
176
$validator->extend('language', function ($attribute, $value) {
175
-
$rule = new Validation\Rules\Language;
177
+
$rule = new Validation\Rules\Language();
176
178
$failed = false;
177
179
$rule->validate($attribute, $value, function () use (&$failed) {
178
180
$failed = true;