@paneljs/paneljs 0.2.0 → 0.2.1

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/cli.js ADDED
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ import { stdin, stdout } from "node:process";
3
+ import { argument } from "./cli/args.js";
4
+ import { authSchema, createSuperuser } from "./cli/auth.js";
5
+ import { createDefaultInstall, runInit } from "./cli/init.js";
6
+ import { confirm, select } from "./cli/prompt.js";
7
+ const usage = () => {
8
+ console.log(`Usage:
9
+ paneljs init [--framework express] [--orm prisma|typeorm] [--pm npm|pnpm|yarn|bun] [--yes] [--dry-run]
10
+ paneljs auth:schema --identifier <email|username>
11
+ paneljs createsuperuser --config ./paneljs.config.mjs [--email value] [--username value] [--password value]
12
+
13
+ init adds PanelJS packages to the current app. It does not rewrite source files.
14
+ auth:schema and createsuperuser are Prisma setup tools. TypeORM uses builtInAuthEntities() instead.
15
+
16
+ The createsuperuser config module must default-export { prisma, auth }, where auth is a built-in auth configuration.`);
17
+ };
18
+ const command = process.argv[2];
19
+ const argv = process.argv.slice(2);
20
+ try {
21
+ if (command === "init") {
22
+ await runInit({
23
+ cwd: process.cwd(),
24
+ argv: process.argv.slice(3),
25
+ io: {
26
+ stdout,
27
+ isTTY: Boolean(stdin.isTTY),
28
+ env: process.env,
29
+ select,
30
+ confirm: (label) => confirm(label, true),
31
+ install: createDefaultInstall(),
32
+ },
33
+ });
34
+ }
35
+ else if (command === "auth:schema") {
36
+ console.log(authSchema(argument(argv, "--identifier") ?? ""));
37
+ }
38
+ else if (command === "createsuperuser") {
39
+ await createSuperuser(argv);
40
+ }
41
+ else if (command === "--help" || command === "-h") {
42
+ usage();
43
+ }
44
+ else {
45
+ usage();
46
+ process.exitCode = command ? 1 : 0;
47
+ }
48
+ }
49
+ catch (error) {
50
+ console.error(error instanceof Error ? error.message : "Command failed.");
51
+ process.exitCode = 1;
52
+ }
53
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAE7C,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,KAAK,GAAG,GAAS,EAAE;IACvB,OAAO,CAAC,GAAG,CAAC;;;;;;;;qHAQuG,CAAC,CAAC;AACvH,CAAC,CAAC;AAEF,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAChC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEnC,IAAI,CAAC;IACH,IAAI,OAAO,KAAK,MAAM,EAAE,CAAC;QACvB,MAAM,OAAO,CAAC;YACZ,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE;YAClB,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;YAC3B,EAAE,EAAE;gBACF,MAAM;gBACN,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC;gBAC3B,GAAG,EAAE,OAAO,CAAC,GAAG;gBAChB,MAAM;gBACN,OAAO,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,IAAI,CAAC;gBACxC,OAAO,EAAE,oBAAoB,EAAE;aAChC;SACF,CAAC,CAAC;IACL,CAAC;SAAM,IAAI,OAAO,KAAK,aAAa,EAAE,CAAC;QACrC,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAChE,CAAC;SAAM,IAAI,OAAO,KAAK,iBAAiB,EAAE,CAAC;QACzC,MAAM,eAAe,CAAC,IAAI,CAAC,CAAC;IAC9B,CAAC;SAAM,IAAI,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;QACpD,KAAK,EAAE,CAAC;IACV,CAAC;SAAM,CAAC;QACN,KAAK,EAAE,CAAC;QACR,OAAO,CAAC,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,CAAC;AACH,CAAC;AAAC,OAAO,KAAK,EAAE,CAAC;IACf,OAAO,CAAC,KAAK,CAAC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC;IAC1E,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC"}
@@ -0,0 +1,17 @@
1
+ import type { DataAdapter } from "./adapter.js";
2
+ import type { FullRegisteredModel } from "./registry.js";
3
+ import type { AdminUser } from "./types.js";
4
+ export type DeleteReferentialAction = "Cascade" | "SetNull" | "Restrict";
5
+ export type DeletePreviewRelation = {
6
+ fieldName: string;
7
+ modelName: string;
8
+ pluralName: string;
9
+ idField: string;
10
+ displayField: string;
11
+ onDelete: DeleteReferentialAction;
12
+ recordsByParentId: Record<string, Record<string, unknown>[]>;
13
+ };
14
+ /** Related rows shown on a transport's delete confirmation page. */
15
+ export declare function loadDeletePreviewRelations(parent: FullRegisteredModel, models: Map<string, FullRegisteredModel>, adapter: DataAdapter, adminUser: AdminUser, ids: Array<string | number>): Promise<DeletePreviewRelation[]>;
16
+ /** Reject deletes that would violate Restrict / Protect relations. */
17
+ export declare function assertNoRestrictedRelations(parent: FullRegisteredModel, models: Map<string, FullRegisteredModel>, adapter: DataAdapter, adminUser: AdminUser, ids: Array<string | number>): Promise<void>;
@@ -0,0 +1,100 @@
1
+ import { buildListRecordSelect } from "./recordSelection.js";
2
+ import { hasModelPermission } from "./permissions.js";
3
+ import { withSelectFields } from "./query.js";
4
+ import { resolveScope } from "./scope.js";
5
+ import { RequestValidationError } from "./validation.js";
6
+ function referentialAction(onDelete) {
7
+ if (onDelete === "Cascade")
8
+ return "Cascade";
9
+ if (onDelete === "SetNull")
10
+ return "SetNull";
11
+ return "Restrict";
12
+ }
13
+ function findChildBelongsTo(parent, child, parentHasManyRelationName) {
14
+ return child.meta.fields.find((field) => {
15
+ const relation = field.relation;
16
+ return (field.type === "relation" &&
17
+ relation?.kind === "belongsTo" &&
18
+ relation.model === parent.meta.name &&
19
+ relation.foreignKeyFields.length === 1 &&
20
+ (relation.relationName === parentHasManyRelationName ||
21
+ field.name === parentHasManyRelationName));
22
+ });
23
+ }
24
+ function listChildRelations(parent, models) {
25
+ const modelsByName = new Map([...models.values()].map((entry) => [entry.meta.name, entry]));
26
+ const children = [];
27
+ for (const relationField of parent.meta.fields) {
28
+ const relation = relationField.relation;
29
+ if (relationField.type !== "relation" || relation?.kind !== "hasMany") {
30
+ continue;
31
+ }
32
+ const childModel = modelsByName.get(relation.model);
33
+ if (!childModel)
34
+ continue;
35
+ const childRelationField = findChildBelongsTo(parent, childModel, relation.relationName);
36
+ const foreignKey = childRelationField?.relation?.foreignKeyFields[0];
37
+ if (!childRelationField || !foreignKey)
38
+ continue;
39
+ children.push({
40
+ parentFieldName: relationField.name,
41
+ childModel,
42
+ foreignKey,
43
+ onDelete: referentialAction(childRelationField.relation?.onDelete),
44
+ });
45
+ }
46
+ return children;
47
+ }
48
+ /** Related rows shown on a transport's delete confirmation page. */
49
+ export async function loadDeletePreviewRelations(parent, models, adapter, adminUser, ids) {
50
+ const relations = [];
51
+ for (const child of listChildRelations(parent, models)) {
52
+ if (!hasModelPermission(adminUser, child.childModel.resolved.permissions, "list")) {
53
+ continue;
54
+ }
55
+ const childScope = await resolveScope(child.childModel.raw, adminUser);
56
+ const childSelect = withSelectFields(buildListRecordSelect(child.childModel.meta, child.childModel), [child.foreignKey]);
57
+ const childRecords = await adapter
58
+ .resource(child.childModel.meta)
59
+ .findMany({
60
+ scope: childScope,
61
+ filters: { [child.foreignKey]: { in: ids } },
62
+ select: childSelect,
63
+ });
64
+ const recordsByParentId = {};
65
+ for (const record of childRecords) {
66
+ const parentId = record[child.foreignKey];
67
+ if (typeof parentId !== "string" && typeof parentId !== "number") {
68
+ continue;
69
+ }
70
+ const key = String(parentId);
71
+ recordsByParentId[key] = [...(recordsByParentId[key] ?? []), record];
72
+ }
73
+ relations.push({
74
+ fieldName: child.parentFieldName,
75
+ modelName: child.childModel.meta.name,
76
+ pluralName: child.childModel.meta.pluralName,
77
+ idField: child.childModel.meta.idField,
78
+ displayField: child.childModel.meta.displayField,
79
+ onDelete: child.onDelete,
80
+ recordsByParentId,
81
+ });
82
+ }
83
+ return relations;
84
+ }
85
+ /** Reject deletes that would violate Restrict / Protect relations. */
86
+ export async function assertNoRestrictedRelations(parent, models, adapter, adminUser, ids) {
87
+ for (const child of listChildRelations(parent, models)) {
88
+ if (child.onDelete !== "Restrict")
89
+ continue;
90
+ const childScope = await resolveScope(child.childModel.raw, adminUser);
91
+ const count = await adapter.resource(child.childModel.meta).count({
92
+ scope: childScope,
93
+ filters: { [child.foreignKey]: { in: ids } },
94
+ });
95
+ if (count === 0)
96
+ continue;
97
+ throw new RequestValidationError(`Cannot delete this ${parent.meta.name} because related ${child.childModel.meta.name} records still reference it.`);
98
+ }
99
+ }
100
+ //# sourceMappingURL=deleteRelations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"deleteRelations.js","sourceRoot":"","sources":["../src/deleteRelations.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,10 +1,13 @@
1
1
  import type { AdminConfig, ModelConfig } from "./types.js";
2
2
  import { AdminRegistry } from "./registry.js";
3
+ import { AdminService } from "./adminService.js";
3
4
  export interface Admin {
4
5
  register(modelName: string, modelConfig?: ModelConfig): Admin;
5
6
  initialize(): Promise<void>;
6
7
  readonly registry: AdminRegistry;
7
8
  readonly config: AdminConfig;
9
+ /** Available after initialize(); transports delegate admin operations here. */
10
+ readonly service: AdminService;
8
11
  }
9
12
  export declare function createAdmin(config: AdminConfig): Admin;
10
13
  export type { AdminConfig, ModelConfig, AdminUser, AuthConfig, BuiltInAuthConfig, ExternalAuthConfig, AdminHttpRequest, AuditConfig, AdminAuditEvent, AdminFieldMeta, AdminModelMeta, AdminFieldType, RelationKind, ModelPermissions, AdminAction, AdminFieldOverride, PaginatedResponse, SchemaResponse, } from "./types.js";
@@ -13,10 +16,14 @@ export type { ActionWhere, CountQuery, CreateQuery, DeleteManyQuery, EqualityFil
13
16
  export { idSelect, withSelectFields } from "./query.js";
14
17
  export type { ResolvedModelConfig, FullRegisteredModel } from "./registry.js";
15
18
  export { AdminRegistry } from "./registry.js";
16
- export { hashAdminPassword, verifyAdminPassword, verifyLoginPassword } from "./passwords.js";
17
- export { AdminApiError, AuthenticationError, PermissionDeniedError, ModelNotFoundError, RecordNotFoundError } from "./errors.js";
18
- export { RequestValidationError, isFieldVisible, isFieldWritable, isSensitiveFieldName, validateWritePayload, validateHookPayload, assertRequiredCreateFields } from "./validation.js";
19
- export { hasModelPermission, hasActionPermission, hasRegisteredActionPermission } from "./permissions.js";
19
+ export { AdminService, validateSelectedIds } from "./adminService.js";
20
+ export type { AdminListInput, AdminServiceOptions, DeletePreview, DeleteSelectedResult, } from "./adminService.js";
21
+ export { assertNoRestrictedRelations, loadDeletePreviewRelations, } from "./deleteRelations.js";
22
+ export type { DeletePreviewRelation, DeleteReferentialAction, } from "./deleteRelations.js";
23
+ export { hashAdminPassword, verifyAdminPassword, verifyLoginPassword, } from "./passwords.js";
24
+ export { AdminApiError, AuthenticationError, PermissionDeniedError, ModelNotFoundError, RecordNotFoundError, } from "./errors.js";
25
+ export { RequestValidationError, isFieldVisible, isFieldWritable, isSensitiveFieldName, validateWritePayload, validateHookPayload, assertRequiredCreateFields, } from "./validation.js";
26
+ export { hasModelPermission, hasActionPermission, hasRegisteredActionPermission, } from "./permissions.js";
20
27
  export type { AdminOperation } from "./permissions.js";
21
28
  export { DELETE_SELECTED_ACTION } from "./defaultActions.js";
22
29
  export { writeAuditEvent } from "./audit.js";
@@ -24,11 +31,11 @@ export { buildSchemaResponse } from "./schema.js";
24
31
  export { parseListQuery } from "./listQuery.js";
25
32
  export type { ParsedListQuery } from "./listQuery.js";
26
33
  export { parseRecordId } from "./ids.js";
27
- export { buildRecordSelect, buildListRecordSelect, assertSelectedRelationsAreVisible } from "./recordSelection.js";
28
- export { applyCreateScope, assertScopeFieldsUnchanged, buildScopedRecordWhere, collectScopeFieldNames, resolveScope } from "./scope.js";
34
+ export { buildRecordSelect, buildListRecordSelect, assertSelectedRelationsAreVisible, } from "./recordSelection.js";
35
+ export { applyCreateScope, assertScopeFieldsUnchanged, buildScopedRecordWhere, collectScopeFieldNames, resolveScope, } from "./scope.js";
29
36
  export { getAdminUiDist } from "./uiPath.js";
30
37
  export { isBuiltInAuth } from "./authMode.js";
31
- export type { AdminAuthStore, AuthStoreOptions, BuiltInSessionRecord, BuiltInUserRecord } from "./authStore.js";
32
- export { DEFAULT_AUTH_SESSION_MODEL, DEFAULT_AUTH_USER_MODEL } from "./authStore.js";
38
+ export type { AdminAuthStore, AuthStoreOptions, BuiltInSessionRecord, BuiltInUserRecord, } from "./authStore.js";
39
+ export { DEFAULT_AUTH_SESSION_MODEL, DEFAULT_AUTH_USER_MODEL, } from "./authStore.js";
33
40
  export { SESSION_COOKIE_NAME, authenticateBuiltInRequest, authenticateSession, clearedSessionCookie, loginWithPassword, logoutBuiltIn, readSessionToken, resolveAuthStore, sessionCookie, } from "./builtInAuth.js";
34
- export type { BuiltInLoginResult, BuiltInLogoutResult, SessionCookie } from "./builtInAuth.js";
41
+ export type { BuiltInLoginResult, BuiltInLogoutResult, SessionCookie, } from "./builtInAuth.js";
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  import { AdminRegistry } from "./registry.js";
2
+ import { AdminService } from "./adminService.js";
2
3
  export function createAdmin(config) {
3
4
  const registry = new AdminRegistry();
4
5
  let initialized = false;
6
+ let service;
5
7
  const admin = {
6
8
  register(modelName, modelConfig = {}) {
7
9
  registry.register(modelName, modelConfig);
@@ -12,6 +14,11 @@ export function createAdmin(config) {
12
14
  return;
13
15
  const models = await config.adapter.introspect();
14
16
  await registry.initialize(models);
17
+ service = new AdminService({
18
+ models: new Map(registry.getAll().map((model) => [model.meta.name, model])),
19
+ adapter: config.adapter,
20
+ audit: config.audit,
21
+ });
15
22
  initialized = true;
16
23
  },
17
24
  get registry() {
@@ -20,24 +27,32 @@ export function createAdmin(config) {
20
27
  get config() {
21
28
  return config;
22
29
  },
30
+ get service() {
31
+ if (!service) {
32
+ throw new Error("[paneljs] AdminService is unavailable before admin.initialize().");
33
+ }
34
+ return service;
35
+ },
23
36
  };
24
37
  return admin;
25
38
  }
26
39
  export { idSelect, withSelectFields } from "./query.js";
27
40
  export { AdminRegistry } from "./registry.js";
28
- export { hashAdminPassword, verifyAdminPassword, verifyLoginPassword } from "./passwords.js";
29
- export { AdminApiError, AuthenticationError, PermissionDeniedError, ModelNotFoundError, RecordNotFoundError } from "./errors.js";
30
- export { RequestValidationError, isFieldVisible, isFieldWritable, isSensitiveFieldName, validateWritePayload, validateHookPayload, assertRequiredCreateFields } from "./validation.js";
31
- export { hasModelPermission, hasActionPermission, hasRegisteredActionPermission } from "./permissions.js";
41
+ export { AdminService, validateSelectedIds } from "./adminService.js";
42
+ export { assertNoRestrictedRelations, loadDeletePreviewRelations, } from "./deleteRelations.js";
43
+ export { hashAdminPassword, verifyAdminPassword, verifyLoginPassword, } from "./passwords.js";
44
+ export { AdminApiError, AuthenticationError, PermissionDeniedError, ModelNotFoundError, RecordNotFoundError, } from "./errors.js";
45
+ export { RequestValidationError, isFieldVisible, isFieldWritable, isSensitiveFieldName, validateWritePayload, validateHookPayload, assertRequiredCreateFields, } from "./validation.js";
46
+ export { hasModelPermission, hasActionPermission, hasRegisteredActionPermission, } from "./permissions.js";
32
47
  export { DELETE_SELECTED_ACTION } from "./defaultActions.js";
33
48
  export { writeAuditEvent } from "./audit.js";
34
49
  export { buildSchemaResponse } from "./schema.js";
35
50
  export { parseListQuery } from "./listQuery.js";
36
51
  export { parseRecordId } from "./ids.js";
37
- export { buildRecordSelect, buildListRecordSelect, assertSelectedRelationsAreVisible } from "./recordSelection.js";
38
- export { applyCreateScope, assertScopeFieldsUnchanged, buildScopedRecordWhere, collectScopeFieldNames, resolveScope } from "./scope.js";
52
+ export { buildRecordSelect, buildListRecordSelect, assertSelectedRelationsAreVisible, } from "./recordSelection.js";
53
+ export { applyCreateScope, assertScopeFieldsUnchanged, buildScopedRecordWhere, collectScopeFieldNames, resolveScope, } from "./scope.js";
39
54
  export { getAdminUiDist } from "./uiPath.js";
40
55
  export { isBuiltInAuth } from "./authMode.js";
41
- export { DEFAULT_AUTH_SESSION_MODEL, DEFAULT_AUTH_USER_MODEL } from "./authStore.js";
56
+ export { DEFAULT_AUTH_SESSION_MODEL, DEFAULT_AUTH_USER_MODEL, } from "./authStore.js";
42
57
  export { SESSION_COOKIE_NAME, authenticateBuiltInRequest, authenticateSession, clearedSessionCookie, loginWithPassword, logoutBuiltIn, readSessionToken, resolveAuthStore, sessionCookie, } from "./builtInAuth.js";
43
58
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAS9C,MAAM,UAAU,WAAW,CAAC,MAAmB;IAC5C,MAAM,QAAQ,GAAG,IAAI,aAAa,EAAE,CAAC;IACrC,IAAI,WAAW,GAAG,KAAK,CAAC;IAExB,MAAM,KAAK,GAAU;QAClB,QAAQ,CAAC,SAAiB,EAAE,cAA2B,EAAE;YACtD,QAAQ,CAAC,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;YAC1C,OAAO,KAAK,CAAC;QAChB,CAAC;QACD,KAAK,CAAC,UAAU;YACb,IAAI,WAAW;gBAAE,OAAO;YACxB,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;YACjD,MAAM,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;YAClC,WAAW,GAAG,IAAI,CAAC;QACtB,CAAC;QACD,IAAI,QAAQ;YACT,OAAO,QAAQ,CAAC;QACnB,CAAC;QACD,IAAI,MAAM;YACP,OAAO,MAAM,CAAC;QACjB,CAAC;KACH,CAAC;IAEF,OAAO,KAAK,CAAC;AAChB,CAAC;AAsCD,OAAO,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAExD,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAC9C,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAC7F,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,qBAAqB,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACjI,OAAO,EAAE,sBAAsB,EAAE,cAAc,EAAE,eAAe,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AACvL,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,6BAA6B,EAAE,MAAM,kBAAkB,CAAC;AAE1G,OAAO,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAEhD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,iCAAiC,EAAE,MAAM,sBAAsB,CAAC;AACnH,OAAO,EAAE,gBAAgB,EAAE,0BAA0B,EAAE,sBAAsB,EAAE,sBAAsB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AACxI,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAE9C,OAAO,EAAE,0BAA0B,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC;AACrF,OAAO,EACJ,mBAAmB,EACnB,0BAA0B,EAC1B,mBAAmB,EACnB,oBAAoB,EACpB,iBAAiB,EACjB,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,aAAa,GACf,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@paneljs/paneljs",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Django-style, schema-driven Prisma admin panel for Express and Node.js applications.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -52,6 +52,8 @@
52
52
  "dev": "pnpm --dir ui dev",
53
53
  "lint": "echo \"paneljs: no lint yet\"",
54
54
  "check-types": "tsc --noEmit -p tsconfig.json",
55
+ "test": "vitest run --passWithNoTests",
56
+ "test:unit": "vitest run --passWithNoTests",
55
57
  "prepublishOnly": "pnpm run build"
56
58
  },
57
59
  "devDependencies": {