adonia 0.1.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/README.md +64 -0
- package/build/adonis_peers.d.ts +14 -0
- package/build/adonis_peers.js +56 -0
- package/build/cli.d.ts +2 -0
- package/build/cli.js +111 -0
- package/build/commands/add.d.ts +18 -0
- package/build/commands/add.js +246 -0
- package/build/commands/check.d.ts +4 -0
- package/build/commands/check.js +159 -0
- package/build/commands/diff.d.ts +2 -0
- package/build/commands/diff.js +74 -0
- package/build/commands/init.d.ts +6 -0
- package/build/commands/init.js +124 -0
- package/build/commands/list.d.ts +2 -0
- package/build/commands/list.js +34 -0
- package/build/config_merge.d.ts +10 -0
- package/build/config_merge.js +71 -0
- package/build/constants_merge.d.ts +4 -0
- package/build/constants_merge.js +85 -0
- package/build/copy.d.ts +8 -0
- package/build/copy.js +33 -0
- package/build/host.d.ts +15 -0
- package/build/host.js +85 -0
- package/build/registry/index.d.ts +26 -0
- package/build/registry/index.js +116 -0
- package/build/registry/plan.d.ts +53 -0
- package/build/registry/plan.js +159 -0
- package/build/schema.d.ts +136 -0
- package/build/schema.js +58 -0
- package/build/types.d.ts +21 -0
- package/build/types.js +1 -0
- package/build/wire.d.ts +16 -0
- package/build/wire.js +162 -0
- package/package.json +51 -0
- package/registry/README.md +86 -0
- package/registry/account/contracts/avatar_storage.ts +7 -0
- package/registry/account/contracts/index.ts +7 -0
- package/registry/account/contracts/types.ts +23 -0
- package/registry/account/events.ts +14 -0
- package/registry/account/module.json +31 -0
- package/registry/account/options.ts +11 -0
- package/registry/account/service.ts +155 -0
- package/registry/account/stubs/adapters/account.ts +21 -0
- package/registry/account/stubs/adapters/drive_avatar_storage.ts +15 -0
- package/registry/account/stubs/controllers/account_controller.ts +190 -0
- package/registry/account/stubs/routes/account.ts +53 -0
- package/registry/account/stubs/validators/account.ts +37 -0
- package/registry/account/tests/account_service.spec.ts +132 -0
- package/registry/account/tests/fakes/memory_avatar_storage.ts +12 -0
- package/registry/api/envelope.ts +66 -0
- package/registry/api/exception.ts +38 -0
- package/registry/api/exception_handler.ts +54 -0
- package/registry/api/module.json +27 -0
- package/registry/api/provider.ts +129 -0
- package/registry/api/service.ts +88 -0
- package/registry/api/tests/emit_safe.spec.ts +107 -0
- package/registry/api/tests/respond.spec.ts +40 -0
- package/registry/auth/contracts/index.ts +18 -0
- package/registry/auth/contracts/password_reset_store.ts +21 -0
- package/registry/auth/contracts/session_manager.ts +19 -0
- package/registry/auth/contracts/types.ts +32 -0
- package/registry/auth/events.ts +21 -0
- package/registry/auth/module.json +45 -0
- package/registry/auth/options.ts +23 -0
- package/registry/auth/service.ts +150 -0
- package/registry/auth/stubs/adapters/auth.ts +21 -0
- package/registry/auth/stubs/adapters/lucid_access_token_session.ts +33 -0
- package/registry/auth/stubs/adapters/lucid_password_reset_store.ts +68 -0
- package/registry/auth/stubs/adapters/lucid_user_store.ts +86 -0
- package/registry/auth/stubs/adapters/map_user.ts +19 -0
- package/registry/auth/stubs/controllers/auth_controller.ts +126 -0
- package/registry/auth/stubs/migrations/add_pending_email_to_users_table.ts +17 -0
- package/registry/auth/stubs/migrations/create_password_resets_table.ts +27 -0
- package/registry/auth/stubs/migrations/create_users_table.ts +31 -0
- package/registry/auth/stubs/models/password_reset.ts +29 -0
- package/registry/auth/stubs/models/user.ts +99 -0
- package/registry/auth/stubs/providers/vine_provider.ts +43 -0
- package/registry/auth/stubs/routes/auth.ts +36 -0
- package/registry/auth/stubs/start/limiter.ts +17 -0
- package/registry/auth/stubs/validators/auth.ts +48 -0
- package/registry/auth/stubs/validators/rules/exists.ts +21 -0
- package/registry/auth/stubs/validators/rules/unique.ts +21 -0
- package/registry/auth/stubs/validators/rules/valid_password.ts +20 -0
- package/registry/auth/stubs/validators/rules/valid_token.ts +27 -0
- package/registry/auth/stubs/validators/vine.d.ts +10 -0
- package/registry/auth/tests/auth_service.spec.ts +145 -0
- package/registry/auth/tests/auth_update_password.spec.ts +99 -0
- package/registry/auth/tests/fakes/index.ts +3 -0
- package/registry/auth/tests/fakes/memory_password_reset_store.ts +71 -0
- package/registry/auth/tests/fakes/memory_session_manager.ts +23 -0
- package/registry/auth/tests/fakes/memory_user_store.ts +89 -0
- package/registry/constants/exceptions.ts +64 -0
- package/registry/constants/index.ts +12 -0
- package/registry/constants/responseCodes.ts +87 -0
- package/registry/contracts/index.ts +5 -0
- package/registry/contracts/user.ts +78 -0
- package/registry/creem/contracts/creem_client.ts +34 -0
- package/registry/creem/contracts/index.ts +8 -0
- package/registry/creem/contracts/types.ts +30 -0
- package/registry/creem/events.ts +9 -0
- package/registry/creem/module.json +28 -0
- package/registry/creem/service.ts +87 -0
- package/registry/creem/stubs/adapters/creem.ts +23 -0
- package/registry/creem/stubs/adapters/creem_io_client.ts +70 -0
- package/registry/creem/tests/fakes/fake_creem_client.ts +68 -0
- package/registry/notification/contracts/app_notification_store.ts +8 -0
- package/registry/notification/contracts/index.ts +9 -0
- package/registry/notification/contracts/mail_transport.ts +7 -0
- package/registry/notification/contracts/types.ts +32 -0
- package/registry/notification/module.json +23 -0
- package/registry/notification/options.ts +11 -0
- package/registry/notification/service.ts +103 -0
- package/registry/notification/stubs/adapters/adonis_mail_transport.ts +15 -0
- package/registry/notification/stubs/adapters/lucid_app_notification_store.ts +21 -0
- package/registry/notification/stubs/adapters/notification.ts +36 -0
- package/registry/notification/stubs/migrations/create_notifications_table.ts +23 -0
- package/registry/notification/stubs/models/app_notification.ts +45 -0
- package/registry/notification/tests/fakes/memory_app_notification_store.ts +19 -0
- package/registry/notification/tests/fakes/memory_mail_transport.ts +16 -0
- package/registry/notification/tests/notification_service.spec.ts +63 -0
- package/registry/registry.json +4 -0
- package/registry/subscription/contracts/index.ts +9 -0
- package/registry/subscription/contracts/subscription_store.ts +15 -0
- package/registry/subscription/contracts/types.ts +39 -0
- package/registry/subscription/events.ts +13 -0
- package/registry/subscription/module.json +31 -0
- package/registry/subscription/options.ts +14 -0
- package/registry/subscription/service.ts +140 -0
- package/registry/subscription/stubs/adapters/lucid_subscription_store.ts +69 -0
- package/registry/subscription/stubs/adapters/subscription.ts +21 -0
- package/registry/subscription/stubs/migrations/create_subscriptions_table.ts +26 -0
- package/registry/subscription/stubs/migrations/subscriptions_user_creem_unique.ts +19 -0
- package/registry/subscription/stubs/models/subscription.ts +45 -0
- package/registry/subscription/tests/billing_service.spec.ts +189 -0
- package/registry/subscription/tests/fakes/memory_subscription_store.ts +63 -0
- package/registry/types/api.ts +41 -0
- package/registry/types/constants.ts +7 -0
- package/registry/types/index.ts +6 -0
- package/registry/types/models.ts +32 -0
- package/schema/adonia.schema.json +39 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { relative } from 'node:path';
|
|
3
|
+
import { loadHostConfig } from '../host.js';
|
|
4
|
+
import { loadManifest, resolveRegistry } from '../registry/index.js';
|
|
5
|
+
import { planModuleFiles, planStubs, resolveStubSelect } from '../registry/plan.js';
|
|
6
|
+
import { sha256 } from '../copy.js';
|
|
7
|
+
export async function runDiff(name, flags) {
|
|
8
|
+
if (!name) {
|
|
9
|
+
console.error('Usage: adonia diff <name>');
|
|
10
|
+
process.exitCode = 1;
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
let config;
|
|
14
|
+
try {
|
|
15
|
+
config = loadHostConfig(flags.cwd);
|
|
16
|
+
}
|
|
17
|
+
catch (err) {
|
|
18
|
+
console.error(err instanceof Error ? err.message : err);
|
|
19
|
+
process.exitCode = 1;
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
const registry = await resolveRegistry({
|
|
23
|
+
registryFlag: flags.registry,
|
|
24
|
+
hostRegistry: config.registry,
|
|
25
|
+
ref: config.ref,
|
|
26
|
+
cwd: flags.cwd,
|
|
27
|
+
});
|
|
28
|
+
const manifest = loadManifest(registry, name);
|
|
29
|
+
const warnings = [];
|
|
30
|
+
const stubSelect = resolveStubSelect(flags);
|
|
31
|
+
const plans = [
|
|
32
|
+
...planModuleFiles(registry, name, manifest, config, flags.cwd),
|
|
33
|
+
...(stubSelect
|
|
34
|
+
? planStubs(registry, name, manifest, config, flags.cwd, warnings, stubSelect)
|
|
35
|
+
: []),
|
|
36
|
+
];
|
|
37
|
+
let differed = false;
|
|
38
|
+
for (const plan of plans) {
|
|
39
|
+
if (!existsSync(plan.dest)) {
|
|
40
|
+
console.log(`--- missing: ${relative(flags.cwd, plan.dest)}`);
|
|
41
|
+
differed = true;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
const a = readFileSync(plan.src);
|
|
45
|
+
const b = readFileSync(plan.dest);
|
|
46
|
+
if (sha256(a) === sha256(b))
|
|
47
|
+
continue;
|
|
48
|
+
differed = true;
|
|
49
|
+
console.log(unifiedDiff(relative(flags.cwd, plan.dest), a.toString('utf8'), b.toString('utf8')));
|
|
50
|
+
}
|
|
51
|
+
for (const w of warnings)
|
|
52
|
+
console.warn(w);
|
|
53
|
+
if (differed)
|
|
54
|
+
process.exitCode = 1;
|
|
55
|
+
else
|
|
56
|
+
console.log('No differences.');
|
|
57
|
+
}
|
|
58
|
+
function unifiedDiff(path, registry, host) {
|
|
59
|
+
const a = registry.split('\n');
|
|
60
|
+
const b = host.split('\n');
|
|
61
|
+
const lines = [`--- registry/${path}`, `+++ host/${path}`];
|
|
62
|
+
const max = Math.max(a.length, b.length);
|
|
63
|
+
for (let i = 0; i < max; i++) {
|
|
64
|
+
const left = a[i];
|
|
65
|
+
const right = b[i];
|
|
66
|
+
if (left === right)
|
|
67
|
+
continue;
|
|
68
|
+
if (left !== undefined)
|
|
69
|
+
lines.push(`-${left}`);
|
|
70
|
+
if (right !== undefined)
|
|
71
|
+
lines.push(`+${right}`);
|
|
72
|
+
}
|
|
73
|
+
return lines.join('\n');
|
|
74
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { defaultHostConfig, isAdonisApp, loadHostConfig, writeHostConfig } from '../host.js';
|
|
2
|
+
import { expandDependencies, resolveRegistry } from '../registry/index.js';
|
|
3
|
+
import { CORE_PACKAGES } from '../schema.js';
|
|
4
|
+
import { applyWire, printWireChecklist } from '../wire.js';
|
|
5
|
+
import { installModules } from './add.js';
|
|
6
|
+
export async function runInit(flags) {
|
|
7
|
+
const cwd = flags.cwd;
|
|
8
|
+
const check = isAdonisApp(cwd);
|
|
9
|
+
if (!check.ok) {
|
|
10
|
+
console.error(check.reason);
|
|
11
|
+
process.exitCode = 1;
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
let wire = Boolean(flags.wire);
|
|
15
|
+
if (flags.scaffold && !wire) {
|
|
16
|
+
console.log('Scaffold implies --wire; enabling host wiring.');
|
|
17
|
+
wire = true;
|
|
18
|
+
}
|
|
19
|
+
let config = defaultHostConfig();
|
|
20
|
+
try {
|
|
21
|
+
config = loadHostConfig(cwd);
|
|
22
|
+
console.log('adonia.json already exists; keeping current config.');
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
writeHostConfig(cwd, config, flags.dryRun);
|
|
26
|
+
console.log(flags.dryRun ? 'Would write adonia.json' : 'Wrote adonia.json');
|
|
27
|
+
}
|
|
28
|
+
if (wire) {
|
|
29
|
+
const actions = applyWire(cwd, flags.dryRun);
|
|
30
|
+
if (actions.length) {
|
|
31
|
+
console.log(`${flags.dryRun ? 'Would wire' : 'Wired'}: ${actions.join(', ')}`);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
console.log('Host wiring already up to date.');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
printWireChecklist();
|
|
39
|
+
}
|
|
40
|
+
let registry;
|
|
41
|
+
try {
|
|
42
|
+
registry = await resolveRegistry({
|
|
43
|
+
registryFlag: flags.registry,
|
|
44
|
+
hostRegistry: config.registry,
|
|
45
|
+
ref: config.ref,
|
|
46
|
+
cwd,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
console.error(err instanceof Error ? err.message : err);
|
|
51
|
+
process.exitCode = 1;
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (!flags.dryRun) {
|
|
55
|
+
try {
|
|
56
|
+
config = loadHostConfig(cwd);
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
/* keep in-memory default */
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
if (flags.skipCore) {
|
|
63
|
+
console.log('Skipping core install (--skip-core).');
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
if (!flags.yes && !flags.dryRun) {
|
|
67
|
+
console.log('Install shared API core into ./modules? (yes)');
|
|
68
|
+
}
|
|
69
|
+
const coreNames = ['api', ...CORE_PACKAGES];
|
|
70
|
+
const toInstall = expandDependencies(registry, coreNames, new Set(config.installed), flags.overwrite);
|
|
71
|
+
if (!toInstall.length) {
|
|
72
|
+
console.log('Core already installed.');
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
const result = await installModules({
|
|
76
|
+
names: toInstall,
|
|
77
|
+
flags: { ...flags, yes: true, withTests: false, suppressNextSteps: true },
|
|
78
|
+
config,
|
|
79
|
+
registry,
|
|
80
|
+
});
|
|
81
|
+
if (result.conflicts.length || result.wireErrors.length) {
|
|
82
|
+
process.exitCode = 1;
|
|
83
|
+
}
|
|
84
|
+
if (!flags.dryRun) {
|
|
85
|
+
try {
|
|
86
|
+
config = loadHostConfig(cwd);
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
/* keep */
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (!flags.scaffold)
|
|
95
|
+
return;
|
|
96
|
+
const featureNames = registry.index.modules;
|
|
97
|
+
if (!featureNames.length) {
|
|
98
|
+
console.log('No feature modules in registry to scaffold.');
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
console.log(`Scaffolding feature modules: ${featureNames.join(', ')}`);
|
|
102
|
+
const ordered = expandDependencies(registry, featureNames, new Set(config.installed), flags.overwrite);
|
|
103
|
+
if (!ordered.length) {
|
|
104
|
+
console.log('Feature modules already installed. Use --overwrite to recopy stubs/routes.');
|
|
105
|
+
// Still attempt route wiring for already-installed modules when route files exist
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const result = await installModules({
|
|
109
|
+
names: ordered,
|
|
110
|
+
flags: {
|
|
111
|
+
...flags,
|
|
112
|
+
yes: true,
|
|
113
|
+
withStubs: true,
|
|
114
|
+
withRoutes: true,
|
|
115
|
+
wireRoutes: true,
|
|
116
|
+
withTests: false,
|
|
117
|
+
},
|
|
118
|
+
config,
|
|
119
|
+
registry,
|
|
120
|
+
});
|
|
121
|
+
if (result.conflicts.length || result.wireErrors.length) {
|
|
122
|
+
process.exitCode = 1;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { loadHostConfig } from '../host.js';
|
|
2
|
+
import { listAllModuleNames, loadManifest, resolveRegistry } from '../registry/index.js';
|
|
3
|
+
export async function runList(flags) {
|
|
4
|
+
let installed = new Set();
|
|
5
|
+
let hostRegistry;
|
|
6
|
+
let ref;
|
|
7
|
+
try {
|
|
8
|
+
const config = loadHostConfig(flags.cwd);
|
|
9
|
+
installed = new Set(config.installed);
|
|
10
|
+
hostRegistry = config.registry;
|
|
11
|
+
ref = config.ref;
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
console.log('(no adonia.json — showing registry only; run npx adonia init)');
|
|
15
|
+
}
|
|
16
|
+
const registry = await resolveRegistry({
|
|
17
|
+
registryFlag: flags.registry,
|
|
18
|
+
hostRegistry,
|
|
19
|
+
ref,
|
|
20
|
+
cwd: flags.cwd,
|
|
21
|
+
});
|
|
22
|
+
console.log(`Registry (${registry.source}): ${registry.root}\n`);
|
|
23
|
+
for (const name of listAllModuleNames(registry)) {
|
|
24
|
+
let description = '';
|
|
25
|
+
try {
|
|
26
|
+
description = loadManifest(registry, name).description;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
description = '';
|
|
30
|
+
}
|
|
31
|
+
const mark = installed.has(name) ? 'installed' : 'available';
|
|
32
|
+
console.log(` ${name.padEnd(16)} [${mark}] ${description}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Insert or replace a single module key in config/modules.ts.
|
|
3
|
+
* Uses a lightweight text approach so we don't need a TS parser.
|
|
4
|
+
*/
|
|
5
|
+
export declare function mergeModulesConfig(path: string, name: string, description: string, events: string[], dryRun?: boolean): {
|
|
6
|
+
ok: true;
|
|
7
|
+
} | {
|
|
8
|
+
ok: false;
|
|
9
|
+
snippet: string;
|
|
10
|
+
};
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
/**
|
|
3
|
+
* Insert or replace a single module key in config/modules.ts.
|
|
4
|
+
* Uses a lightweight text approach so we don't need a TS parser.
|
|
5
|
+
*/
|
|
6
|
+
export function mergeModulesConfig(path, name, description, events, dryRun = false) {
|
|
7
|
+
const entry = buildEntry(name, description, events);
|
|
8
|
+
if (!existsSync(path)) {
|
|
9
|
+
const content = `const modulesConfig: Record<string, { description: string; emits: string[] }> = {\n${entry}\n}\n\nexport default modulesConfig\n`;
|
|
10
|
+
if (!dryRun)
|
|
11
|
+
writeFileSync(path, content);
|
|
12
|
+
return { ok: true };
|
|
13
|
+
}
|
|
14
|
+
const original = readFileSync(path, 'utf8');
|
|
15
|
+
if (!/modulesConfig\s*[:=]/.test(original) && !/export\s+default/.test(original)) {
|
|
16
|
+
return { ok: false, snippet: entry };
|
|
17
|
+
}
|
|
18
|
+
// Replace existing key block or insert before closing brace of the object
|
|
19
|
+
const keyRe = new RegExp(`(^|\\n)\\s*${escapeRegExp(name)}\\s*:\\s*\\{[\\s\\S]*?\\n\\s*\\},?`, 'm');
|
|
20
|
+
let next;
|
|
21
|
+
if (keyRe.test(original)) {
|
|
22
|
+
next = original.replace(keyRe, `$1${entry}`);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
// Find the modulesConfig object closing
|
|
26
|
+
const openMatch = original.match(/((?:const|let|var)\s+modulesConfig[^=]*=\s*\{|modulesConfig\s*[:=]\s*\{)/);
|
|
27
|
+
if (!openMatch || openMatch.index === undefined) {
|
|
28
|
+
return { ok: false, snippet: entry };
|
|
29
|
+
}
|
|
30
|
+
const start = openMatch.index + openMatch[0].length;
|
|
31
|
+
const close = findMatchingBrace(original, start - 1);
|
|
32
|
+
if (close === -1)
|
|
33
|
+
return { ok: false, snippet: entry };
|
|
34
|
+
const before = original.slice(0, close);
|
|
35
|
+
const after = original.slice(close);
|
|
36
|
+
const needsComma = /[^\s,{]$/.test(before.trimEnd()) || /emits:\s*\[[^\]]*\]\s*$/.test(before);
|
|
37
|
+
const trimmed = before.replace(/\s*$/, '');
|
|
38
|
+
const comma = trimmed.endsWith(',') || trimmed.endsWith('{') ? '' : ',';
|
|
39
|
+
next = `${trimmed}${comma}\n${entry}\n${after}`;
|
|
40
|
+
void needsComma;
|
|
41
|
+
}
|
|
42
|
+
if (!dryRun)
|
|
43
|
+
writeFileSync(path, next);
|
|
44
|
+
return { ok: true };
|
|
45
|
+
}
|
|
46
|
+
function buildEntry(name, description, events) {
|
|
47
|
+
const emits = events.map((e) => ` '${e}',`).join('\n');
|
|
48
|
+
return ` ${name}: {
|
|
49
|
+
description: ${JSON.stringify(description)},
|
|
50
|
+
emits: [
|
|
51
|
+
${emits}
|
|
52
|
+
],
|
|
53
|
+
},`;
|
|
54
|
+
}
|
|
55
|
+
function escapeRegExp(s) {
|
|
56
|
+
return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
57
|
+
}
|
|
58
|
+
function findMatchingBrace(source, openIndex) {
|
|
59
|
+
let depth = 0;
|
|
60
|
+
for (let i = openIndex; i < source.length; i++) {
|
|
61
|
+
const ch = source[i];
|
|
62
|
+
if (ch === '{')
|
|
63
|
+
depth++;
|
|
64
|
+
else if (ch === '}') {
|
|
65
|
+
depth--;
|
|
66
|
+
if (depth === 0)
|
|
67
|
+
return i;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
return -1;
|
|
71
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
const CATALOG_FILES = ['exceptions.ts', 'responseCodes.ts'];
|
|
4
|
+
/**
|
|
5
|
+
* Merge registry constant catalogs into the host copy without deleting host keys.
|
|
6
|
+
*/
|
|
7
|
+
export function mergeConstantsCatalogs(registryConstantsDir, hostConstantsDir, dryRun = false) {
|
|
8
|
+
const warnings = [];
|
|
9
|
+
for (const file of CATALOG_FILES) {
|
|
10
|
+
const src = join(registryConstantsDir, file);
|
|
11
|
+
const dest = join(hostConstantsDir, file);
|
|
12
|
+
if (!existsSync(src) || !existsSync(dest))
|
|
13
|
+
continue;
|
|
14
|
+
const result = mergeOneCatalog(src, dest, dryRun);
|
|
15
|
+
if (result)
|
|
16
|
+
warnings.push(result);
|
|
17
|
+
}
|
|
18
|
+
return warnings;
|
|
19
|
+
}
|
|
20
|
+
function mergeOneCatalog(src, dest, dryRun) {
|
|
21
|
+
const registryText = readFileSync(src, 'utf8');
|
|
22
|
+
const hostText = readFileSync(dest, 'utf8');
|
|
23
|
+
const regEntries = parseCatalogEntries(registryText);
|
|
24
|
+
const hostEntries = parseCatalogEntries(hostText);
|
|
25
|
+
if (!regEntries || !hostEntries) {
|
|
26
|
+
return `Could not parse ${dest} for constants merge; copy keys from the registry by hand.`;
|
|
27
|
+
}
|
|
28
|
+
let inserted = 0;
|
|
29
|
+
const conflicts = [];
|
|
30
|
+
const hostKeys = new Set(Object.keys(hostEntries.map));
|
|
31
|
+
let nextBody = hostEntries.body;
|
|
32
|
+
for (const [key, value] of Object.entries(regEntries.map)) {
|
|
33
|
+
if (!hostKeys.has(key)) {
|
|
34
|
+
// insert before closing brace of object
|
|
35
|
+
const insert = ` ${key}: ${value},\n`;
|
|
36
|
+
nextBody = nextBody.replace(/\n\}\s*$/, `\n${insert}}`);
|
|
37
|
+
inserted++;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (normalizeEntry(hostEntries.map[key]) !== normalizeEntry(value)) {
|
|
41
|
+
conflicts.push(key);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (inserted && !dryRun) {
|
|
45
|
+
writeFileSync(dest, hostText.slice(0, hostEntries.start) + nextBody + hostText.slice(hostEntries.end));
|
|
46
|
+
}
|
|
47
|
+
if (conflicts.length) {
|
|
48
|
+
return `Constants conflict in ${dest}: ${conflicts.join(', ')}`;
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
function normalizeEntry(s) {
|
|
53
|
+
return s.replace(/\s+/g, '');
|
|
54
|
+
}
|
|
55
|
+
function parseCatalogEntries(text) {
|
|
56
|
+
// Find first `= {` after export/const
|
|
57
|
+
const m = text.match(/=\s*\{/);
|
|
58
|
+
if (!m || m.index === undefined)
|
|
59
|
+
return null;
|
|
60
|
+
const open = m.index + m[0].length - 1;
|
|
61
|
+
let depth = 0;
|
|
62
|
+
let end = -1;
|
|
63
|
+
for (let i = open; i < text.length; i++) {
|
|
64
|
+
if (text[i] === '{')
|
|
65
|
+
depth++;
|
|
66
|
+
else if (text[i] === '}') {
|
|
67
|
+
depth--;
|
|
68
|
+
if (depth === 0) {
|
|
69
|
+
end = i + 1;
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
if (end === -1)
|
|
75
|
+
return null;
|
|
76
|
+
const body = text.slice(open, end);
|
|
77
|
+
const map = {};
|
|
78
|
+
// Match KEY: { ... }, at top level
|
|
79
|
+
const entryRe = /(\w+)\s*:\s*(\{[^}]*\})/g;
|
|
80
|
+
let match;
|
|
81
|
+
while ((match = entryRe.exec(body))) {
|
|
82
|
+
map[match[1]] = match[2];
|
|
83
|
+
}
|
|
84
|
+
return { map, body, start: open, end };
|
|
85
|
+
}
|
package/build/copy.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type CopyResult = 'copied' | 'skipped' | 'conflict' | 'would-copy' | 'would-skip' | 'would-conflict';
|
|
2
|
+
export interface CopyOptions {
|
|
3
|
+
overwrite?: boolean;
|
|
4
|
+
dryRun?: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function sha256(content: Buffer | string): string;
|
|
7
|
+
export declare function copyFileWithPolicy(src: string, dest: string, options?: CopyOptions): CopyResult;
|
|
8
|
+
export declare function ensureDir(path: string, dryRun?: boolean): void;
|
package/build/copy.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { dirname } from 'node:path';
|
|
4
|
+
export function sha256(content) {
|
|
5
|
+
return createHash('sha256').update(content).digest('hex');
|
|
6
|
+
}
|
|
7
|
+
export function copyFileWithPolicy(src, dest, options = {}) {
|
|
8
|
+
const { overwrite = false, dryRun = false } = options;
|
|
9
|
+
const srcBuf = readFileSync(src);
|
|
10
|
+
if (!existsSync(dest)) {
|
|
11
|
+
if (dryRun)
|
|
12
|
+
return 'would-copy';
|
|
13
|
+
mkdirSync(dirname(dest), { recursive: true });
|
|
14
|
+
copyFileSync(src, dest);
|
|
15
|
+
return 'copied';
|
|
16
|
+
}
|
|
17
|
+
const destBuf = readFileSync(dest);
|
|
18
|
+
if (sha256(srcBuf) === sha256(destBuf)) {
|
|
19
|
+
return dryRun ? 'would-skip' : 'skipped';
|
|
20
|
+
}
|
|
21
|
+
if (!overwrite) {
|
|
22
|
+
return dryRun ? 'would-conflict' : 'conflict';
|
|
23
|
+
}
|
|
24
|
+
if (dryRun)
|
|
25
|
+
return 'would-copy';
|
|
26
|
+
writeFileSync(dest, srcBuf);
|
|
27
|
+
return 'copied';
|
|
28
|
+
}
|
|
29
|
+
export function ensureDir(path, dryRun = false) {
|
|
30
|
+
if (dryRun)
|
|
31
|
+
return;
|
|
32
|
+
mkdirSync(path, { recursive: true });
|
|
33
|
+
}
|
package/build/host.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type HostModulesConfig } from './schema.js';
|
|
2
|
+
export declare const ADONIA_JSON = "adonia.json";
|
|
3
|
+
/** @deprecated Read-only fallback for one minor after rename. */
|
|
4
|
+
export declare const LEGACY_MODULES_JSON = "modules.json";
|
|
5
|
+
export declare function packageVersion(): string;
|
|
6
|
+
export declare function isAdonisApp(cwd: string): {
|
|
7
|
+
ok: true;
|
|
8
|
+
} | {
|
|
9
|
+
ok: false;
|
|
10
|
+
reason: string;
|
|
11
|
+
};
|
|
12
|
+
export declare function loadHostConfig(cwd: string): HostModulesConfig;
|
|
13
|
+
export declare function writeHostConfig(cwd: string, config: HostModulesConfig, dryRun?: boolean): void;
|
|
14
|
+
export declare function defaultHostConfig(): HostModulesConfig;
|
|
15
|
+
export declare function detectPackageManager(cwd: string): 'npm' | 'pnpm' | 'yarn';
|
package/build/host.js
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { createRequire } from 'node:module';
|
|
3
|
+
import { dirname, join } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
import { hostModulesSchema } from './schema.js';
|
|
6
|
+
export const ADONIA_JSON = 'adonia.json';
|
|
7
|
+
/** @deprecated Read-only fallback for one minor after rename. */
|
|
8
|
+
export const LEGACY_MODULES_JSON = 'modules.json';
|
|
9
|
+
const require = createRequire(import.meta.url);
|
|
10
|
+
export function packageVersion() {
|
|
11
|
+
try {
|
|
12
|
+
const pkgPath = join(dirname(fileURLToPath(import.meta.url)), '../package.json');
|
|
13
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
14
|
+
return pkg.version ?? '0.0.0';
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
try {
|
|
18
|
+
return require('../package.json').version;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return '0.0.0';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export function isAdonisApp(cwd) {
|
|
26
|
+
const adonisrc = join(cwd, 'adonisrc.ts');
|
|
27
|
+
const pkgPath = join(cwd, 'package.json');
|
|
28
|
+
if (!existsSync(adonisrc)) {
|
|
29
|
+
return { ok: false, reason: 'adonisrc.ts not found — not an Adonis app' };
|
|
30
|
+
}
|
|
31
|
+
if (!existsSync(pkgPath)) {
|
|
32
|
+
return { ok: false, reason: 'package.json not found' };
|
|
33
|
+
}
|
|
34
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
35
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
36
|
+
if (!deps['@adonisjs/core']) {
|
|
37
|
+
return { ok: false, reason: 'package.json does not depend on @adonisjs/core' };
|
|
38
|
+
}
|
|
39
|
+
return { ok: true };
|
|
40
|
+
}
|
|
41
|
+
function configPath(cwd) {
|
|
42
|
+
const primary = join(cwd, ADONIA_JSON);
|
|
43
|
+
if (existsSync(primary))
|
|
44
|
+
return primary;
|
|
45
|
+
const legacy = join(cwd, LEGACY_MODULES_JSON);
|
|
46
|
+
if (existsSync(legacy))
|
|
47
|
+
return legacy;
|
|
48
|
+
throw new Error(`Missing ${ADONIA_JSON}. Run \`npx adonia init\` first.`);
|
|
49
|
+
}
|
|
50
|
+
export function loadHostConfig(cwd) {
|
|
51
|
+
const path = configPath(cwd);
|
|
52
|
+
return hostModulesSchema.parse(JSON.parse(readFileSync(path, 'utf8')));
|
|
53
|
+
}
|
|
54
|
+
export function writeHostConfig(cwd, config, dryRun = false) {
|
|
55
|
+
if (dryRun)
|
|
56
|
+
return;
|
|
57
|
+
writeFileSync(join(cwd, ADONIA_JSON), `${JSON.stringify(config, null, 2)}\n`);
|
|
58
|
+
}
|
|
59
|
+
export function defaultHostConfig() {
|
|
60
|
+
return hostModulesSchema.parse({
|
|
61
|
+
$schema: './node_modules/adonia/schema/adonia.schema.json',
|
|
62
|
+
registry: 'bundled',
|
|
63
|
+
ref: packageVersion(),
|
|
64
|
+
paths: {
|
|
65
|
+
modules: 'modules',
|
|
66
|
+
models: 'app/models',
|
|
67
|
+
migrations: 'database/migrations',
|
|
68
|
+
controllers: 'app/controllers',
|
|
69
|
+
validators: 'app/validators',
|
|
70
|
+
},
|
|
71
|
+
aliases: {
|
|
72
|
+
modules: '#modules',
|
|
73
|
+
constants: '#constants',
|
|
74
|
+
models: '#models',
|
|
75
|
+
},
|
|
76
|
+
installed: [],
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
export function detectPackageManager(cwd) {
|
|
80
|
+
if (existsSync(join(cwd, 'pnpm-lock.yaml')))
|
|
81
|
+
return 'pnpm';
|
|
82
|
+
if (existsSync(join(cwd, 'yarn.lock')))
|
|
83
|
+
return 'yarn';
|
|
84
|
+
return 'npm';
|
|
85
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type ModuleManifest, type RegistryIndex } from '../schema.js';
|
|
2
|
+
export interface Registry {
|
|
3
|
+
root: string;
|
|
4
|
+
index: RegistryIndex;
|
|
5
|
+
source: 'flag' | 'bundled';
|
|
6
|
+
}
|
|
7
|
+
export declare function bundledRegistryPath(): string;
|
|
8
|
+
export declare function loadRegistryIndex(root: string): RegistryIndex;
|
|
9
|
+
/**
|
|
10
|
+
* Resolution order:
|
|
11
|
+
* 1. --registry <dir>
|
|
12
|
+
* 2. bundled (adonia.json "bundled" or empty)
|
|
13
|
+
* 3. local path in adonia.json
|
|
14
|
+
* github:… is rejected until remote materialization lands.
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveRegistry(options: {
|
|
17
|
+
registryFlag?: string;
|
|
18
|
+
hostRegistry?: string;
|
|
19
|
+
ref?: string;
|
|
20
|
+
cwd: string;
|
|
21
|
+
}): Promise<Registry>;
|
|
22
|
+
export declare function loadManifest(registry: Registry, name: string): ModuleManifest;
|
|
23
|
+
export declare function expandDependencies(registry: Registry, names: string[], installed: Set<string>, overwrite: boolean): string[];
|
|
24
|
+
export declare function listAllModuleNames(registry: Registry): string[];
|
|
25
|
+
export declare function moduleDir(registry: Registry, name: string): string;
|
|
26
|
+
export declare function relativeFromRegistry(registry: Registry, absPath: string): string;
|