Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quarto-dev
GitHub Repository: quarto-dev/quarto-cli
Path: blob/main/src/core/api/registry.ts
6452 views
1
// src/core/api/registry.ts
2
3
import type {
4
NamespaceProviders,
5
ProviderFunction,
6
QuartoAPI,
7
} from "./types.ts";
8
9
/**
10
* Error thrown when attempting to access an unregistered namespace
11
*/
12
export class UnregisteredNamespaceError extends Error {
13
constructor(namespace: string) {
14
super(
15
`QuartoAPI namespace '${namespace}' has not been registered. ` +
16
`Ensure that 'src/core/api/register.ts' is imported before using the API.`,
17
);
18
this.name = "UnregisteredNamespaceError";
19
}
20
}
21
22
/**
23
* Error thrown when attempting to register after API has been finalized
24
*/
25
export class RegistryFinalizedError extends Error {
26
constructor(namespace: string) {
27
super(
28
`Cannot register namespace '${namespace}': Registry has been finalized. ` +
29
`All registrations must occur before createAPI() is called.`,
30
);
31
this.name = "RegistryFinalizedError";
32
}
33
}
34
35
/**
36
* Generic registry for QuartoAPI namespaces using dependency inversion pattern
37
*/
38
export class QuartoAPIRegistry {
39
private providers: NamespaceProviders = {};
40
private implementations: Partial<QuartoAPI> = {};
41
private finalized = false;
42
private apiInstance: QuartoAPI | null = null;
43
44
/**
45
* Register a namespace provider function
46
* @throws {RegistryFinalizedError} if registry is already finalized
47
* @throws {Error} if namespace is already registered
48
*/
49
register<K extends keyof QuartoAPI>(
50
namespace: K,
51
provider: ProviderFunction<QuartoAPI[K]>,
52
): void {
53
if (this.finalized) {
54
throw new RegistryFinalizedError(namespace);
55
}
56
57
if (this.providers[namespace]) {
58
throw new Error(
59
`QuartoAPI namespace '${namespace}' is already registered`,
60
);
61
}
62
63
// deno-lint-ignore no-explicit-any
64
(this.providers as any)[namespace] = provider;
65
}
66
67
/**
68
* Create the QuartoAPI instance with eager initialization and validation
69
* @returns {QuartoAPI} The complete API object with all namespaces
70
* @throws {UnregisteredNamespaceError} if any required namespace is missing
71
*/
72
createAPI(): QuartoAPI {
73
// Return cached instance if already created
74
if (this.apiInstance) {
75
return this.apiInstance;
76
}
77
78
// List of all required namespaces
79
const requiredNamespaces: Array<keyof QuartoAPI> = [
80
"markdownRegex",
81
"mappedString",
82
"jupyter",
83
"format",
84
"path",
85
"system",
86
"text",
87
"console",
88
"crypto",
89
];
90
91
// Validate all required namespaces are registered
92
const missingNamespaces = requiredNamespaces.filter(
93
(ns) => !this.providers[ns],
94
);
95
96
if (missingNamespaces.length > 0) {
97
throw new UnregisteredNamespaceError(
98
`Missing required namespaces: ${missingNamespaces.join(", ")}`,
99
);
100
}
101
102
// Eagerly initialize all namespaces by calling provider functions
103
for (const namespace of requiredNamespaces) {
104
const provider = this.providers[namespace];
105
if (provider) {
106
// deno-lint-ignore no-explicit-any
107
(this.implementations as any)[namespace] = provider();
108
}
109
}
110
111
// Mark registry as finalized
112
this.finalized = true;
113
114
// Create and cache the API instance
115
this.apiInstance = this.implementations as QuartoAPI;
116
117
return this.apiInstance;
118
}
119
120
/**
121
* Check if a namespace is registered (useful for optional features)
122
*/
123
isRegistered(namespace: keyof QuartoAPI): boolean {
124
return !!this.providers[namespace];
125
}
126
127
/**
128
* Clear cached implementations (for testing purposes)
129
*/
130
clearCache(): void {
131
this.implementations = {};
132
this.apiInstance = null;
133
this.finalized = false;
134
}
135
}
136
137
/**
138
* Global registry instance
139
*/
140
export const globalRegistry = new QuartoAPIRegistry();
141
142