@arkstack/common 0.13.2 → 0.14.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/dist/index.d.ts CHANGED
@@ -236,6 +236,38 @@ type HookFor<N extends string> = N extends keyof HookRegistry ? HookRegistry[N]
236
236
  type HookPos<N extends string, P extends string> = N extends keyof HookRegistry ? P extends keyof HookRegistry[N] ? HookRegistry[N][P] : (...args: any[]) => void : (...args: any[]) => void;
237
237
  type HookPositions<N extends string> = N extends keyof HookRegistry ? keyof HookRegistry[N] : Position;
238
238
  type HookArgs<N extends string, P extends string> = N extends keyof HookRegistry ? P extends keyof HookRegistry[N] ? HookRegistry[N][P] extends ((...args: infer A) => any) ? A : any[] : any[] : any[];
239
+ /**
240
+ * A single source → destination mapping a package wants to publish into the
241
+ * consuming application.
242
+ */
243
+ interface PublishEntry {
244
+ /** Absolute path to the file or directory shipped by the package. */
245
+ from: string;
246
+ /**
247
+ * Destination path, relative to the application root, where the artifact is
248
+ * written when published.
249
+ */
250
+ to: string;
251
+ }
252
+ /**
253
+ * A group of publishable artifacts registered by a package.
254
+ */
255
+ interface PublishGroup {
256
+ /** The package registering the artifacts, e.g. `@arkstack/cache`. */
257
+ package: string;
258
+ /**
259
+ * Optional tag for selective publishing (`ark publish --tag <tag>`). A
260
+ * package may register several groups under different tags.
261
+ */
262
+ tag?: string;
263
+ /** The files/directories to publish. */
264
+ entries: PublishEntry[];
265
+ }
266
+ /** Optional filter applied when reading the registry. */
267
+ interface PublishFilter {
268
+ package?: string;
269
+ tag?: string;
270
+ }
239
271
  //#endregion
240
272
  //#region src/system.d.ts
241
273
  /**
@@ -262,6 +294,23 @@ declare const CONFIG_KEY: unique symbol;
262
294
  * @returns The configuration value.
263
295
  */
264
296
  declare const config: GlobalConfig;
297
+ /**
298
+ * Resolve the unified application key.
299
+ *
300
+ * `APP_KEY` (exposed as `config('app.key')`) is the single secret used for
301
+ * signing and encryption across the framework. Resolution order:
302
+ *
303
+ * 1. An explicit `APP_KEY` environment variable.
304
+ * 2. Any legacy environment variable(s) passed in, for backward compatibility
305
+ * with apps that predate `APP_KEY` (e.g. `JWT_SECRET`,
306
+ * `TWO_FACTOR_ENCRYPTION_KEY`).
307
+ * 3. `config('app.key')` — the value from `src/config/app.ts`, which may itself
308
+ * be a placeholder default when no config is loaded.
309
+ *
310
+ * @param legacy Legacy env var name(s) to fall back to.
311
+ * @returns The resolved key, or `undefined` when none is configured.
312
+ */
313
+ declare const appKey: (legacy?: string | string[]) => string | undefined;
265
314
  /**
266
315
  * Gets the current Node environment (development or production).
267
316
  *
@@ -403,6 +452,45 @@ declare const shouldLogError: typeof ErrorHandler.shouldLogError;
403
452
  declare const createErrorPayload: typeof ErrorHandler.createErrorPayload;
404
453
  declare const logUnhandledError: typeof ErrorHandler.logUnhandledError;
405
454
  //#endregion
455
+ //#region src/Publisher.d.ts
456
+ /**
457
+ * Registry of artifacts packages want to publish into the consuming application.
458
+ *
459
+ * Packages call {@link Publisher.publishes} from their `setup` module so that
460
+ * `ark publish` can copy the artifacts (migrations, config stubs, assets, …)
461
+ * into the app. The registry is backed by a global symbol so it stays a single
462
+ * shared instance even across duplicated module copies.
463
+ */
464
+ declare class Publisher {
465
+ /** The shared, global-symbol-backed registry of publishable groups. */
466
+ private static get registry();
467
+ /**
468
+ * Register artifacts a package wants to publish into the application.
469
+ *
470
+ * @example
471
+ * ```ts
472
+ * Publisher.publishes({
473
+ * package: '@arkstack/cache',
474
+ * tag: 'cache-migrations',
475
+ * entries: [{ from: join(here, '../stubs/...'), to: 'src/database/migrations/...' }],
476
+ * })
477
+ * ```
478
+ *
479
+ * @param group The publishable group to register.
480
+ */
481
+ static publishes(group: PublishGroup): void;
482
+ /**
483
+ * Read the registered publishable groups, optionally filtered by package or
484
+ * tag.
485
+ *
486
+ * @param filter Restrict the result to a package and/or tag.
487
+ * @returns The matching publishable groups.
488
+ */
489
+ static publishables(filter?: PublishFilter): PublishGroup[];
490
+ /** Remove every registered publishable group (primarily for tests). */
491
+ static clear(): void;
492
+ }
493
+ //#endregion
406
494
  //#region src/Hook.d.ts
407
495
  declare class Hook {
408
496
  private static hooks;
@@ -457,4 +545,4 @@ declare class Hook {
457
545
  static clear: () => void;
458
546
  }
459
547
  //#endregion
460
- export { AbstractModelConstructor, AppException, ArkstackErrorPayload, ArkstackErrorShape, CONFIG_KEY, ConfigRegistry, ConfigShape, DotPath, DotPathValue, Encryption, EnvRegistry, ErrorHandler, Exception, FileImporter, GlobalConfig, GlobalEnv, Hash, Hook, HookArgs, HookFor, HookName, HookPos, HookPositions, HookRegistry, IHook, Logger, LoggerChalk, LoggerLog, LoggerParseSignature, MergedConfig, ModelConstructor, ModelRegistry, Primitive, RequestException, UnionToIntersection, abort, abortIf, appUrl, assertFound, bindGracefulShutdown, bootWithDetectedPort, config, createErrorPayload, discoverCommands, env, getErrorLogger, getModel, getPrimaryError, getValidationErrors, importFile, initializeGlobalContext, interopDefault, isClass, isModelNotFoundError, isValidationError, loadPrototypes, logUnhandledError, nodeEnv, normalizeStatusCode, outputDir, perPage, rebuildOutput, renderError, serializeError, shouldHideStack, shouldLogError, toErrorShape };
548
+ export { AbstractModelConstructor, AppException, ArkstackErrorPayload, ArkstackErrorShape, CONFIG_KEY, ConfigRegistry, ConfigShape, DotPath, DotPathValue, Encryption, EnvRegistry, ErrorHandler, Exception, FileImporter, GlobalConfig, GlobalEnv, Hash, Hook, HookArgs, HookFor, HookName, HookPos, HookPositions, HookRegistry, IHook, Logger, LoggerChalk, LoggerLog, LoggerParseSignature, MergedConfig, ModelConstructor, ModelRegistry, Primitive, PublishEntry, PublishFilter, PublishGroup, Publisher, RequestException, UnionToIntersection, abort, abortIf, appKey, appUrl, assertFound, bindGracefulShutdown, bootWithDetectedPort, config, createErrorPayload, discoverCommands, env, getErrorLogger, getModel, getPrimaryError, getValidationErrors, importFile, initializeGlobalContext, interopDefault, isClass, isModelNotFoundError, isValidationError, loadPrototypes, logUnhandledError, nodeEnv, normalizeStatusCode, outputDir, perPage, rebuildOutput, renderError, serializeError, shouldHideStack, shouldLogError, toErrorShape };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { C as env, D as outputDir, E as nodeEnv, O as rebuildOutput, S as discoverCommands, T as interopDefault, _ as Hash, b as appUrl, c as abortIf, d as initializeGlobalContext, f as isClass, g as Exception, h as AppException, l as assertFound, m as RequestException, p as perPage, s as abort, u as getModel, v as Encryption, w as importFile, x as config, y as CONFIG_KEY } from "./utils-yPBhsll_.js";
1
+ import { C as discoverCommands, D as nodeEnv, E as interopDefault, O as outputDir, S as config, T as importFile, _ as Hash, b as appKey, c as abortIf, d as initializeGlobalContext, f as isClass, g as Exception, h as AppException, k as rebuildOutput, l as assertFound, m as RequestException, p as perPage, s as abort, u as getModel, v as Encryption, w as env, x as appUrl, y as CONFIG_KEY } from "./utils-CL_FD72V.js";
2
2
  import { Hook as Hook$1 } from "@arkstack/foundry";
3
3
  import { str } from "@h3ravel/support";
4
4
  import { Arkstack } from "@arkstack/contract";
@@ -210,6 +210,54 @@ const shouldLogError = ErrorHandler.shouldLogError;
210
210
  const createErrorPayload = ErrorHandler.createErrorPayload;
211
211
  const logUnhandledError = ErrorHandler.logUnhandledError;
212
212
  //#endregion
213
+ //#region src/Publisher.ts
214
+ const REGISTRY_KEY = Symbol.for("arkstack.publishables");
215
+ /**
216
+ * Registry of artifacts packages want to publish into the consuming application.
217
+ *
218
+ * Packages call {@link Publisher.publishes} from their `setup` module so that
219
+ * `ark publish` can copy the artifacts (migrations, config stubs, assets, …)
220
+ * into the app. The registry is backed by a global symbol so it stays a single
221
+ * shared instance even across duplicated module copies.
222
+ */
223
+ var Publisher = class {
224
+ /** The shared, global-symbol-backed registry of publishable groups. */
225
+ static get registry() {
226
+ return globalThis[REGISTRY_KEY] ??= [];
227
+ }
228
+ /**
229
+ * Register artifacts a package wants to publish into the application.
230
+ *
231
+ * @example
232
+ * ```ts
233
+ * Publisher.publishes({
234
+ * package: '@arkstack/cache',
235
+ * tag: 'cache-migrations',
236
+ * entries: [{ from: join(here, '../stubs/...'), to: 'src/database/migrations/...' }],
237
+ * })
238
+ * ```
239
+ *
240
+ * @param group The publishable group to register.
241
+ */
242
+ static publishes(group) {
243
+ this.registry.push(group);
244
+ }
245
+ /**
246
+ * Read the registered publishable groups, optionally filtered by package or
247
+ * tag.
248
+ *
249
+ * @param filter Restrict the result to a package and/or tag.
250
+ * @returns The matching publishable groups.
251
+ */
252
+ static publishables(filter = {}) {
253
+ return this.registry.filter((group) => (!filter.package || group.package === filter.package) && (!filter.tag || group.tag === filter.tag));
254
+ }
255
+ /** Remove every registered publishable group (primarily for tests). */
256
+ static clear() {
257
+ this.registry.length = 0;
258
+ }
259
+ };
260
+ //#endregion
213
261
  //#region src/Logger.ts
214
262
  var Console = class {
215
263
  static log = (...args) => Logger.log(args.map((e) => [e, "white"]));
@@ -481,4 +529,4 @@ var Hook = class {
481
529
  };
482
530
  };
483
531
  //#endregion
484
- export { AppException, CONFIG_KEY, Encryption, ErrorHandler, Exception, Hash, Hook, Logger, RequestException, abort, abortIf, appUrl, assertFound, bindGracefulShutdown, bootWithDetectedPort, config, createErrorPayload, discoverCommands, env, getErrorLogger, getModel, getPrimaryError, getValidationErrors, importFile, initializeGlobalContext, interopDefault, isClass, isModelNotFoundError, isValidationError, loadPrototypes, logUnhandledError, nodeEnv, normalizeStatusCode, outputDir, perPage, rebuildOutput, renderError, serializeError, shouldHideStack, shouldLogError, toErrorShape };
532
+ export { AppException, CONFIG_KEY, Encryption, ErrorHandler, Exception, Hash, Hook, Logger, Publisher, RequestException, abort, abortIf, appKey, appUrl, assertFound, bindGracefulShutdown, bootWithDetectedPort, config, createErrorPayload, discoverCommands, env, getErrorLogger, getModel, getPrimaryError, getValidationErrors, importFile, initializeGlobalContext, interopDefault, isClass, isModelNotFoundError, isValidationError, loadPrototypes, logUnhandledError, nodeEnv, normalizeStatusCode, outputDir, perPage, rebuildOutput, renderError, serializeError, shouldHideStack, shouldLogError, toErrorShape };
@@ -1,2 +1,2 @@
1
- import { _ as Hash, a as use, c as abortIf, d as initializeGlobalContext, f as isClass, i as trait, l as assertFound, n as crc32, o as uses, p as perPage, r as getTraitMethods, s as abort, t as callTraitMethods, u as getModel, v as Encryption } from "../utils-yPBhsll_.js";
1
+ import { _ as Hash, a as use, c as abortIf, d as initializeGlobalContext, f as isClass, i as trait, l as assertFound, n as crc32, o as uses, p as perPage, r as getTraitMethods, s as abort, t as callTraitMethods, u as getModel, v as Encryption } from "../utils-CL_FD72V.js";
2
2
  export { Encryption, Hash, abort, abortIf, assertFound, callTraitMethods, crc32, getModel, getTraitMethods, initializeGlobalContext, isClass, perPage, trait, use, uses };
@@ -98,6 +98,31 @@ const config = (key, defaultValue) => {
98
98
  return globalThis[CONFIG_KEY];
99
99
  };
100
100
  /**
101
+ * Resolve the unified application key.
102
+ *
103
+ * `APP_KEY` (exposed as `config('app.key')`) is the single secret used for
104
+ * signing and encryption across the framework. Resolution order:
105
+ *
106
+ * 1. An explicit `APP_KEY` environment variable.
107
+ * 2. Any legacy environment variable(s) passed in, for backward compatibility
108
+ * with apps that predate `APP_KEY` (e.g. `JWT_SECRET`,
109
+ * `TWO_FACTOR_ENCRYPTION_KEY`).
110
+ * 3. `config('app.key')` — the value from `src/config/app.ts`, which may itself
111
+ * be a placeholder default when no config is loaded.
112
+ *
113
+ * @param legacy Legacy env var name(s) to fall back to.
114
+ * @returns The resolved key, or `undefined` when none is configured.
115
+ */
116
+ const appKey = (legacy = []) => {
117
+ const explicit = env("APP_KEY");
118
+ if (explicit) return explicit;
119
+ for (const name of Array.isArray(legacy) ? legacy : [legacy]) {
120
+ const value = env(name);
121
+ if (value) return value;
122
+ }
123
+ return config("app.key") || void 0;
124
+ };
125
+ /**
101
126
  * Gets the current Node environment (development or production).
102
127
  *
103
128
  * @returns
@@ -252,8 +277,8 @@ const interopDefault = (imp) => {
252
277
  var Encryption = class {
253
278
  static algorithm = "aes-256-gcm";
254
279
  static getKey() {
255
- const secret = env("TWO_FACTOR_ENCRYPTION_KEY");
256
- if (!secret) throw new Error("TWO_FACTOR_ENCRYPTION_KEY is required to use two-factor authentication");
280
+ const secret = appKey("TWO_FACTOR_ENCRYPTION_KEY");
281
+ if (!secret) throw new Error("APP_KEY is required to use two-factor authentication. Run `ark key:generate`.");
257
282
  return createHash("sha256").update(secret).digest();
258
283
  }
259
284
  static encrypt(value) {
@@ -677,4 +702,4 @@ function uses(instance, trait) {
677
702
  return false;
678
703
  }
679
704
  //#endregion
680
- export { env as C, outputDir as D, nodeEnv as E, rebuildOutput as O, discoverCommands as S, interopDefault as T, Hash as _, use as a, appUrl as b, abortIf as c, initializeGlobalContext as d, isClass as f, Exception as g, AppException as h, trait as i, assertFound as l, RequestException as m, crc32 as n, uses as o, perPage as p, getTraitMethods as r, abort as s, callTraitMethods as t, getModel as u, Encryption as v, importFile as w, config as x, CONFIG_KEY as y };
705
+ export { discoverCommands as C, nodeEnv as D, interopDefault as E, outputDir as O, config as S, importFile as T, Hash as _, use as a, appKey as b, abortIf as c, initializeGlobalContext as d, isClass as f, Exception as g, AppException as h, trait as i, rebuildOutput as k, assertFound as l, RequestException as m, crc32 as n, uses as o, perPage as p, getTraitMethods as r, abort as s, callTraitMethods as t, getModel as u, Encryption as v, env as w, appUrl as x, CONFIG_KEY as y };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkstack/common",
3
- "version": "0.13.2",
3
+ "version": "0.14.0",
4
4
  "type": "module",
5
5
  "description": "Core utilities, primitives, and shared infrastructure for the Arkstack ecosystem.",
6
6
  "homepage": "https://arkstack.toneflix.net",
@@ -42,8 +42,8 @@
42
42
  "peerDependencies": {
43
43
  "@h3ravel/support": "^2.1.4",
44
44
  "arkormx": "^2.9.2",
45
- "@arkstack/foundry": "^0.13.2",
46
- "@arkstack/contract": "^0.13.2"
45
+ "@arkstack/foundry": "^0.14.0",
46
+ "@arkstack/contract": "^0.14.0"
47
47
  },
48
48
  "scripts": {
49
49
  "build": "tsdown --config-loader unrun",