@warlock.js/core 4.2.1 → 4.2.4
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/esm/_virtual/_rolldown/runtime.mjs +7 -0
- package/esm/cli/cli-commands.manager.mjs +290 -0
- package/esm/cli/cli-commands.manager.mjs.map +1 -0
- package/esm/cli/cli-commands.utils.mjs +217 -0
- package/esm/cli/cli-commands.utils.mjs.map +1 -0
- package/esm/cli/commands/add.command.mjs +27 -0
- package/esm/cli/commands/add.command.mjs.map +1 -0
- package/esm/cli/commands/build.command.mjs +14 -0
- package/esm/cli/commands/build.command.mjs.map +1 -0
- package/esm/cli/commands/create-database.command.mjs +24 -0
- package/esm/cli/commands/create-database.command.mjs.map +1 -0
- package/esm/cli/commands/dev-server.command.mjs +48 -0
- package/esm/cli/commands/dev-server.command.mjs.map +1 -0
- package/esm/cli/commands/drop-tables.command.mjs +23 -0
- package/esm/cli/commands/drop-tables.command.mjs.map +1 -0
- package/esm/cli/commands/generate/generate.command.mjs +201 -0
- package/esm/cli/commands/generate/generate.command.mjs.map +1 -0
- package/esm/cli/commands/generate/generators/column-dsl-parser.mjs +49 -0
- package/esm/cli/commands/generate/generators/column-dsl-parser.mjs.map +1 -0
- package/esm/cli/commands/generate/generators/controller.generator.mjs +48 -0
- package/esm/cli/commands/generate/generators/controller.generator.mjs.map +1 -0
- package/esm/cli/commands/generate/generators/migration.generator.mjs +81 -0
- package/esm/cli/commands/generate/generators/migration.generator.mjs.map +1 -0
- package/esm/cli/commands/generate/generators/model.generator.mjs +60 -0
- package/esm/cli/commands/generate/generators/model.generator.mjs.map +1 -0
- package/esm/cli/commands/generate/generators/module.generator.mjs +113 -0
- package/esm/cli/commands/generate/generators/module.generator.mjs.map +1 -0
- package/esm/cli/commands/generate/generators/repository.generator.mjs +42 -0
- package/esm/cli/commands/generate/generators/repository.generator.mjs.map +1 -0
- package/esm/cli/commands/generate/generators/resource.generator.mjs +42 -0
- package/esm/cli/commands/generate/generators/resource.generator.mjs.map +1 -0
- package/esm/cli/commands/generate/generators/service.generator.mjs +42 -0
- package/esm/cli/commands/generate/generators/service.generator.mjs.map +1 -0
- package/esm/cli/commands/generate/templates/stubs.mjs +514 -0
- package/esm/cli/commands/generate/templates/stubs.mjs.map +1 -0
- package/esm/cli/commands/generate/utils/name-parser.mjs +69 -0
- package/esm/cli/commands/generate/utils/name-parser.mjs.map +1 -0
- package/esm/cli/commands/generate/utils/path-resolver.mjs +56 -0
- package/esm/cli/commands/generate/utils/path-resolver.mjs.map +1 -0
- package/esm/cli/commands/generate/utils/prompt.mjs +28 -0
- package/esm/cli/commands/generate/utils/prompt.mjs.map +1 -0
- package/esm/cli/commands/generate/utils/writer.mjs +60 -0
- package/esm/cli/commands/generate/utils/writer.mjs.map +1 -0
- package/esm/cli/commands/migrate.command.mjs +60 -0
- package/esm/cli/commands/migrate.command.mjs.map +1 -0
- package/esm/cli/commands/seed.command.mjs +41 -0
- package/esm/cli/commands/seed.command.mjs.map +1 -0
- package/esm/cli/commands/start-production.command.mjs +50 -0
- package/esm/cli/commands/start-production.command.mjs.map +1 -0
- package/esm/cli/commands/storage-put.action.mjs +75 -0
- package/esm/cli/commands/storage-put.action.mjs.map +1 -0
- package/esm/cli/commands/storage-put.command.mjs +46 -0
- package/esm/cli/commands/storage-put.command.mjs.map +1 -0
- package/esm/cli/commands/typings-generator.command.mjs +38 -0
- package/esm/cli/commands/typings-generator.command.mjs.map +1 -0
- package/esm/cli/commands-loader.mjs +50 -0
- package/esm/cli/commands-loader.mjs.map +1 -0
- package/esm/cli/framework-cli-commands.mjs +37 -0
- package/esm/cli/framework-cli-commands.mjs.map +1 -0
- package/esm/cli/parse-cli-args.mjs +62 -0
- package/esm/cli/parse-cli-args.mjs.map +1 -0
- package/esm/cli/start.d.mts +1 -0
- package/esm/cli/start.mjs +8 -0
- package/esm/cli/start.mjs.map +1 -0
- package/esm/cli/string-similarity.mjs +56 -0
- package/esm/cli/string-similarity.mjs.map +1 -0
- package/esm/database/create-database-action.mjs +32 -0
- package/esm/database/create-database-action.mjs.map +1 -0
- package/esm/database/drop-tables-action.mjs +35 -0
- package/esm/database/drop-tables-action.mjs.map +1 -0
- package/esm/database/migrate-action.mjs +99 -0
- package/esm/database/migrate-action.mjs.map +1 -0
- package/esm/database/seed-command-action.mjs +74 -0
- package/esm/database/seed-command-action.mjs.map +1 -0
- package/esm/database/seeds/seeders.manager.mjs +129 -0
- package/esm/database/seeds/seeders.manager.mjs.map +1 -0
- package/esm/database/seeds/seeds-table-migration.mjs +31 -0
- package/esm/database/seeds/seeds-table-migration.mjs.map +1 -0
- package/esm/database/seeds/utils.mjs +6 -0
- package/esm/database/seeds/utils.mjs.map +1 -0
- package/esm/dev-server/dev-logger.mjs +12 -1
- package/esm/dev-server/dev-logger.mjs.map +1 -1
- package/esm/dev-server/development-server.mjs +115 -0
- package/esm/dev-server/development-server.mjs.map +1 -0
- package/esm/dev-server/health-checker/workers/eslint-health.worker.d.mts +1 -0
- package/esm/dev-server/health-checker/workers/ts-health.worker.d.mts +1 -0
- package/esm/dev-server/layer-executor.mjs +114 -0
- package/esm/dev-server/layer-executor.mjs.map +1 -0
- package/esm/dev-server/loader/hook-thread.d.mts +48 -0
- package/esm/dev-server/loader/hook-thread.d.mts.map +1 -0
- package/esm/dev-server/loader/hook-thread.mjs +47 -0
- package/esm/dev-server/loader/hook-thread.mjs.map +1 -0
- package/esm/dev-server/loader/load-hook.d.mts +58 -0
- package/esm/dev-server/loader/load-hook.d.mts.map +1 -0
- package/esm/dev-server/loader/load-hook.mjs +86 -0
- package/esm/dev-server/loader/load-hook.mjs.map +1 -0
- package/esm/dev-server/loader/own-resolver.mjs +69 -0
- package/esm/dev-server/loader/own-resolver.mjs.map +1 -0
- package/esm/dev-server/loader/resolve-capture.mjs +43 -0
- package/esm/dev-server/loader/resolve-capture.mjs.map +1 -0
- package/esm/dev-server/loader/resolve-hook.d.mts +41 -0
- package/esm/dev-server/loader/resolve-hook.d.mts.map +1 -0
- package/esm/dev-server/loader/resolve-hook.mjs +87 -0
- package/esm/dev-server/loader/resolve-hook.mjs.map +1 -0
- package/esm/dev-server/loader/source-slug.mjs +22 -0
- package/esm/dev-server/loader/source-slug.mjs.map +1 -0
- package/esm/dev-server/loader/transpile-cache.mjs +163 -1
- package/esm/dev-server/loader/transpile-cache.mjs.map +1 -1
- package/esm/dev-server/loader/version-registry.mjs +45 -0
- package/esm/dev-server/loader/version-registry.mjs.map +1 -0
- package/esm/dev-server/start-development-server.mjs +63 -0
- package/esm/dev-server/start-development-server.mjs.map +1 -0
- package/esm/dev-server/type-generator.mjs +453 -0
- package/esm/dev-server/type-generator.mjs.map +1 -0
- package/esm/generations/add-command.action.mjs +480 -0
- package/esm/generations/add-command.action.mjs.map +1 -0
- package/esm/generations/stubs.mjs +436 -0
- package/esm/generations/stubs.mjs.map +1 -0
- package/esm/manifest/manifest-manager.mjs +79 -0
- package/esm/manifest/manifest-manager.mjs.map +1 -0
- package/esm/production/build-app-production.mjs +14 -0
- package/esm/production/build-app-production.mjs.map +1 -0
- package/esm/production/esbuild-plugins.mjs +37 -0
- package/esm/production/esbuild-plugins.mjs.map +1 -0
- package/esm/production/production-builder.mjs +264 -0
- package/esm/production/production-builder.mjs.map +1 -0
- package/esm/production/resolve-build-config.mjs +28 -0
- package/esm/production/resolve-build-config.mjs.map +1 -0
- package/esm/utils/framework-vesion.mjs +10 -1
- package/esm/utils/framework-vesion.mjs.map +1 -1
- package/package.json +33 -9
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
//#region ../@warlock.js/core/src/generations/stubs.ts
|
|
2
|
+
const accessConfigStub = `import { type AccessConfigurations } from "@warlock.js/access";
|
|
3
|
+
import { DatabaseAccessResolver } from "app/access/services/access-resolver";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Authorization configuration — read by @warlock.js/access on boot.
|
|
7
|
+
*
|
|
8
|
+
* The resolver is the one required piece: it tells the engine how to read a
|
|
9
|
+
* user's roles + permissions. The ejected DatabaseAccessResolver reads roles
|
|
10
|
+
* from the user_roles table and maps them through the roles catalog table (so
|
|
11
|
+
* roles + their permissions are managed at runtime, in the DB).
|
|
12
|
+
*
|
|
13
|
+
* For a fixed, code-defined catalog with no tables, swap in DefaultAccessResolver:
|
|
14
|
+
* import { DefaultAccessResolver } from "@warlock.js/access";
|
|
15
|
+
* resolver: new DefaultAccessResolver({ admin: ["*"], editor: ["orders.*"] }),
|
|
16
|
+
*
|
|
17
|
+
* Multi-tenant? Add a \`resolveTenant()\` to the resolver to read the active
|
|
18
|
+
* tenant from the request; checks then scope to it automatically.
|
|
19
|
+
*/
|
|
20
|
+
const access: AccessConfigurations = {
|
|
21
|
+
resolver: new DatabaseAccessResolver(),
|
|
22
|
+
|
|
23
|
+
// Cache resolved permission sets (default "10m").
|
|
24
|
+
// cache: { ttl: "10m" },
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default access;
|
|
28
|
+
`;
|
|
29
|
+
const accessRoleModelStub = `import { Model, RegisterModel } from "@warlock.js/cascade";
|
|
30
|
+
import { type Infer, v } from "@warlock.js/seal";
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Validation schema for the roles catalog — mirrors the migration columns
|
|
34
|
+
* (snake_case). Each row is a role name plus the permission strings it grants;
|
|
35
|
+
* wildcards work ("orders.*", "*"). The DatabaseAccessResolver maps a user's
|
|
36
|
+
* assigned role names through this table to their effective permissions.
|
|
37
|
+
*/
|
|
38
|
+
export const roleSchema = v.object({
|
|
39
|
+
name: v.string(),
|
|
40
|
+
permissions: v.array(v.string()).default([]),
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
export type RoleSchema = Infer<typeof roleSchema>;
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* The roles catalog — role name → the permissions it grants. Managed at runtime
|
|
47
|
+
* (admins add roles + edit their permissions), unlike a fixed code map. Read by
|
|
48
|
+
* DatabaseAccessResolver.resolvePermissions to expand a user's roles to permissions.
|
|
49
|
+
*/
|
|
50
|
+
@RegisterModel()
|
|
51
|
+
export class Role extends Model<RoleSchema> {
|
|
52
|
+
public static table = "roles";
|
|
53
|
+
|
|
54
|
+
public static schema = roleSchema;
|
|
55
|
+
|
|
56
|
+
/** The permission strings this role grants. */
|
|
57
|
+
public get permissions(): string[] {
|
|
58
|
+
return this.get<string[]>("permissions", []);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
`;
|
|
62
|
+
const accessRoleModelIndexStub = `export * from "./role.model";
|
|
63
|
+
`;
|
|
64
|
+
const accessRoleMigrationStub = `import { arrayText, Migration, text } from "@warlock.js/cascade";
|
|
65
|
+
import { Role } from "../role.model";
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Roles catalog table. \`name\` is unique (one row per role); \`permissions\` is a
|
|
69
|
+
* text array of the permission strings the role grants.
|
|
70
|
+
*/
|
|
71
|
+
export default Migration.create(Role, {
|
|
72
|
+
name: text().notNullable().unique(),
|
|
73
|
+
permissions: arrayText().nullable(),
|
|
74
|
+
});
|
|
75
|
+
`;
|
|
76
|
+
const accessUserRoleModelStub = `import { access } from "@warlock.js/access";
|
|
77
|
+
import type { Auth } from "@warlock.js/auth";
|
|
78
|
+
import { Model, RegisterModel } from "@warlock.js/cascade";
|
|
79
|
+
import { type Infer, v } from "@warlock.js/seal";
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Validation schema for a role assignment — mirrors the migration columns
|
|
83
|
+
* (snake_case). \`tenant\` is nullable: a null tenant is a GLOBAL assignment.
|
|
84
|
+
*/
|
|
85
|
+
export const userRoleSchema = v.object({
|
|
86
|
+
user_id: v.string(),
|
|
87
|
+
user_type: v.string(),
|
|
88
|
+
role: v.string(),
|
|
89
|
+
tenant: v.string().optional(),
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
export type UserRoleSchema = Infer<typeof userRoleSchema>;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* The role-assignment table — which roles a user holds, optionally per tenant.
|
|
96
|
+
* Read by DatabaseAccessResolver.resolveRoles; mutated via the statics below.
|
|
97
|
+
* \`assign\` / \`revoke\` flush the cached permission set automatically, so callers
|
|
98
|
+
* never need to call \`access.flush(user, tenant)\` themselves.
|
|
99
|
+
*/
|
|
100
|
+
@RegisterModel()
|
|
101
|
+
export class UserRole extends Model<UserRoleSchema> {
|
|
102
|
+
public static table = "user_roles";
|
|
103
|
+
|
|
104
|
+
public static schema = userRoleSchema;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Role names assigned to the user in the given tenant.
|
|
108
|
+
*
|
|
109
|
+
* An unresolved tenant (\`undefined\`) scopes to GLOBAL roles only — the rows
|
|
110
|
+
* stored with no tenant (\`null\`) — never the union across every tenant. The
|
|
111
|
+
* union would be a privilege-escalation: a user who is \`owner\` in one tenant
|
|
112
|
+
* must not be treated as \`owner\` everywhere just because a check didn't carry
|
|
113
|
+
* a tenant. This mirrors how \`assign(user, role)\` stores a global row.
|
|
114
|
+
*/
|
|
115
|
+
public static async rolesFor(user: Auth, tenant?: string): Promise<string[]> {
|
|
116
|
+
const rows = await this.query()
|
|
117
|
+
.where({
|
|
118
|
+
user_id: user.id,
|
|
119
|
+
user_type: user.userType,
|
|
120
|
+
tenant: tenant ?? null,
|
|
121
|
+
})
|
|
122
|
+
.get();
|
|
123
|
+
|
|
124
|
+
// De-dupe so a duplicate row (a concurrent assign that slipped past the
|
|
125
|
+
// existence check) can't distort the resolved set.
|
|
126
|
+
return [...new Set(rows.map((row) => row.get("role") as string))];
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Assign a role to the user. No-op if the assignment already exists.
|
|
131
|
+
* Flushes the user's cached permission set automatically.
|
|
132
|
+
*/
|
|
133
|
+
public static async assign(user: Auth, role: string, tenant?: string): Promise<void> {
|
|
134
|
+
const existing = await this.first({
|
|
135
|
+
user_id: user.id,
|
|
136
|
+
user_type: user.userType,
|
|
137
|
+
role,
|
|
138
|
+
tenant: tenant ?? null,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
if (existing) return;
|
|
142
|
+
|
|
143
|
+
await this.create({
|
|
144
|
+
user_id: user.id,
|
|
145
|
+
user_type: user.userType,
|
|
146
|
+
role,
|
|
147
|
+
tenant,
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
await access.flush(user, tenant);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Remove a role assignment from the user.
|
|
155
|
+
* Flushes the user's cached permission set automatically.
|
|
156
|
+
*/
|
|
157
|
+
public static async revoke(user: Auth, role: string, tenant?: string): Promise<void> {
|
|
158
|
+
await this.delete({
|
|
159
|
+
user_id: user.id,
|
|
160
|
+
user_type: user.userType,
|
|
161
|
+
role,
|
|
162
|
+
tenant: tenant ?? null,
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
await access.flush(user, tenant);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
`;
|
|
169
|
+
const accessUserRoleModelIndexStub = `export * from "./user-role.model";
|
|
170
|
+
`;
|
|
171
|
+
const accessUserRoleMigrationStub = `import { Migration, text, uuid } from "@warlock.js/cascade";
|
|
172
|
+
import { UserRole } from "../user-role.model";
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Role-assignment table. \`user_id\` is a UUID — override this migration if your
|
|
176
|
+
* user ids are integers. The composite index powers the per-user (per-tenant)
|
|
177
|
+
* lookup the resolver runs on every check.
|
|
178
|
+
*/
|
|
179
|
+
export default Migration.create(
|
|
180
|
+
UserRole,
|
|
181
|
+
{
|
|
182
|
+
user_id: uuid().notNullable().index(),
|
|
183
|
+
user_type: text().notNullable(),
|
|
184
|
+
role: text().notNullable().index(),
|
|
185
|
+
tenant: text().nullable().index(),
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
index: [{ columns: ["user_id", "user_type", "tenant"] }],
|
|
189
|
+
},
|
|
190
|
+
);
|
|
191
|
+
`;
|
|
192
|
+
const accessResolverStub = `import type { AccessResolver } from "@warlock.js/access";
|
|
193
|
+
import type { Auth } from "@warlock.js/auth";
|
|
194
|
+
import { Role } from "app/access/models/role";
|
|
195
|
+
import { UserRole } from "app/access/models/user-role";
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* The app's access adapter — connects @warlock.js/access to the ejected role
|
|
199
|
+
* tables. Roles come from the user_roles assignment table; permissions are
|
|
200
|
+
* expanded by mapping those role names through the roles catalog table. Both
|
|
201
|
+
* are managed at runtime (in the DB), so admins can add roles + edit their
|
|
202
|
+
* permissions without a deploy.
|
|
203
|
+
*
|
|
204
|
+
* The engine owns the hard parts (wildcard matching, caching, fail-closed); this
|
|
205
|
+
* resolver only fetches — keep it dumb, never cache inside it.
|
|
206
|
+
*/
|
|
207
|
+
export class DatabaseAccessResolver implements AccessResolver {
|
|
208
|
+
/** The role names this user holds (powers \`hasRole\` / \`hasAnyRole\`). */
|
|
209
|
+
public async resolveRoles(user: Auth, tenant?: string): Promise<string[]> {
|
|
210
|
+
return UserRole.rolesFor(user, tenant);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/** The effective permission strings this user has (powers \`can\` / \`authorize\`). */
|
|
214
|
+
public async resolvePermissions(user: Auth, tenant?: string): Promise<string[]> {
|
|
215
|
+
const names = await this.resolveRoles(user, tenant);
|
|
216
|
+
|
|
217
|
+
if (names.length === 0) return [];
|
|
218
|
+
|
|
219
|
+
const roles = await Role.query().whereIn("name", names).get();
|
|
220
|
+
|
|
221
|
+
// Flatten + de-dupe so two roles granting the same permission yield one entry.
|
|
222
|
+
return [...new Set(roles.flatMap((role) => role.permissions))];
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Optional. Resolve the ambient tenant when a check doesn't pass one
|
|
227
|
+
* explicitly — derive it from the authenticated user (safer than reading
|
|
228
|
+
* client request input, which a caller could spoof). Uncomment + adapt for a
|
|
229
|
+
* multi-tenant app (single-tenant apps leave this off and return undefined).
|
|
230
|
+
*/
|
|
231
|
+
// public resolveTenant(user: Auth): string | undefined {
|
|
232
|
+
// return user.get("organization_id");
|
|
233
|
+
// }
|
|
234
|
+
}
|
|
235
|
+
`;
|
|
236
|
+
const socketConfigStub = `import type { SocketOptions } from "@warlock.js/core";
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Socket.IO configuration — read by the framework's socket connector
|
|
240
|
+
* on boot. When the HTTP server is running the socket server attaches
|
|
241
|
+
* to it; otherwise it listens on its own configured port.
|
|
242
|
+
*
|
|
243
|
+
* Remove this file to disable the socket server entirely.
|
|
244
|
+
*/
|
|
245
|
+
export default {
|
|
246
|
+
options: {
|
|
247
|
+
cors: {
|
|
248
|
+
origin: "*",
|
|
249
|
+
},
|
|
250
|
+
},
|
|
251
|
+
} as SocketOptions;
|
|
252
|
+
`;
|
|
253
|
+
const communicatorsConfigStub = `import { env } from "@mongez/dotenv";
|
|
254
|
+
import type { BrokerConfigurations, RabbitMQClientOptions } from "@warlock.js/herald";
|
|
255
|
+
|
|
256
|
+
const heraldConfigurations: BrokerConfigurations<RabbitMQClientOptions> = {
|
|
257
|
+
driver: "rabbitmq",
|
|
258
|
+
name: "default",
|
|
259
|
+
isDefault: true,
|
|
260
|
+
|
|
261
|
+
// ============================================================================
|
|
262
|
+
// Connection Settings
|
|
263
|
+
// ============================================================================
|
|
264
|
+
|
|
265
|
+
host: env("RABBITMQ_HOST", "localhost"),
|
|
266
|
+
port: env("RABBITMQ_PORT", 5672),
|
|
267
|
+
username: env("RABBITMQ_USERNAME", "guest"),
|
|
268
|
+
password: env("RABBITMQ_PASSWORD", "guest"),
|
|
269
|
+
vhost: env("RABBITMQ_VHOST", "/"),
|
|
270
|
+
|
|
271
|
+
// Or use connection URI (takes precedence over host/port)
|
|
272
|
+
// uri: env("RABBITMQ_URL"),
|
|
273
|
+
|
|
274
|
+
// ============================================================================
|
|
275
|
+
// Connection Options
|
|
276
|
+
// ============================================================================
|
|
277
|
+
|
|
278
|
+
/** Heartbeat interval in seconds */
|
|
279
|
+
heartbeat: 60,
|
|
280
|
+
|
|
281
|
+
/** Connection timeout in milliseconds */
|
|
282
|
+
connectionTimeout: 10000,
|
|
283
|
+
|
|
284
|
+
/** Enable automatic reconnection on disconnect */
|
|
285
|
+
reconnect: true,
|
|
286
|
+
|
|
287
|
+
/** Delay between reconnection attempts in milliseconds */
|
|
288
|
+
reconnectDelay: 5_000,
|
|
289
|
+
|
|
290
|
+
// ============================================================================
|
|
291
|
+
// Consumer Options
|
|
292
|
+
// ============================================================================
|
|
293
|
+
|
|
294
|
+
/** Default prefetch count (number of unacknowledged messages per consumer) */
|
|
295
|
+
prefetch: 10,
|
|
296
|
+
|
|
297
|
+
// ============================================================================
|
|
298
|
+
// Client Options (Native amqplib options)
|
|
299
|
+
// ============================================================================
|
|
300
|
+
// These options are passed directly to amqplib.connect()
|
|
301
|
+
// for low-level configuration like frame size, TLS, socket options, etc.
|
|
302
|
+
// ============================================================================
|
|
303
|
+
clientOptions: {
|
|
304
|
+
// Frame max size in bytes (0 = no limit)
|
|
305
|
+
// frameMax: 0,
|
|
306
|
+
|
|
307
|
+
// Channel max (0 = unlimited)
|
|
308
|
+
// channelMax: 0,
|
|
309
|
+
|
|
310
|
+
// Socket options
|
|
311
|
+
socket: {
|
|
312
|
+
// Enable TCP keep-alive
|
|
313
|
+
keepAlive: true,
|
|
314
|
+
|
|
315
|
+
// Disable Nagle's algorithm for lower latency
|
|
316
|
+
noDelay: true,
|
|
317
|
+
|
|
318
|
+
// Socket timeout (in addition to heartbeat)
|
|
319
|
+
// timeout: 30000,
|
|
320
|
+
},
|
|
321
|
+
|
|
322
|
+
// TLS/SSL options (uncomment for secure connections)
|
|
323
|
+
// socket: {
|
|
324
|
+
// ca: fs.readFileSync('/path/to/ca.pem'),
|
|
325
|
+
// cert: fs.readFileSync('/path/to/cert.pem'),
|
|
326
|
+
// key: fs.readFileSync('/path/to/key.pem'),
|
|
327
|
+
// rejectUnauthorized: true,
|
|
328
|
+
// },
|
|
329
|
+
},
|
|
330
|
+
};
|
|
331
|
+
|
|
332
|
+
export default heraldConfigurations;
|
|
333
|
+
`;
|
|
334
|
+
const notificationsConfigStub = `import { type NotificationConfig, inApp, mailChannel } from "@warlock.js/notifications";
|
|
335
|
+
import { Notification } from "app/notifications/notification.model";
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* Notifications configuration. Auto-loaded from src/config on boot — the
|
|
339
|
+
* framework's notifications connector reads this default export and hands it to
|
|
340
|
+
* setNotificationConfig, so this file stays declarative (no side-effect call).
|
|
341
|
+
*
|
|
342
|
+
* Each channel is payload-typed, so notify.mail(...) / notify.database(...)
|
|
343
|
+
* and defineNotification are type-checked against the registry.
|
|
344
|
+
*
|
|
345
|
+
* Channels enabled here:
|
|
346
|
+
* - mail wraps @warlock.js/core sendMail; route is notifiable.email.
|
|
347
|
+
* The "from" address defaults to config/mail.ts; override per
|
|
348
|
+
* channel with mailChannel({ from: "no-reply@yourapp.com" }).
|
|
349
|
+
* - database in-app store backed by the Notification model. The "inApp"
|
|
350
|
+
* facade exposes the recipient-scoped read API: listUnread,
|
|
351
|
+
* countUnread, markAsRead, dismiss, ...
|
|
352
|
+
*
|
|
353
|
+
* Async delivery (.queue()) is OPTIONAL: run "npx warlock add herald",
|
|
354
|
+
* import { heraldQueue } from "@warlock.js/notifications", and uncomment the
|
|
355
|
+
* queue line below.
|
|
356
|
+
*/
|
|
357
|
+
const config: NotificationConfig = {
|
|
358
|
+
channels: {
|
|
359
|
+
mail: mailChannel(),
|
|
360
|
+
database: inApp.configure({ model: Notification }),
|
|
361
|
+
},
|
|
362
|
+
|
|
363
|
+
// Async queue — requires @warlock.js/herald (npx warlock add herald):
|
|
364
|
+
// queue: heraldQueue(),
|
|
365
|
+
};
|
|
366
|
+
|
|
367
|
+
export default config;
|
|
368
|
+
`;
|
|
369
|
+
const notificationModelStub = `import { RegisterModel } from "@warlock.js/cascade";
|
|
370
|
+
import { DatabaseNotification, type NotificationColumnMap } from "@warlock.js/notifications";
|
|
371
|
+
import { v } from "@warlock.js/seal";
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Validation schema for the notifications table — mirrors the migration
|
|
375
|
+
* columns (snake_case). Cascade validates + casts every write against it:
|
|
376
|
+
* nullable columns use .nullish() (may be absent or null), and payload is
|
|
377
|
+
* free-form JSON. Keep this in sync with the migration + columnMap when you
|
|
378
|
+
* add or rename columns.
|
|
379
|
+
*/
|
|
380
|
+
const notificationSchema = v.object({
|
|
381
|
+
user_id: v.string(),
|
|
382
|
+
type: v.string(),
|
|
383
|
+
title: v.string(),
|
|
384
|
+
body: v.string().nullish(),
|
|
385
|
+
payload: v.record(v.any()).nullish(),
|
|
386
|
+
read_at: v.date().nullish(),
|
|
387
|
+
idempotency_key: v.string().nullish(),
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* In-app notification model.
|
|
392
|
+
*
|
|
393
|
+
* Extends the package's DatabaseNotification base, which provides the stable
|
|
394
|
+
* accessors (recipientId, tenantId, isRead, readAt, markRead) — all derived
|
|
395
|
+
* from the columnMap below. The read/write API lives on the inApp facade
|
|
396
|
+
* (configured in config/notifications.ts); you rarely touch this class directly.
|
|
397
|
+
*/
|
|
398
|
+
@RegisterModel()
|
|
399
|
+
export class Notification extends DatabaseNotification {
|
|
400
|
+
public static table = "notifications";
|
|
401
|
+
public static schema = notificationSchema;
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Maps the in-app store's roles to your columns. This default is
|
|
405
|
+
* single-tenant + read_at-only. Add tenant: "organization_id" for
|
|
406
|
+
* multi-tenant; use isRead: "is_read" (instead of, or alongside, readAt) to
|
|
407
|
+
* track a boolean read flag. The migration + accessors all follow this map.
|
|
408
|
+
*/
|
|
409
|
+
public static columnMap: NotificationColumnMap = { readAt: "read_at" };
|
|
410
|
+
}
|
|
411
|
+
`;
|
|
412
|
+
const notificationMigrationStub = `import { Migration } from "@warlock.js/cascade";
|
|
413
|
+
import { notificationColumns } from "@warlock.js/notifications";
|
|
414
|
+
import { Notification } from "../notification.model";
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Notifications table.
|
|
418
|
+
*
|
|
419
|
+
* Columns come from notificationColumns(Notification) — the recipient / tenant
|
|
420
|
+
* / read-state names follow the model's columnMap; type / title / body /
|
|
421
|
+
* payload / idempotency_key are fixed. Spread it to add your own columns
|
|
422
|
+
* (remember to mirror them in the model schema):
|
|
423
|
+
*
|
|
424
|
+
* import { uuid } from "@warlock.js/cascade";
|
|
425
|
+
*
|
|
426
|
+
* export default Migration.create(Notification, {
|
|
427
|
+
* ...notificationColumns(Notification),
|
|
428
|
+
* // category_id: uuid().index().nullable(),
|
|
429
|
+
* });
|
|
430
|
+
*/
|
|
431
|
+
export default Migration.create(Notification, notificationColumns(Notification));
|
|
432
|
+
`;
|
|
433
|
+
|
|
434
|
+
//#endregion
|
|
435
|
+
export { accessConfigStub, accessResolverStub, accessRoleMigrationStub, accessRoleModelIndexStub, accessRoleModelStub, accessUserRoleMigrationStub, accessUserRoleModelIndexStub, accessUserRoleModelStub, communicatorsConfigStub, notificationMigrationStub, notificationModelStub, notificationsConfigStub, socketConfigStub };
|
|
436
|
+
//# sourceMappingURL=stubs.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stubs.mjs","names":[],"sources":["../../../../../../../@warlock.js/core/src/generations/stubs.ts"],"sourcesContent":["export const accessConfigStub = `import { type AccessConfigurations } from \"@warlock.js/access\";\r\nimport { DatabaseAccessResolver } from \"app/access/services/access-resolver\";\r\n\r\n/**\r\n * Authorization configuration — read by @warlock.js/access on boot.\r\n *\r\n * The resolver is the one required piece: it tells the engine how to read a\r\n * user's roles + permissions. The ejected DatabaseAccessResolver reads roles\r\n * from the user_roles table and maps them through the roles catalog table (so\r\n * roles + their permissions are managed at runtime, in the DB).\r\n *\r\n * For a fixed, code-defined catalog with no tables, swap in DefaultAccessResolver:\r\n * import { DefaultAccessResolver } from \"@warlock.js/access\";\r\n * resolver: new DefaultAccessResolver({ admin: [\"*\"], editor: [\"orders.*\"] }),\r\n *\r\n * Multi-tenant? Add a \\`resolveTenant()\\` to the resolver to read the active\r\n * tenant from the request; checks then scope to it automatically.\r\n */\r\nconst access: AccessConfigurations = {\r\n resolver: new DatabaseAccessResolver(),\r\n\r\n // Cache resolved permission sets (default \"10m\").\r\n // cache: { ttl: \"10m\" },\r\n};\r\n\r\nexport default access;\r\n`;\r\n\r\nexport const accessRoleModelStub = `import { Model, RegisterModel } from \"@warlock.js/cascade\";\r\nimport { type Infer, v } from \"@warlock.js/seal\";\r\n\r\n/**\r\n * Validation schema for the roles catalog — mirrors the migration columns\r\n * (snake_case). Each row is a role name plus the permission strings it grants;\r\n * wildcards work (\"orders.*\", \"*\"). The DatabaseAccessResolver maps a user's\r\n * assigned role names through this table to their effective permissions.\r\n */\r\nexport const roleSchema = v.object({\r\n name: v.string(),\r\n permissions: v.array(v.string()).default([]),\r\n});\r\n\r\nexport type RoleSchema = Infer<typeof roleSchema>;\r\n\r\n/**\r\n * The roles catalog — role name → the permissions it grants. Managed at runtime\r\n * (admins add roles + edit their permissions), unlike a fixed code map. Read by\r\n * DatabaseAccessResolver.resolvePermissions to expand a user's roles to permissions.\r\n */\r\n@RegisterModel()\r\nexport class Role extends Model<RoleSchema> {\r\n public static table = \"roles\";\r\n\r\n public static schema = roleSchema;\r\n\r\n /** The permission strings this role grants. */\r\n public get permissions(): string[] {\r\n return this.get<string[]>(\"permissions\", []);\r\n }\r\n}\r\n`;\r\n\r\nexport const accessRoleModelIndexStub = `export * from \"./role.model\";\r\n`;\r\n\r\nexport const accessRoleMigrationStub = `import { arrayText, Migration, text } from \"@warlock.js/cascade\";\r\nimport { Role } from \"../role.model\";\r\n\r\n/**\r\n * Roles catalog table. \\`name\\` is unique (one row per role); \\`permissions\\` is a\r\n * text array of the permission strings the role grants.\r\n */\r\nexport default Migration.create(Role, {\r\n name: text().notNullable().unique(),\r\n permissions: arrayText().nullable(),\r\n});\r\n`;\r\n\r\nexport const accessUserRoleModelStub = `import { access } from \"@warlock.js/access\";\r\nimport type { Auth } from \"@warlock.js/auth\";\r\nimport { Model, RegisterModel } from \"@warlock.js/cascade\";\r\nimport { type Infer, v } from \"@warlock.js/seal\";\r\n\r\n/**\r\n * Validation schema for a role assignment — mirrors the migration columns\r\n * (snake_case). \\`tenant\\` is nullable: a null tenant is a GLOBAL assignment.\r\n */\r\nexport const userRoleSchema = v.object({\r\n user_id: v.string(),\r\n user_type: v.string(),\r\n role: v.string(),\r\n tenant: v.string().optional(),\r\n});\r\n\r\nexport type UserRoleSchema = Infer<typeof userRoleSchema>;\r\n\r\n/**\r\n * The role-assignment table — which roles a user holds, optionally per tenant.\r\n * Read by DatabaseAccessResolver.resolveRoles; mutated via the statics below.\r\n * \\`assign\\` / \\`revoke\\` flush the cached permission set automatically, so callers\r\n * never need to call \\`access.flush(user, tenant)\\` themselves.\r\n */\r\n@RegisterModel()\r\nexport class UserRole extends Model<UserRoleSchema> {\r\n public static table = \"user_roles\";\r\n\r\n public static schema = userRoleSchema;\r\n\r\n /**\r\n * Role names assigned to the user in the given tenant.\r\n *\r\n * An unresolved tenant (\\`undefined\\`) scopes to GLOBAL roles only — the rows\r\n * stored with no tenant (\\`null\\`) — never the union across every tenant. The\r\n * union would be a privilege-escalation: a user who is \\`owner\\` in one tenant\r\n * must not be treated as \\`owner\\` everywhere just because a check didn't carry\r\n * a tenant. This mirrors how \\`assign(user, role)\\` stores a global row.\r\n */\r\n public static async rolesFor(user: Auth, tenant?: string): Promise<string[]> {\r\n const rows = await this.query()\r\n .where({\r\n user_id: user.id,\r\n user_type: user.userType,\r\n tenant: tenant ?? null,\r\n })\r\n .get();\r\n\r\n // De-dupe so a duplicate row (a concurrent assign that slipped past the\r\n // existence check) can't distort the resolved set.\r\n return [...new Set(rows.map((row) => row.get(\"role\") as string))];\r\n }\r\n\r\n /**\r\n * Assign a role to the user. No-op if the assignment already exists.\r\n * Flushes the user's cached permission set automatically.\r\n */\r\n public static async assign(user: Auth, role: string, tenant?: string): Promise<void> {\r\n const existing = await this.first({\r\n user_id: user.id,\r\n user_type: user.userType,\r\n role,\r\n tenant: tenant ?? null,\r\n });\r\n\r\n if (existing) return;\r\n\r\n await this.create({\r\n user_id: user.id,\r\n user_type: user.userType,\r\n role,\r\n tenant,\r\n });\r\n\r\n await access.flush(user, tenant);\r\n }\r\n\r\n /**\r\n * Remove a role assignment from the user.\r\n * Flushes the user's cached permission set automatically.\r\n */\r\n public static async revoke(user: Auth, role: string, tenant?: string): Promise<void> {\r\n await this.delete({\r\n user_id: user.id,\r\n user_type: user.userType,\r\n role,\r\n tenant: tenant ?? null,\r\n });\r\n\r\n await access.flush(user, tenant);\r\n }\r\n}\r\n`;\r\n\r\nexport const accessUserRoleModelIndexStub = `export * from \"./user-role.model\";\r\n`;\r\n\r\nexport const accessUserRoleMigrationStub = `import { Migration, text, uuid } from \"@warlock.js/cascade\";\r\nimport { UserRole } from \"../user-role.model\";\r\n\r\n/**\r\n * Role-assignment table. \\`user_id\\` is a UUID — override this migration if your\r\n * user ids are integers. The composite index powers the per-user (per-tenant)\r\n * lookup the resolver runs on every check.\r\n */\r\nexport default Migration.create(\r\n UserRole,\r\n {\r\n user_id: uuid().notNullable().index(),\r\n user_type: text().notNullable(),\r\n role: text().notNullable().index(),\r\n tenant: text().nullable().index(),\r\n },\r\n {\r\n index: [{ columns: [\"user_id\", \"user_type\", \"tenant\"] }],\r\n },\r\n);\r\n`;\r\n\r\nexport const accessResolverStub = `import type { AccessResolver } from \"@warlock.js/access\";\r\nimport type { Auth } from \"@warlock.js/auth\";\r\nimport { Role } from \"app/access/models/role\";\r\nimport { UserRole } from \"app/access/models/user-role\";\r\n\r\n/**\r\n * The app's access adapter — connects @warlock.js/access to the ejected role\r\n * tables. Roles come from the user_roles assignment table; permissions are\r\n * expanded by mapping those role names through the roles catalog table. Both\r\n * are managed at runtime (in the DB), so admins can add roles + edit their\r\n * permissions without a deploy.\r\n *\r\n * The engine owns the hard parts (wildcard matching, caching, fail-closed); this\r\n * resolver only fetches — keep it dumb, never cache inside it.\r\n */\r\nexport class DatabaseAccessResolver implements AccessResolver {\r\n /** The role names this user holds (powers \\`hasRole\\` / \\`hasAnyRole\\`). */\r\n public async resolveRoles(user: Auth, tenant?: string): Promise<string[]> {\r\n return UserRole.rolesFor(user, tenant);\r\n }\r\n\r\n /** The effective permission strings this user has (powers \\`can\\` / \\`authorize\\`). */\r\n public async resolvePermissions(user: Auth, tenant?: string): Promise<string[]> {\r\n const names = await this.resolveRoles(user, tenant);\r\n\r\n if (names.length === 0) return [];\r\n\r\n const roles = await Role.query().whereIn(\"name\", names).get();\r\n\r\n // Flatten + de-dupe so two roles granting the same permission yield one entry.\r\n return [...new Set(roles.flatMap((role) => role.permissions))];\r\n }\r\n\r\n /**\r\n * Optional. Resolve the ambient tenant when a check doesn't pass one\r\n * explicitly — derive it from the authenticated user (safer than reading\r\n * client request input, which a caller could spoof). Uncomment + adapt for a\r\n * multi-tenant app (single-tenant apps leave this off and return undefined).\r\n */\r\n // public resolveTenant(user: Auth): string | undefined {\r\n // return user.get(\"organization_id\");\r\n // }\r\n}\r\n`;\r\n\r\nexport const socketConfigStub = `import type { SocketOptions } from \"@warlock.js/core\";\r\n\r\n/**\r\n * Socket.IO configuration — read by the framework's socket connector\r\n * on boot. When the HTTP server is running the socket server attaches\r\n * to it; otherwise it listens on its own configured port.\r\n *\r\n * Remove this file to disable the socket server entirely.\r\n */\r\nexport default {\r\n options: {\r\n cors: {\r\n origin: \"*\",\r\n },\r\n },\r\n} as SocketOptions;\r\n`;\r\n\r\nexport const communicatorsConfigStub = `import { env } from \"@mongez/dotenv\";\r\nimport type { BrokerConfigurations, RabbitMQClientOptions } from \"@warlock.js/herald\";\r\n\r\nconst heraldConfigurations: BrokerConfigurations<RabbitMQClientOptions> = {\r\n driver: \"rabbitmq\",\r\n name: \"default\",\r\n isDefault: true,\r\n\r\n // ============================================================================\r\n // Connection Settings\r\n // ============================================================================\r\n\r\n host: env(\"RABBITMQ_HOST\", \"localhost\"),\r\n port: env(\"RABBITMQ_PORT\", 5672),\r\n username: env(\"RABBITMQ_USERNAME\", \"guest\"),\r\n password: env(\"RABBITMQ_PASSWORD\", \"guest\"),\r\n vhost: env(\"RABBITMQ_VHOST\", \"/\"),\r\n\r\n // Or use connection URI (takes precedence over host/port)\r\n // uri: env(\"RABBITMQ_URL\"),\r\n\r\n // ============================================================================\r\n // Connection Options\r\n // ============================================================================\r\n\r\n /** Heartbeat interval in seconds */\r\n heartbeat: 60,\r\n\r\n /** Connection timeout in milliseconds */\r\n connectionTimeout: 10000,\r\n\r\n /** Enable automatic reconnection on disconnect */\r\n reconnect: true,\r\n\r\n /** Delay between reconnection attempts in milliseconds */\r\n reconnectDelay: 5_000,\r\n\r\n // ============================================================================\r\n // Consumer Options\r\n // ============================================================================\r\n\r\n /** Default prefetch count (number of unacknowledged messages per consumer) */\r\n prefetch: 10,\r\n\r\n // ============================================================================\r\n // Client Options (Native amqplib options)\r\n // ============================================================================\r\n // These options are passed directly to amqplib.connect()\r\n // for low-level configuration like frame size, TLS, socket options, etc.\r\n // ============================================================================\r\n clientOptions: {\r\n // Frame max size in bytes (0 = no limit)\r\n // frameMax: 0,\r\n\r\n // Channel max (0 = unlimited)\r\n // channelMax: 0,\r\n\r\n // Socket options\r\n socket: {\r\n // Enable TCP keep-alive\r\n keepAlive: true,\r\n\r\n // Disable Nagle's algorithm for lower latency\r\n noDelay: true,\r\n\r\n // Socket timeout (in addition to heartbeat)\r\n // timeout: 30000,\r\n },\r\n\r\n // TLS/SSL options (uncomment for secure connections)\r\n // socket: {\r\n // ca: fs.readFileSync('/path/to/ca.pem'),\r\n // cert: fs.readFileSync('/path/to/cert.pem'),\r\n // key: fs.readFileSync('/path/to/key.pem'),\r\n // rejectUnauthorized: true,\r\n // },\r\n },\r\n};\r\n\r\nexport default heraldConfigurations;\r\n`;\r\n\r\nexport const notificationsConfigStub = `import { type NotificationConfig, inApp, mailChannel } from \"@warlock.js/notifications\";\r\nimport { Notification } from \"app/notifications/notification.model\";\r\n\r\n/**\r\n * Notifications configuration. Auto-loaded from src/config on boot — the\r\n * framework's notifications connector reads this default export and hands it to\r\n * setNotificationConfig, so this file stays declarative (no side-effect call).\r\n *\r\n * Each channel is payload-typed, so notify.mail(...) / notify.database(...)\r\n * and defineNotification are type-checked against the registry.\r\n *\r\n * Channels enabled here:\r\n * - mail wraps @warlock.js/core sendMail; route is notifiable.email.\r\n * The \"from\" address defaults to config/mail.ts; override per\r\n * channel with mailChannel({ from: \"no-reply@yourapp.com\" }).\r\n * - database in-app store backed by the Notification model. The \"inApp\"\r\n * facade exposes the recipient-scoped read API: listUnread,\r\n * countUnread, markAsRead, dismiss, ...\r\n *\r\n * Async delivery (.queue()) is OPTIONAL: run \"npx warlock add herald\",\r\n * import { heraldQueue } from \"@warlock.js/notifications\", and uncomment the\r\n * queue line below.\r\n */\r\nconst config: NotificationConfig = {\r\n channels: {\r\n mail: mailChannel(),\r\n database: inApp.configure({ model: Notification }),\r\n },\r\n\r\n // Async queue — requires @warlock.js/herald (npx warlock add herald):\r\n // queue: heraldQueue(),\r\n};\r\n\r\nexport default config;\r\n`;\r\n\r\nexport const notificationModelStub = `import { RegisterModel } from \"@warlock.js/cascade\";\r\nimport { DatabaseNotification, type NotificationColumnMap } from \"@warlock.js/notifications\";\r\nimport { v } from \"@warlock.js/seal\";\r\n\r\n/**\r\n * Validation schema for the notifications table — mirrors the migration\r\n * columns (snake_case). Cascade validates + casts every write against it:\r\n * nullable columns use .nullish() (may be absent or null), and payload is\r\n * free-form JSON. Keep this in sync with the migration + columnMap when you\r\n * add or rename columns.\r\n */\r\nconst notificationSchema = v.object({\r\n user_id: v.string(),\r\n type: v.string(),\r\n title: v.string(),\r\n body: v.string().nullish(),\r\n payload: v.record(v.any()).nullish(),\r\n read_at: v.date().nullish(),\r\n idempotency_key: v.string().nullish(),\r\n});\r\n\r\n/**\r\n * In-app notification model.\r\n *\r\n * Extends the package's DatabaseNotification base, which provides the stable\r\n * accessors (recipientId, tenantId, isRead, readAt, markRead) — all derived\r\n * from the columnMap below. The read/write API lives on the inApp facade\r\n * (configured in config/notifications.ts); you rarely touch this class directly.\r\n */\r\n@RegisterModel()\r\nexport class Notification extends DatabaseNotification {\r\n public static table = \"notifications\";\r\n public static schema = notificationSchema;\r\n\r\n /**\r\n * Maps the in-app store's roles to your columns. This default is\r\n * single-tenant + read_at-only. Add tenant: \"organization_id\" for\r\n * multi-tenant; use isRead: \"is_read\" (instead of, or alongside, readAt) to\r\n * track a boolean read flag. The migration + accessors all follow this map.\r\n */\r\n public static columnMap: NotificationColumnMap = { readAt: \"read_at\" };\r\n}\r\n`;\r\n\r\nexport const notificationMigrationStub = `import { Migration } from \"@warlock.js/cascade\";\r\nimport { notificationColumns } from \"@warlock.js/notifications\";\r\nimport { Notification } from \"../notification.model\";\r\n\r\n/**\r\n * Notifications table.\r\n *\r\n * Columns come from notificationColumns(Notification) — the recipient / tenant\r\n * / read-state names follow the model's columnMap; type / title / body /\r\n * payload / idempotency_key are fixed. Spread it to add your own columns\r\n * (remember to mirror them in the model schema):\r\n *\r\n * import { uuid } from \"@warlock.js/cascade\";\r\n *\r\n * export default Migration.create(Notification, {\r\n * ...notificationColumns(Notification),\r\n * // category_id: uuid().index().nullable(),\r\n * });\r\n */\r\nexport default Migration.create(Notification, notificationColumns(Notification));\r\n`;\r\n"],"mappings":";AAAA,MAAa,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BhC,MAAa,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCnC,MAAa,2BAA2B;;AAGxC,MAAa,0BAA0B;;;;;;;;;;;;AAavC,MAAa,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8FvC,MAAa,+BAA+B;;AAG5C,MAAa,8BAA8B;;;;;;;;;;;;;;;;;;;;;AAsB3C,MAAa,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6ClC,MAAa,mBAAmB;;;;;;;;;;;;;;;;;AAkBhC,MAAa,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkFvC,MAAa,0BAA0B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAoCvC,MAAa,wBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4CrC,MAAa,4BAA4B"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { warlockPath } from "../utils/paths.mjs";
|
|
2
|
+
import "../utils/index.mjs";
|
|
3
|
+
import { getJsonFileAsync, putJsonFileAsync, unlinkAsync } from "@warlock.js/fs";
|
|
4
|
+
|
|
5
|
+
//#region ../@warlock.js/core/src/manifest/manifest-manager.ts
|
|
6
|
+
var ManifestManager = class {
|
|
7
|
+
constructor() {
|
|
8
|
+
this._hasChanges = false;
|
|
9
|
+
this._isLoaded = false;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Get if commands json has changes
|
|
13
|
+
*/
|
|
14
|
+
get hasChanges() {
|
|
15
|
+
return this._hasChanges;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Check if commands json is loaded
|
|
19
|
+
*/
|
|
20
|
+
get isCommandLoaded() {
|
|
21
|
+
return this._isLoaded;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Load commands.json file
|
|
25
|
+
*/
|
|
26
|
+
async loadCommands() {
|
|
27
|
+
if (this._isLoaded) return this._commandsJson;
|
|
28
|
+
try {
|
|
29
|
+
this._commandsJson = await getJsonFileAsync(warlockPath("commands.json"));
|
|
30
|
+
this._isLoaded = true;
|
|
31
|
+
return this._commandsJson;
|
|
32
|
+
} catch {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Get commands json content
|
|
38
|
+
*/
|
|
39
|
+
get commandsJson() {
|
|
40
|
+
return this._commandsJson;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Save commands in commands.json file
|
|
44
|
+
*/
|
|
45
|
+
async saveCommands() {
|
|
46
|
+
await putJsonFileAsync(warlockPath("commands.json"), this._commandsJson);
|
|
47
|
+
this._isLoaded = true;
|
|
48
|
+
this._hasChanges = false;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Add command info to commands list (But do not save commands)
|
|
52
|
+
*/
|
|
53
|
+
addCommandToList(name, command) {
|
|
54
|
+
if (!this._commandsJson) this._commandsJson = { commands: {} };
|
|
55
|
+
this._commandsJson.commands[name] = command;
|
|
56
|
+
this._hasChanges = true;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Clear commands cache
|
|
60
|
+
*/
|
|
61
|
+
clearCommandsCache() {
|
|
62
|
+
this._commandsJson = void 0;
|
|
63
|
+
this._hasChanges = false;
|
|
64
|
+
this._isLoaded = false;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Remove commands.json file
|
|
68
|
+
*/
|
|
69
|
+
async removeCommandsFile() {
|
|
70
|
+
try {
|
|
71
|
+
await unlinkAsync(warlockPath("commands.json"));
|
|
72
|
+
} catch {}
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
const manifestManager = new ManifestManager();
|
|
76
|
+
|
|
77
|
+
//#endregion
|
|
78
|
+
export { manifestManager };
|
|
79
|
+
//# sourceMappingURL=manifest-manager.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest-manager.mjs","names":[],"sources":["../../../../../../../@warlock.js/core/src/manifest/manifest-manager.ts"],"sourcesContent":["import { getJsonFileAsync, putJsonFileAsync, unlinkAsync } from \"@warlock.js/fs\";\r\nimport { warlockPath } from \"../utils\";\r\n\r\n/**\r\n * Stored option metadata in manifest\r\n */\r\nexport type ManifestCommandOption = {\r\n name: string;\r\n text: string;\r\n alias?: string;\r\n description?: string;\r\n type?: \"string\" | \"boolean\" | \"number\";\r\n required?: boolean;\r\n defaultValue?: string | boolean | number;\r\n};\r\n\r\n/**\r\n * Command metadata stored in commands.json\r\n */\r\nexport type ManifestCommandData = {\r\n relativePath?: string;\r\n source: \"framework\" | \"plugin\" | \"project\";\r\n description?: string;\r\n alias?: string;\r\n options?: ManifestCommandOption[];\r\n};\r\n\r\ntype ManifestCommandsJson = {\r\n commands: Record<string, ManifestCommandData>;\r\n};\r\n\r\nexport class ManifestManager {\r\n /**\r\n * commands json\r\n */\r\n protected _commandsJson?: ManifestCommandsJson;\r\n\r\n /**\r\n * Check if commands json is changed\r\n */\r\n protected _hasChanges = false;\r\n\r\n /**\r\n * Whether commands json is loaded\r\n */\r\n protected _isLoaded = false;\r\n\r\n /**\r\n * Get if commands json has changes\r\n */\r\n public get hasChanges() {\r\n return this._hasChanges;\r\n }\r\n\r\n /**\r\n * Check if commands json is loaded\r\n */\r\n public get isCommandLoaded() {\r\n return this._isLoaded;\r\n }\r\n\r\n /**\r\n * Load commands.json file\r\n */\r\n public async loadCommands(): Promise<ManifestCommandsJson | undefined> {\r\n // now try to locate it in .warlock folder\r\n if (this._isLoaded) return this._commandsJson;\r\n\r\n try {\r\n this._commandsJson = await getJsonFileAsync(warlockPath(\"commands.json\"));\r\n\r\n this._isLoaded = true;\r\n\r\n return this._commandsJson;\r\n } catch {\r\n return;\r\n }\r\n }\r\n\r\n /**\r\n * Get commands json content\r\n */\r\n public get commandsJson() {\r\n return this._commandsJson;\r\n }\r\n\r\n /**\r\n * Save commands in commands.json file\r\n */\r\n public async saveCommands() {\r\n await putJsonFileAsync(warlockPath(\"commands.json\"), this._commandsJson);\r\n\r\n this._isLoaded = true;\r\n this._hasChanges = false;\r\n }\r\n\r\n /**\r\n * Add command info to commands list (But do not save commands)\r\n */\r\n public addCommandToList(name: string, command: ManifestCommandsJson[\"commands\"][string]) {\r\n if (!this._commandsJson) {\r\n this._commandsJson = {\r\n commands: {},\r\n };\r\n }\r\n\r\n this._commandsJson.commands[name] = command;\r\n\r\n this._hasChanges = true;\r\n }\r\n\r\n /**\r\n * Clear commands cache\r\n */\r\n public clearCommandsCache() {\r\n this._commandsJson = undefined;\r\n this._hasChanges = false;\r\n this._isLoaded = false;\r\n }\r\n\r\n /**\r\n * Remove commands.json file\r\n */\r\n public async removeCommandsFile() {\r\n try {\r\n await unlinkAsync(warlockPath(\"commands.json\"));\r\n } catch {\r\n // do nothing\r\n }\r\n }\r\n}\r\n\r\nexport const manifestManager = new ManifestManager();\r\n"],"mappings":";;;;;AA+BA,IAAa,kBAAb,MAA6B;;qBASH;mBAKF;;;;;CAKtB,IAAW,aAAa;EACtB,OAAO,KAAK;CACd;;;;CAKA,IAAW,kBAAkB;EAC3B,OAAO,KAAK;CACd;;;;CAKA,MAAa,eAA0D;EAErE,IAAI,KAAK,WAAW,OAAO,KAAK;EAEhC,IAAI;GACF,KAAK,gBAAgB,MAAM,iBAAiB,YAAY,eAAe,CAAC;GAExE,KAAK,YAAY;GAEjB,OAAO,KAAK;EACd,QAAQ;GACN;EACF;CACF;;;;CAKA,IAAW,eAAe;EACxB,OAAO,KAAK;CACd;;;;CAKA,MAAa,eAAe;EAC1B,MAAM,iBAAiB,YAAY,eAAe,GAAG,KAAK,aAAa;EAEvE,KAAK,YAAY;EACjB,KAAK,cAAc;CACrB;;;;CAKA,AAAO,iBAAiB,MAAc,SAAmD;EACvF,IAAI,CAAC,KAAK,eACR,KAAK,gBAAgB,EACnB,UAAU,CAAC,EACb;EAGF,KAAK,cAAc,SAAS,QAAQ;EAEpC,KAAK,cAAc;CACrB;;;;CAKA,AAAO,qBAAqB;EAC1B,KAAK,gBAAgB;EACrB,KAAK,cAAc;EACnB,KAAK,YAAY;CACnB;;;;CAKA,MAAa,qBAAqB;EAChC,IAAI;GACF,MAAM,YAAY,YAAY,eAAe,CAAC;EAChD,QAAQ,CAER;CACF;AACF;AAEA,MAAa,kBAAkB,IAAI,gBAAgB"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { ProductionBuilder } from "./production-builder.mjs";
|
|
2
|
+
|
|
3
|
+
//#region ../@warlock.js/core/src/production/build-app-production.ts
|
|
4
|
+
/**
|
|
5
|
+
* Build the application for production
|
|
6
|
+
* Options are loaded from warlock.config.ts
|
|
7
|
+
*/
|
|
8
|
+
async function buildAppProduction() {
|
|
9
|
+
await new ProductionBuilder().build();
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
//#endregion
|
|
13
|
+
export { buildAppProduction };
|
|
14
|
+
//# sourceMappingURL=build-app-production.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"build-app-production.mjs","names":[],"sources":["../../../../../../../@warlock.js/core/src/production/build-app-production.ts"],"sourcesContent":["import { ProductionBuilder } from \"./production-builder\";\n\n/**\n * Build the application for production\n * Options are loaded from warlock.config.ts\n */\nexport async function buildAppProduction() {\n const builder = new ProductionBuilder();\n await builder.build();\n}\n"],"mappings":";;;;;;;AAMA,eAAsB,qBAAqB;CAEzC,MAAM,IADc,kBACR,CAAC,CAAC,MAAM;AACtB"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { __require } from "../_virtual/_rolldown/runtime.mjs";
|
|
2
|
+
|
|
3
|
+
//#region ../@warlock.js/core/src/production/esbuild-plugins.ts
|
|
4
|
+
const nativeNodeModulesPlugin = {
|
|
5
|
+
name: "native-node-modules",
|
|
6
|
+
setup(build) {
|
|
7
|
+
build.onResolve({
|
|
8
|
+
filter: /\.node$/,
|
|
9
|
+
namespace: "file"
|
|
10
|
+
}, (args) => ({
|
|
11
|
+
path: __require.resolve(args.path, { paths: [args.resolveDir] }),
|
|
12
|
+
namespace: "node-file"
|
|
13
|
+
}));
|
|
14
|
+
build.onLoad({
|
|
15
|
+
filter: /.*/,
|
|
16
|
+
namespace: "node-file"
|
|
17
|
+
}, (args) => ({ contents: `
|
|
18
|
+
import path from ${JSON.stringify(args.path)}
|
|
19
|
+
try { module.exports = require(path) }
|
|
20
|
+
catch {}
|
|
21
|
+
` }));
|
|
22
|
+
build.onResolve({
|
|
23
|
+
filter: /\.node$/,
|
|
24
|
+
namespace: "node-file"
|
|
25
|
+
}, (args) => ({
|
|
26
|
+
path: args.path,
|
|
27
|
+
namespace: "file"
|
|
28
|
+
}));
|
|
29
|
+
const opts = build.initialOptions;
|
|
30
|
+
opts.loader = opts.loader || {};
|
|
31
|
+
opts.loader[".node"] = "file";
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
//#endregion
|
|
36
|
+
export { nativeNodeModulesPlugin };
|
|
37
|
+
//# sourceMappingURL=esbuild-plugins.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"esbuild-plugins.mjs","names":[],"sources":["../../../../../../../@warlock.js/core/src/production/esbuild-plugins.ts"],"sourcesContent":["import type { OnLoadArgs, OnResolveArgs, PluginBuild } from \"esbuild\";\n\nexport const nativeNodeModulesPlugin = {\n name: \"native-node-modules\",\n setup(build: PluginBuild) {\n build.onResolve({ filter: /\\.node$/, namespace: \"file\" }, (args: OnResolveArgs) => ({\n path: require.resolve(args.path, { paths: [args.resolveDir] }),\n namespace: \"node-file\",\n }));\n\n build.onLoad({ filter: /.*/, namespace: \"node-file\" }, (args: OnLoadArgs) => ({\n contents: `\n import path from ${JSON.stringify(args.path)}\n try { module.exports = require(path) }\n catch {}\n `,\n }));\n\n build.onResolve({ filter: /\\.node$/, namespace: \"node-file\" }, (args: OnResolveArgs) => ({\n path: args.path,\n namespace: \"file\",\n }));\n\n const opts = build.initialOptions;\n opts.loader = opts.loader || {};\n opts.loader[\".node\"] = \"file\";\n },\n};\n"],"mappings":";;;AAEA,MAAa,0BAA0B;CACrC,MAAM;CACN,MAAM,OAAoB;EACxB,MAAM,UAAU;GAAE,QAAQ;GAAW,WAAW;EAAO,IAAI,UAAyB;GAClF,gBAAc,QAAQ,KAAK,MAAM,EAAE,OAAO,CAAC,KAAK,UAAU,EAAE,CAAC;GAC7D,WAAW;EACb,EAAE;EAEF,MAAM,OAAO;GAAE,QAAQ;GAAM,WAAW;EAAY,IAAI,UAAsB,EAC5E,UAAU;6BACa,KAAK,UAAU,KAAK,IAAI,EAAE;;;UAInD,EAAE;EAEF,MAAM,UAAU;GAAE,QAAQ;GAAW,WAAW;EAAY,IAAI,UAAyB;GACvF,MAAM,KAAK;GACX,WAAW;EACb,EAAE;EAEF,MAAM,OAAO,MAAM;EACnB,KAAK,SAAS,KAAK,UAAU,CAAC;EAC9B,KAAK,OAAO,WAAW;CACzB;AACF"}
|